spring webflux自定義netty 參數(shù)解析
自定義 webflux 容器配置
配置代碼
@Component
public class ContainerConfig extends ReactiveWebServerFactoryCustomizer {
public ContainerConfig(ServerProperties serverProperties) {
super(serverProperties);
}
@Override
public void customize(ConfigurableReactiveWebServerFactory factory) {
super.customize(factory);
NettyReactiveWebServerFactory nettyFactory = (NettyReactiveWebServerFactory) factory;
nettyFactory.setResourceFactory(null);
nettyFactory.addServerCustomizers(server ->
server.tcpConfiguration(tcpServer ->
tcpServer.runOn(LoopResources.create("mfilesvc", Runtime.getRuntime().availableProcessors() * 4, Runtime.getRuntime().availableProcessors() * 8, true))
.selectorOption(CONNECT_TIMEOUT_MILLIS, 200)
).channelGroup(new ChannelGroup())
);
}
@Override
public int getOrder() {
return -10;
}
}
服務(wù)重啟時 報錯
SpringContextShutdownHook Socket couldn't be stopped within 3000ms
解決方案

初識Spring WebFlux
在我的認識中,大部分人都在用SpringMVC(包括我自己)。在最近的學習中,發(fā)現(xiàn)spring5中有一個和SpringMVC平級的東西Spring WebFlux,接下來初步認識一下這是個什么東東?
Spring Web新的改變
眾所周知Spring MVC是同步阻塞的IO模型,當我們在處理一個耗時的任務(wù)時,如上傳文件,服務(wù)器的處理線程會一直處于等待狀態(tài),等待文件的上傳,這期間什么也做不了,等到文件上傳完畢后可能需要寫入,寫入的過程線程又只能在那等待,非常浪費資源。為了避免這類資源的浪費,Spring WebFlux應(yīng)運而生,在Spring WebFlux中若文件還沒上傳完畢,線程可以先去做其他事情,當文件上傳完畢后會通知線程,線程再來處理,后續(xù)寫入也是類似的,通過異步非阻塞機制節(jié)省了系統(tǒng)資源,極大的提高了系統(tǒng)的并發(fā)量。這兩種形式,是不是像極了BIO和NIO這兩種形式,實際上,SpringMVC和Spring WebFlux也就是這兩種IO特點的體現(xiàn)。
以下為官網(wǎng)的介紹:

Spring WebFlux的特性
1.異步非阻塞
如上文所說,線程不需要一直處于等待狀態(tài),Spring WebFlux很好的體現(xiàn)了NIO的異步非阻塞思想。
2.響應(yīng)式(reactive)編程
響應(yīng)式編程是一種新的編程風格,其特點是異步和并發(fā)、事件驅(qū)動、推送PUSH機制一級觀察者模式的衍生。reactive引用允許開發(fā)者構(gòu)建事件驅(qū)動,可擴展性,彈性的反應(yīng)系統(tǒng):提供高度敏感的實時用戶體驗感覺,可伸縮性和彈性的引用程序棧的支持,隨時可以部署在多核和云計算架構(gòu)。
Reactive的主要接口:
- Publisher:發(fā)布者,數(shù)據(jù)的生產(chǎn)端
- Subscriber:消費者,此處可以定義獲取到數(shù)據(jù)后響應(yīng)的操作
- Processor:消費者與發(fā)布者之間的數(shù)據(jù)處理
- back pressure:背壓,消費者告訴發(fā)布者自己能處理多少數(shù)據(jù)
消費者的回調(diào)方法:
- onSubscribe:訂閱關(guān)系處理,用它來響應(yīng)發(fā)布者
- onNext:接收到數(shù)據(jù)后會響應(yīng)的方法
- onError:出現(xiàn)錯誤時處理的方法
- onComplete:任務(wù)完成后響應(yīng)的方法
3.適配多種web容器
既然Spring WebFlux很好的體現(xiàn)了NIO的異步非阻塞思想。作為首屈一指的NIO框架netty,便是Spring WebFlux默認的運行容器。此外,大家熟悉的Tomcat、Jetty等Servlet容器,也能運行Spring WebFlux,前提是容器需要支持Servlet3.1,因為非阻塞IO是使用了Servlet3.1的特性。
Spring WebFlux簡單實踐
本文默認開發(fā)環(huán)境是JDK8,開發(fā)工具是IDEA。實踐分兩部分內(nèi)容,第一部分與SpringMVC對比開發(fā)中的不一樣的地方;第二部分為Spring WebFlux獨有的響應(yīng)式編程的簡單實踐。
在webflux中,Mono代表返回0或1個元素(相當于一個對象)。Flux代表返回0-n個元素(相當于集合)
1.工程創(chuàng)建
新建springboot工程。
選擇Web -> Spring Reactive Web 創(chuàng)建

或者在springboot工程中pom文件添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.Controller中與SpringMVC的對比
在controller中,webflux的寫法可以和springMVC的寫法類似
@RestController
@Slf4j
public class UserController {
@RequestMapping("/index")
public String index(){
log.info("springmvc index begin");
String result="cc666";
log.info("springmvc index end");
return result;
}
@RequestMapping("/index2")
public Mono<String> index2(){
log.info("webflux index begin");
Mono<String> result=Mono.just("666cc");
log.info("webflux index end");
return result;
}
}
3.異步非阻塞的體現(xiàn)
上面已經(jīng)實現(xiàn)了初步的數(shù)據(jù)返回,不過webflux和springmvc目前來看沒有什么區(qū)別,已知springmvc線程執(zhí)行時會阻塞的,webflux線程是異步非阻塞的。下面修改一下代碼,在獲取數(shù)據(jù)的時候加一些額外的耗時操作,看看webflux是否是真的異步非阻塞
@RestController
@Slf4j
@AllArgsConstructor
public class UserController {
/**
* 模擬耗時查詢操作
*/
public String createStr(){
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "cc666cc";
}
@RequestMapping("/index")
public String index(){
log.info("springmvc index begin");
String result=this.createStr();
log.info("springmvc index end");
return result;
}
@RequestMapping("/index2")
public Mono<String> index2(){
log.info("webflux index begin");
Mono<String> result=Mono.fromSupplier(()->this.createStr());
log.info("webflux index end");
return result;
}
}
通過日志結(jié)果,可以很明顯的發(fā)現(xiàn),雖然前端頁面展示的效果是一樣的,但springmvc是等待后返回結(jié)果;而webflux是先執(zhí)行,等有結(jié)果后,再返回結(jié)果。由此體現(xiàn)了webflux異步非阻塞的特性
springmvc
2020-08-04 21:28:57.430 INFO 14156 --- [ctor-http-nio-2] c.w.webflux.controller.UserController : springmvc index begin
2020-08-04 21:29:00.430 INFO 14156 --- [ctor-http-nio-2] c.w.webflux.controller.UserController : springmvc index end
webflux
2020-08-04 21:29:09.640 INFO 14156 --- [ctor-http-nio-2] c.w.webflux.controller.UserController : webflux index begin
2020-08-04 21:29:09.641 INFO 14156 --- [ctor-http-nio-2] c.w.webflux.controller.UserController : webflux index end
4.添加數(shù)據(jù)庫支持
對于數(shù)據(jù)庫的支持,webflux用到的是r2dbc這樣一個東西。
R2DBC(Reactive Relational Database Connectivity)是一個使用反應(yīng)式驅(qū)動集成關(guān)系數(shù)據(jù)庫的孵化器。Spring Data R2DBC運用熟悉的Spring抽象和repository 支持R2DBC?;诖耍陧憫?yīng)式程序棧上使用關(guān)系數(shù)據(jù)訪問技術(shù),構(gòu)建由Spring驅(qū)動的程序?qū)⒆兊梅浅:唵巍?/p>
在pom中引入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>com.github.jasync-sql</groupId>
<artifactId>jasync-r2dbc-mysql</artifactId>
<version>1.1.3</version>
</dependency>
在application.yml中加入數(shù)據(jù)源:
spring:
r2dbc:
url: r2dbc:mysql://127.0.0.1:3306/study
username: xxx
password: xxx
5.Dao的編寫
Dao的編寫和springmvc中類似,本文中繼承了ReactiveCrudRepository類,是Repository的一個實現(xiàn)類,其中實現(xiàn)了簡單的crud操作,model和dao的實現(xiàn):
@Table("user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
private Long id;
private String username;
private String password;
}
public interface UserDao extends ReactiveCrudRepository<User,Long> {
}
6.Controller的編寫
controller的編寫還是和springmvc類似,這里采用RESTful的方式
@RestController
@AllArgsConstructor
public class UserController {
private final UserDao userDao;
@GetMapping("/findAll")
public Flux<User> findAll(){
return userDao.findAll();
}
@PostMapping("/save")
public Mono save(@RequestBody User user){
return this.userDao.save(user);
}
@DeleteMapping("/delete/{id}")
public Mono delete(@PathVariable Long id){
return this.userDao.deleteById(id);
}
@GetMapping("/get/{id}")
public Mono get(@PathVariable Long id){
return this.userDao.findById(id);
}
}
7.響應(yīng)式編程Handler的編寫
在使用上,webflux可以和springmvc類似,不過webflux也有自己的一套響應(yīng)式編程的寫法,先定義handler,類似于controller,不過只有業(yè)務(wù)處理的代碼,其中就用到了reactive模擬的request(ServerRequest )和response(ServerResponse),同樣的實現(xiàn)簡單的crud功能:
@Component
@AllArgsConstructor
public class UserHandler {
private final UserDao userDao;
public Mono<ServerResponse> saveUser(ServerRequest request){
Mono<User> mono=request.bodyToMono(User.class);
User user = mono.block();
return ServerResponse.ok().build(this.userDao.save(user).then());
}
public Mono<ServerResponse> deleteById(ServerRequest request){
Long id=Long.parseLong(request.pathVariable("id"));
return ServerResponse.ok().build(this.userDao.deleteById(id).then());
}
public Mono<ServerResponse> getByid(ServerRequest request){
Long id=Long.parseLong(request.pathVariable("id"));
Mono<User> mono = this.userDao.findById(id);
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(mono,User.class);
}
public Mono<ServerResponse> findAll(ServerRequest request){
Flux<User> all = this.userDao.findAll();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(all,User.class);
}
}
8.響應(yīng)式編程Route的編寫
在handler中我們編寫了處理代碼,但是怎么通過請求地址訪問呢?
學習過vue的老鐵們應(yīng)該知道,vue是通過路由定義地址和頁面的對應(yīng)關(guān)系的,vue也是響應(yīng)式編程的一種體現(xiàn)。webflux中也是通過類似的方式來實現(xiàn)的,在Route中定義規(guī)則:
@Configuration
public class UserRoute {
@Bean
public RouterFunction<ServerResponse> routeUser(UserHandler userHandler){
return RouterFunctions
.route(RequestPredicates.GET("findAll2")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),userHandler::findAll)
.andRoute(RequestPredicates.GET("/get2/{id}")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),userHandler::getByid)
.andRoute(RequestPredicates.DELETE("/delete2/{id}")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),userHandler::deleteById)
.andRoute(RequestPredicates.POST("/save2")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),userHandler::saveUser);
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot快速集成sse服務(wù)端推流(最新整理)
SSE?Server-Sent?Events是一種允許服務(wù)器向客戶端推送實時數(shù)據(jù)的技術(shù),它建立在?HTTP?和簡單文本格式之上,提供了一種輕量級的服務(wù)器推送方式,通常也被稱為“事件流”(Event?Stream),這篇文章主要介紹了Springboot快速集成sse服務(wù)端推流(最新整理),需要的朋友可以參考下2024-02-02
詳解java JDK 動態(tài)代理類分析(java.lang.reflect.Proxy)
這篇文章主要介紹了詳解java JDK 動態(tài)代理類分析(java.lang.reflect.Proxy)的相關(guān)資料,需要的朋友可以參考下2017-06-06
詳談Java中instanceof和isInstance的區(qū)別
下面小編就為大家?guī)硪黄斦凧ava中instanceof和isInstance的區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01
解決java調(diào)用python代碼返回值中文亂碼問題
這篇文章主要介紹了解決java調(diào)用python代碼返回值中文亂碼問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
Java 實戰(zhàn)練手項目之酒店管理系統(tǒng)的實現(xiàn)流程
讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實現(xiàn)一個酒店管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11

