原标题:当RX遇上Spring——Spring 5新特性之响应式Web应用
介 绍
1.1 何谓响应式编程?
简单来说,响应式编程讲的就是让应用变得异步、事件驱动,而且仅需少量线程就能在同一个JVM上垂直扩展而无需通过集群方式水平扩展。
响应式应用的一个关键特征就是背压。所谓背压就是一种保护消费者不被生产者的过度生产压垮的机制。举例来说,假如有一连串的从数据库一直延伸到HTTP响应的响应式组件,在HTTP链接太慢的情况下,数据仓储也会相应降低速度,或是在网络容量恢复前彻底停止工作。
响应式编程也使得编程范式从命令式向声明式的异步逻辑组合变迁。打个比方,命令式好比一个代码块来执行任务,声明式就是用Java 8的CompletableFuture通过lambda表达式来构建下一步的操作。
更全面的介绍参见Dave Syer的博客”Notes on Reactive Programming”。
1.2 响应式API与重要组建
Spring Framework 5用ReactiveStreams作为异步组件以及库之间的交流协议。Reactive Streams是业内协作共同构建的规格。Java 9也采用了Reactive Streams,并命名为java.util.concurrent.Flow。
Spring Framework内部采用Reactor来支持响应式编程。ReactiveStreams在实现Reactor时用Flux和Mono 这两个组合式API扩展了基本的Publisher协议,分别提供了对0..N以及0..1的数据序列的声明式操作的支持。
Spring框架在滋生的许多响应式API中同样暴露了Flux和Mono接口。在应用层,用户也可以选择使用Spring提供的对RxJava的全面支持。
更多响应式类型参见Sebastien Deleuze的”understanding Reactive Types”。
Spring Web响应式模块
Spring Framework 5包括了全新的spring-web-reactive模块。该模块提供了响应式HTTP以及WebSocket客户端的支持。在响应式Web应用服务器上也支持REST,HTML浏览器以及WebSocket类型的交互。
2.1 服务器端
服务器端支持两种不同的编程模型:
基于注解(包括@Controller以及其他Spring MVC支持的注解)
函数式(Java 8 lambda式的路由以及处理)
两种编程模型都是基于吧非阻塞HTTP运行时转化为Reactive Steams API。下图展示了服务器端技术栈。左边是传统的基于Servlet的Spring MVC(spring-web-mvc)以及右边的响应式技术栈(spring-web-reactive)。
全新的响应式技术栈能和运行在支持Servlet 3.1非阻塞IO API的Servlet容器之上,也能运行在诸如Netty活Undertow的异步运行时之上。所有的运行时都被包装秤响应式的ServerHttpRequest以及ServerHttpResponse,请求和响应体被表示为有背压支持的Flux而不是InputStream和OutputStream。Flux不仅支持REST风格的JSON和XML序列化和反序列化,也支持HTML视图渲染以及Server-Sent Events。
基于注解的编程模型:
Spring MVC 中的@Controller编程模式在响应式模式下同样得到支持。主要区别就是其使用的底层框架协议。在响应式模式下,HandlerMapping和HandlerAdaper是非阻塞的,而且工作在ServerHttpRequest和ServerHttpResponse之上,而不是传统的HttpServletRequest和HttpServletResponse之上。
例如这个响应式控制器:
@RestControllerpublicclass PersonController { privatefinalPersonRepository repository; publicPersonController(PersonRepository repository) { this.repository= repository; } @PostMapping("/person") Mono create(@RequestBody Publisher personStream) { returnthis.repository.save(personStream).then(); } @GetMapping("/person") Flux list() { returnthis.repository.findAll(); } @GetMapping("/person/{id}") Mono findById(@PathVariable String id) { returnthis.repository.findOne(id); }}
函数式模型:
函数式模型用Java 8 lambda风格的撸起以及请求响应。主要的API是函数式接口RouterFunction和HandlerFunction。他们是创建Web应用简单又强大的工具。
函数式请求响应参见下例:
PersonRepository repository = ...RouterFunctions .route(GET("/person/{id}").and(accept(APPLICATION_JSON)), request -> { int personId = Integer.valueOf(request.pathVariable("id")); Mono notFound = ServerResponse.notFound().build(); return repository.findOne(personId) .then(person -> ServerResponse.ok().body(Mono.just(person), Person.class)) .otherwiseIfEmpty(notFound); }) .andRoute(GET("/person").and(accept(APPLICATION_JSON)), request -> ServerResponse.ok().body(repository.findAll(), Person.class)) .andRoute(POST("/person").and(contentType(APPLICATION_JSON)), request -> ServerResponse.ok().build(repository.save(request.bodyToMono(Person.class))));
更多详情参见M3 release blog post
2.2 客户端
Spring Framework 5提供了一个函数式、响应式的WebClient。WebClient是
RestTemplate的全面的非阻塞、响应式的替代解决方案。他爸网络输入输出暴露为ClientHttpRequest和ClientHttpResponse,请求体和响应体为Flux。响应与服务器端,json, xml, sse的序列化在WebClient上也都有响应支持。WebClient需要一个ClientHttpConnector实例来诸如特定的HTTP客户端。
下例使用了Reactor Nettty:
WebClient client = WebClient.create(new ReactorClientHttpConnector());ClientRequest request = ClientRequest .GET("http://example.com/accounts/{id}", 1L) .accept(APPLICATION_JSON) .build();
完
责任编辑: