项目框架spring webflux
Spring boot 在 2.x 版本增添了新的框架 WebFlux。传统的web框架都是基于Servlet API与Servlet容器基础之上运行的,而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。
什么是webflux:
![](https://img.haomeiwen.com/i5558402/8225371f9579e46c.png)
右侧是spring 5.0 引入的基于Reactive Streams的Spring WebFlux框架,从上到下依次是Router Functions,WebFlux,Reactive Streams三个新组件。
Router Functions:
对标@Controller,@RequestMapping等标准的Spring MVC注解,提供一套函数式风格的API,用于创建Router,Handler和Filter。
WebFlux:
核心组件,协调上下游各个组件提供响应式编程支持。
Reactive Streams:
一种支持背压(Backpressure背压是一种常用策略,使得发布者拥有无限制的缓冲区存储元素,用于确保发布者发布元素太快时,不会去压制订阅者。)的异步数据流处理标准,主流实现有RxJava和Reactor,Spring WebFlux默认集成的是Reactor。
在Web容器的选择上,Spring WebFlux既支持像Tomcat,Jetty这样的的传统容器(前提是支持Servlet 3.1 Non-Blocking IO API),又支持像Netty,Undertow那样的异步容器。不管是何种容器,Spring WebFlux都会将其输入输出流适配成Flux<DataBuffer>格式,以便进行统一处理。
Webflux的优势
首先确定的是,非阻塞的处理方式规避了线程排队等待的情况,从而可以用少量而固定的线程处理应对大量请求的处理。他并不能提升程序的运行速度,但是在大规模同时请求时的吞吐量和响应时长能有显著改善。
Webflux目前的不足
由于MySQL数据库是同步,官方暂时还没推出MySQL数据库驱动异步解决方案,目前只有Mongodb、redis、couchdb、Cassandra官方提供了异步驱动。
项目结构
![](https://img.haomeiwen.com/i5558402/70fce85b7a97f041.png)
项目结构上与spring boot 没有太大区别,其中handler是Router Functions的写法,还有一种与mvc类似的结构,两种不能混用,返回结果统一都是Mono或者Flux。
webflux中使用filter
/**
* 业务记录日志filter
*
* @author pilsy
*/
@Order(-1)
@Component
public class BusinessLogFilter implements WebFilter {
private static final Logger log = LoggerFactory.getLogger(BusinessLogFilter.class);
private final LogRepository logRepository;
private final PathPattern pathPattern;
@Autowired
public BusinessLogFilter(LogRepository logRepository) {
this.logRepository = logRepository;
pathPattern = new PathPatternParser().parse("/bd-exchange/**");
}
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
final ServerHttpRequest request = serverWebExchange.getRequest();
// 匹配请求路径
if (pathPattern.matches(request.getPath().pathWithinApplication())) {
// 匹配到业务请求记录日志
RequestPath path = request.getPath();
MultiValueMap<String, String> queryParams = request.getQueryParams();
String hostAddress = request.getRemoteAddress().getAddress().getHostAddress();
LogEntity logEntity = new LogEntity();
logEntity.setPath(path.value());
logEntity.setQueryParams(queryParams.toString());
logEntity.setHostAddress(hostAddress);
// 保存日志,并打印
Mono<LogEntity> mono = logRepository.save(logEntity);
mono.subscribe(entity -> log.info(entity.toString()));
}
return webFilterChain.filter(serverWebExchange);
}
}
webflux中使用RouterFunction
/**
* 请求路由配置 RouterFunction
*
* @author pilsy
*/
@Configuration
public class RouterConfig {
private final ExchangeHandler exchangeHandler;
private final AuthHandler authHandler;
private final ErrorHandler errorHandler;
private final ViewHandler viewHandler;
@Autowired
public RouterConfig(ExchangeHandler exchangeHandler, AuthHandler authHandler, ErrorHandler errorHandler, ViewHandler viewHandler) {
this.exchangeHandler = exchangeHandler;
this.errorHandler = errorHandler;
this.viewHandler = viewHandler;
this.authHandler = authHandler;
}
/**
* 业务请求路由
*
* @return
*/
@Bean
public RouterFunction<ServerResponse> exchangeRouterFunction() {
return RouterFunctions
.route(POST("/bd-exchange/services/validatePeople.do").and(accept(APPLICATION_JSON)), exchangeHandler::validatePeople)
.andRoute(POST("/bd-exchange/services/queryPeople.do").and(accept(APPLICATION_JSON)), exchangeHandler::queryPeople);
}
/**
* 鉴权请求路由
*
* @return
*/
@Bean
public RouterFunction<ServerResponse> AuthRouterFunction() {
return RouterFunctions
.route(GET("/auth/error").and(accept(APPLICATION_JSON)), errorHandler::authError)
.andRoute(GET("/auth/getPage").and(accept(APPLICATION_JSON)), authHandler::getPage)
.andRoute(POST("/auth/save").and(accept(APPLICATION_JSON)), authHandler::save)
.andRoute(GET("/auth/delete/{id}").and(accept(APPLICATION_JSON)), authHandler::delete)
.andRoute(GET("/auth/getSecret").and(accept(APPLICATION_JSON)), authHandler::getSecret);
}
/**
* view页面路由
*
* @return
*/
@Bean
public RouterFunction<ServerResponse> viewRoutesRegister() {
return RouterFunctions
.route(GET("/index"), viewHandler::loginPage)
.andRoute(GET("/"), viewHandler::loginPage);
}
}
webflux(使用netty为Web容器)中调整request请求长度限制
package com.gsoft.xc.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class NettyConfiguration implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
@Value("${server.max-initial-line-length:65536}")
private int maxInitialLingLength;
public void customize(NettyReactiveWebServerFactory container) {
container.addServerCustomizers(
httpServer -> httpServer.httpRequestDecoder(
httpRequestDecoderSpec -> httpRequestDecoderSpec.maxInitialLineLength(maxInitialLingLength)
)
);
}
}
网友评论