首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Ratpack:构建简单高效的 HTTP 微服务(1)

Ratpack:构建简单高效的 HTTP 微服务(1)

微服务(microservice)是目前流行的软件架构模式。在微服务架构中,每个服务只实现特定的功能。不同的服务的实现是相互独立的,可以使用不同的数据库,甚至是不同的编程语言。在处理一个特定的请求时,可能需要多个微服务的参与和交互。微服务之间的通讯方式一般是 TCP/HTTP,采用的数据传输格式可以是 Protocol Buffer 或 JSON。通过 HTTP 的方式传输 JSON 数据,是一种常见的微服务集成方式。这要求每个微服务实现都暴露 HTTP 接口。暴露 HTTP 服务并不是一件复杂的事情,已经有非常多的开源框架和库可供利用。从简单的 Servlet 实现,到复杂的 JAX-RS 规范或 Spring MVC 框架。已有的这些框架和库的问题在于它们并不是为了实现微服务而设计的。它们的设计目标是开发传统的复杂 Web 应用。因此这些框架所包含的内容很多,运行起来的内存开销较大。Ratpack 是一个创建 HTTP 应用的轻量级库,可以作为创建 HTTP 微服务的基础。本文将对 Ratpack 进行详细介绍。
Ratpack 简介Ratpack 基于流行的网络开发库 Netty 来开发,在 Netty 的基础上添加了 HTTP 协议相关的内容。Netty 作为一个高性能高吞吐量的网络开发库,为 Ratpack 的简单高效打下了良好的基础。与传统的 Web 开发框架相比,Ratpack 所提供的功能相对较少。但是 Ratpack 所缺失的这些功能,在微服务开发中通常也是不需要的。从这个角度来说,Ratpack 是作为一个库,而不是框架来使用的。Ratpack 的作用类似于 NodeJS 社区中的 Connect。
在 Ratpack 编程中有两个重要的概念,分别是上下文对象(Context)和处理器(Handler)。处理器可以看成是在上下文对象上进行处理的函数。处理器在进行处理时,需要从上下文对象中获取所需的信息,如 HTTP 的请求和响应对象,或者通过上下文对象进行错误处理和重定向等。在创建 Ratpack 服务器时需要提供一个处理器的实现。这个处理器实际上就是 Ratpack 应用本身。一个处理器实现可以代理给其他处理器来完成其工作。多个代理器可以嵌套和链接起来,形成复杂的处理器链结构。使用 Ratpack 开发 HTTP 微服务,实际上就是开发处理微服务请求的处理器链。
上下文对象Ratpack 的 Context 接口表示处理器当前的处理上下文。在处理器执行时,当前的 Context 对象会被作为唯一的参数传递给处理器。Context 接口中所提供的功能非常繁多,主要有下面几个类别。
首先是可以获取当前 HTTP 连接的请求和响应对象。通过 Context 接口的 getRequest 方法可以获取到表示当前 HTTP 请求的 Request 接口对象。Request 接口提供了一系列方法用来获取与 HTTP 请求相关的信息,如 getBody 方法来读取请求的内容,getHeaders 方法来获取 HTTP 头,getQuery 方法来获取查询字符串等。Context 接口的 getResponse 方法可以获取到作为 HTTP 请求响应的 Response 接口对象。Response 接口的方法用来对响应进行操作,如 status 方法设置 HTTP 响应状态码,getHeaders 方法返回可以进行修改的 HTTP 响应头,cookie 方法用来设置响应的 Cookie 信息。当 HTTP 响应配置完成之后,通过 send 方法来发送响应。Response 可以发送不同类型的响应,如 byte[]、String 类型或文件等。
Context 接口的另外一个重要功能是作为上下文对象的注册表。Context 的父接口 Registry 提供了与对象注册和获取相关的方法。在 Ratpack 应用开发中,处理器的实现可能会需要用到相关的上下文对象。这些对象都保存在 Context 中。比如一个获取用户订单的微服务,可能由一个处理器负责根据订单号进行数据库查询,而另外一个处理器则负责处理订单对象并生成 JSON 格式的响应。前一个处理器可以把获取到的订单对象保存到 Context 中,而后一个处理器则直接从 Context 中获取订单对象。通过这样的职责划分,前一个处理器所执行的逻辑可以在不同的地方被复用。
Context 接口还提供了 insert 和 next 方法来构建复杂的处理器链。insert 方法负责插入一个或多个处理器到当前处理链中,并代理给第一个处理器;next 方法则直接代理给处理链中的下一个处理器。
在  中,首先在 Context 中添加了一个 ArrayList 类型的对象。addOutput 方法的实现中向该 ArrayList 对象中添加一个字符串,然后通过 Context 的 next 方法代理给下一个处理器来执行。通过 Context 的 insert 方法添加了 3 个不同的处理器,前两个处理器用来修改 Context 中的对象,最后一个处理器用来把结果以 JSON 格式输出。
清单 1. Context 中的处理器链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class HandlerChain {
   public static void main(String[] args) throws Exception {
       new HandlerChain().start();
   }

   public void start() throws Exception {
       RatpackServer.start(server ->
               server.registry(Registry.single(new ArrayList<String>()))
                       .handlers(chain ->
                           chain.get(ctx -> ctx.insert(
                                   addOutput("Hello"),
                                   addOutput("World"),
                                   ctx3 -> ctx3.render(json(ctx3.get(List.class)))
                           ))
               )
       );
   }

   private Handler addOutput(final String text) {
       return ctx -> {
           ctx.get(List.class).add(text);
           ctx.next();
       };
   }
}




Context 接口中还有一些辅助方法,如 clientError 来返回一个与客户端错误相关的状态码,error 来返回服务器错误相关的状态码,notFound 来返回 404 错误,redirect 来返回重定向的响应。Context 还提供了 parse 方法来解析 HTTP 请求内容。对于 HTTP 响应,Context 接口提供了 render 方法来把不同类型的对象作为响应的内容。
处理器处理器 Handler 的接口定义很简单,只有一个 handle 方法。handle 方法只有唯一的参数是 Context 接口的对象。每个 Handler 负责对 Context 进行操作。每个处理器所执行的操作可以简单或复杂。单个处理器可以获取 HTTP 请求的内容,调用服务层的代码来执行业务逻辑,并生成相应的响应。也可以只完成某个简单的任务,再通过 Context 接口的 insert 和 next 方法来代理给其他处理器继续处理。从可复用和可测试的角度出发,每个处理器的实现应该尽可能的简单。这样可以充分利用 Ratpack 提供的处理器代理和链接功能。
返回列表