文章目录

  • WebFlux初次尝试
  • 处理过程源码分析



SpringMvc通常是Servlet应用,因此,可能被当前线程阻塞。以远程调用为例,由于阻塞的缘故,导致Servlet容器使用较大的线程池处理请求。而Spring WebFlux通常是非阻塞的服务(同步/异步无法确定,Reactor默认同步,可改为异步),不会发生阻塞,因此该阻塞服务器可使用少量、固定大小的线程池处理请求。(非阻塞无非就是当前不阻塞,后续去回调。)

WebFlux初次尝试

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public RouterFunction<ServerResponse> routerFunction(){
        return route(GET("/hello"),this::hello);
    }

    public Mono<ServerResponse> hello(ServerRequest serverRequest){
        return ServerResponse.status(HttpStatus.OK)
                .body(Mono.just("hello world"),String.class);
    }

}

启动项目后,通过访问"localhost:8080/hello"就可以得到返回值"hello world"。
这个过程中,需要注意这五个类及其方法:

  • 路由方法:RouterFunction#route
  • 请求判定:RequestPredicate(类似于Predicate)
  • 处理函数:HandlerFunction
  • 请求接口:ServerRequest
  • 响应接口:ServerResponse

处理过程源码分析

传入的请求被路由到有RouterFunction<T>的处理函数(即Function<Request, Optional<HandlerFunction<T>>)路由到处理函数,如果它匹配的话;否则就返回一个空的结果。路由方法与@RequestMapping注解的作用相似。但是,还有一个显著的区别:用注解时路由会被限制到注解的value所能表达的范围,处理这些方法的覆盖是困难的(除非用@profile注解);当用路由方法的时候,代码就在那里,可以轻松的覆盖或替换。

@Configuration
public class PersonRouter {
  // solution
  @Bean
  public RouterFunction<ServerResponse> peopleRoutes(PersonHandler personHandler) {
    return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get)
        .andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all)
        .andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post)
        .andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put)
        .andRoute(DELETE("/people/{id}"), personHandler::delete)
        .andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry);
  }
}

路由函数的组合 如果第一个RountHandler 匹配不成功,则进入的下一个RountHandler,匹配顺序按代码顺序执行。

静态引入RouterFunctions.route(),这样就可以用请求判断式(RequestPredicate) (即 Predicate<Request>)和处理方法(HandlerFunction)创建路由方法了,其java源码如下:

public static <T extends ServerResponse> RouterFunction<T> route(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
        return new RouterFunctions.DefaultRouterFunction(predicate, handlerFunction);
    }

再来看看Predicate的源码:

@FunctionalInterface
public interface RequestPredicate {
    boolean test(ServerRequest var1);
    ...default method...
   }

可以知道RequestPredicate其实就是一个返回布尔值的方法参数。如果判断式判断成功则返回处理方法,否则返回空结果

这一新框架的起点是HandlerFunction<T>,基本上是Function<Request, Response<T>>,其源码如下:

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
    Mono<T> handle(ServerRequest var1);
}

可以看到,HandlerFunction接受ServerRequest类型的参数,返回Mono包装的ServerResponse类型的返回值。一个简单的“Hello World”处理函数的例子,返回有200状态以及body为String的响应消息:

HandlerFunction<String> helloWorld =
 request -> Response.ok().body(fromObject("Hello World"));

正如我们在上面的例子中看到的,处理函数是通过构建在Reactor的基础上而完全响应:它们接受Flux,Mono,或任何其他相应的流Publisher作为响应类型。要注意的一点,HandlerFunction本身是没有副作用的,因为它返回响应,而不是把它当作一个参数(参见Servlet.service(ServletRequest,ServletResponse),这实质上是BiConsumer<ServletRequest,ServletResponse> )。没有副作用有很多好处:易于测试,编写和优化。

ServerRequest并且ServerResponse接口可以提供JDK-8(Lambda)对底层HTTP消息的友好访问。

函数式端点组件请求流程处理:

restTemplate 非阻塞获取流 springboot 非阻塞_JSON


注解驱动组件流程请求处理:

restTemplate 非阻塞获取流 springboot 非阻塞_spring_02

WebFlux 示例程序