自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

爸爸又給Spring MVC生了個(gè)弟弟叫Spring WebFlux

開發(fā) 架構(gòu)
很早之前,Java就火起來了,是因?yàn)樗朴陂_發(fā)和處理網(wǎng)絡(luò)方面的應(yīng)用。Java有一個(gè)愛好,就是喜歡制定規(guī)范標(biāo)準(zhǔn),但自己又不善于去實(shí)現(xiàn)。反倒是一些服務(wù)提供商使用它的規(guī)范標(biāo)準(zhǔn)來制造應(yīng)用服務(wù)器而賺的盆滿缽滿。

[[319694]]

 情景引入

很早之前,Java就火起來了,是因?yàn)樗朴陂_發(fā)和處理網(wǎng)絡(luò)方面的應(yīng)用。

Java有一個(gè)愛好,就是喜歡制定規(guī)范標(biāo)準(zhǔn),但自己又不善于去實(shí)現(xiàn)。

反倒是一些服務(wù)提供商使用它的規(guī)范標(biāo)準(zhǔn)來制造應(yīng)用服務(wù)器而賺的盆滿缽滿。

企業(yè)用戶因要使用這些應(yīng)用服務(wù)器而向提供商支付高額費(fèi)用,而且也不是特別好用。

一個(gè)青年才俊為了打破這種局面而奔走呼號、奮發(fā)圖強(qiáng)。

自我介紹

顯然,這個(gè)青年才俊就是后來的Spring。

因企業(yè)應(yīng)用大都和web相關(guān),而Java的web標(biāo)準(zhǔn)中較核心的一部分其實(shí)就是JavaEE里的Servlet。

Spring和Servlet“相親相愛”一番后,我就來到了這個(gè)世界。我的全名叫Spring MVC,這里的Spring既是我的姓也是我的“爸爸”,那Servlet就是我的“媽媽”了,大家叫我MVC就行了。

那個(gè)年代社會很落后,條件也不好,好歹我們要求也不高,求個(gè)溫飽就行了。

所以我的媽媽Servlet和她的閨蜜Filter天生就是同步阻塞的,包括她們同事HttpServletRequest的getParameter,getPart等這些方法也都是阻塞的。

雖然我的爸爸Spring給了我23條染色體來進(jìn)行改良,但不要忘了我還從Servlet媽媽那里繼承了23條,所以我也是同步阻塞的。不過我的“長相”已經(jīng)好看很多了,因?yàn)镾pring爸爸知道,在以后的日子里,除了拼實(shí)力之外,顏值也是非常重要的。

因?yàn)槲覌寢孲ervlet是一個(gè)規(guī)范,我爸爸Spring是一個(gè)框架,所以我跟他們一樣,都是無法自己獨(dú)立運(yùn)行的。

所以在我們要運(yùn)行的時(shí)候,必須要尋找一個(gè)特殊的“家”,通常稱它為Servlet容器,比如tomcat就算非常知名的一個(gè)。

Servlet容器熟知我極有可能阻塞當(dāng)前執(zhí)行線程,所以專門量身打造。它給我準(zhǔn)備了一個(gè)非常大的線程池,里面有好多線程。每過來一個(gè)請求,它就扔給我一個(gè)線程,說自己玩去吧,隨便“折騰”。

好在那時(shí)美國那個(gè)叫喬布斯的家伙被自己的公司趕出去在外面“流浪”,Servlet容器為我量身打造的這種方法完全能夠勝任日常,關(guān)鍵還非常的簡單。

這種小富即安的日子就這樣往前過著。

兄弟出生

生命不息,變化不止。隨著喬布斯推出iphone,智能機(jī)瞬間大火,全民進(jìn)入移動(dòng)互聯(lián)網(wǎng)時(shí)代。激增的網(wǎng)民數(shù)量,給現(xiàn)有軟件架構(gòu)帶來極大的挑戰(zhàn)。

一般來說,社會越發(fā)達(dá),分工越精細(xì),對單一工種的要求就越高。

軟件也是如此,在傳統(tǒng)“大塊頭”軟件表現(xiàn)的越來越格格不入的時(shí)候,微服務(wù)就如一絲春風(fēng)吹了進(jìn)來。

按它的指導(dǎo)原則,將大軟件按某種方式拆分為一個(gè)個(gè)小工程。小工程規(guī)模小,便于管理,而且機(jī)動(dòng)性也好,功能聚合性更好。它承受的并發(fā)應(yīng)該更高。

有人覺得與微服務(wù)比起來,過去使用的web服務(wù)器如tomcat略顯笨重,不夠輕量級。也有人說tomcat內(nèi)部一個(gè)請求一個(gè)線程這種阻塞執(zhí)行方式消耗太多線程,不太容易支撐超高并發(fā)。

無論怎么說,簡而言之一句話,一個(gè)全新的時(shí)代已經(jīng)到來。

此時(shí)我們需要一個(gè)更加輕量級web應(yīng)用,它使用更少的硬件資源和線程,反而更容易處理高并發(fā)。那么它一定是異步非阻塞的。

這樣的使命自然落到了響應(yīng)式編程的范疇上了。所以我的爸爸Spring審時(shí)度勢,在5.0之后就趕緊把我推出來了。

沒錯(cuò),我就是Spring WebFlux,這里的Spring既是我的姓也是我爸爸。大家可以叫我WebFlux。初來乍到,好多人都對我不熟悉,請容許我介紹一番。

首先這個(gè)響應(yīng)式究竟是什么意思呢?響應(yīng)式這個(gè)術(shù)語,指的是一個(gè)編程模型,它是圍繞著對變化的反映來構(gòu)建的。

如網(wǎng)絡(luò)組件用來響應(yīng)I/O事件,UI控制器用來響應(yīng)鼠標(biāo)事件等等。按照這種意識的話,非阻塞就是響應(yīng)式的,對操作完成或數(shù)據(jù)可用通知事件的響應(yīng)方式。

另外一個(gè)關(guān)于響應(yīng)式的機(jī)制是非阻塞后壓。在命令式代碼中,同步阻塞調(diào)用帶有自然的后壓迫使調(diào)用者等待。

在異步代碼中,它變得非常重要,用來控制事件的速率,以至于不讓一個(gè)快速的事件源壓垮它的響應(yīng)者。就是響應(yīng)者能夠控制事件源發(fā)射事件的快慢。

因?yàn)轫憫?yīng)式編程是非阻塞的,所以我也是非阻塞的,因此我通常運(yùn)行在非阻塞web服務(wù)器上,如Netty,Undertow等。

因?yàn)槲也粫枞€程的執(zhí)行,所以使用一個(gè)小的固定數(shù)量的線程池(event loop workers)來處理請求。典型地,線程數(shù)與CPU的核數(shù)相同。

這里還要感謝我的姥爺Java 8,他老人家引入了lambda表達(dá)式造就了函數(shù)式編程API。這對于非阻塞應(yīng)用和連續(xù)式API來說是一個(gè)非常棒的東西,允許以聲明的方式把異步邏輯組合起來。

我感覺我的爸爸Spring已經(jīng)超越了一個(gè)框架,成為一個(gè)平臺了。所以他自己并沒有親自去實(shí)現(xiàn)響應(yīng)式處理,而是為我選擇Reactor作為響應(yīng)式庫。

Reactor提供Flux和Mono類型,擁有豐富的操作符,支持非阻塞后壓,使用函數(shù)式API來組合異步邏輯。并且Reactor強(qiáng)烈聚焦于Java服務(wù)器端。它在開發(fā)時(shí)就已經(jīng)與爸爸Spring親密協(xié)作了。

爸爸說,我也支持其它的庫如RxJava,但看樣子似乎讓我更愛Reactor一些。

這就是我,WebFlux,一個(gè)集天時(shí)地利于一身的幸運(yùn)兒。但你是不是已經(jīng)暈暈的啦,沒關(guān)系,慢慢來。

包羅萬象

我想,大家都看出了我爸爸Spring的野心,他不僅要成為一個(gè)平臺,還要建起自己的生態(tài)系統(tǒng),豎起壁壘。

所以他的核心事業(yè)就是進(jìn)行抽象,組合和裝配,進(jìn)而包羅萬象。說的掉渣一些,就是哪個(gè)技術(shù)好,就給它整合進(jìn)來。

為了抹平底層不同web服務(wù)器的差異,我爸爸抽象了一個(gè)最低級別的契約接口,HttpHandler,用于響應(yīng)式HTTP請求的處理。

  1. Mono<java.lang.Void> handle(ServerHttpRequest request, ServerHttpResponse response); 

它是一個(gè)通用的接口,要橫跨不同的運(yùn)行時(shí)。它是有意設(shè)計(jì)成最小化的,只有一個(gè)方法,主要唯一目的就是在不同的HTTP服務(wù)器API上面成為一個(gè)最小化的抽象。

如果想用Netty服務(wù)器的話,就基于Netty實(shí)現(xiàn)一下,同理也可以基于Undertow實(shí)現(xiàn)一下,等等,只要以后有了新的服務(wù)器,都可以加進(jìn)來的。

顯而易見,HttpHandler的目標(biāo)是抽象出來對不同HTTP服務(wù)器的使用,說白了就是為了和底層服務(wù)器對接。但由于太偏底層,不利用上層代碼使用。

為此,我的爸爸又抽象出一個(gè)稍微高一點(diǎn)級別的契約接口,WebHandler,用于Web請求處理。很明顯,WebHandler的目標(biāo)是提供web應(yīng)用中廣泛使用的通用特性,如Session、表單數(shù)據(jù)和附件等等,也是為了更容易和上層代碼對接。

很自然的,WebHandler是構(gòu)建于HttpHandler之上的,換句話說WebHander的處理會通過一個(gè)適配器HttpWebHandlerAdapter最終代理給HttpHandler來執(zhí)行。

WebHandler接口也只有一個(gè)方法:

  1. Mono<java.lang.Void> handle(ServerWebExchange exchange); 

參數(shù)類型是ServerWebExchange,可以這樣理解,你發(fā)一個(gè)請求,給你一個(gè)響應(yīng),相當(dāng)于用請求交換了一個(gè)響應(yīng),而且是在服務(wù)器端交換的。

其實(shí),整個(gè)web請求的處理過程是一個(gè)鏈?zhǔn)降模詈蟛攀且粋€(gè)WebHandler,它前面可以插入多個(gè)錯(cuò)誤處理器,WebExceptionHandler,多個(gè)過濾器,WebFilter。

這是錯(cuò)誤處理器接口:

  1. Mono<java.lang.Void> handle(ServerWebExchange exchange, java.lang.Throwable ex); 

這是過濾器接口:

  1. Mono<java.lang.Void> filter(ServerWebExchange exchange, WebFilterChain chain); 

可見,我的爸爸Spring的抽象能力非常強(qiáng),對下抽象一個(gè)接口,抹平了不同服務(wù)器的差異。對上抽象一個(gè)接口,可以用于支撐不同的編程模型。

都有哪些編程模型呢,請繼續(xù)往下看吧。

皮囊之下

上面我在介紹自己的時(shí)候使用了美顏,所以諸位很難看清我的“真面目”,下面就來進(jìn)行一下自我剖析,看看真實(shí)的我。

我包含一個(gè)輕量級函數(shù)式編程模型,函數(shù)被用來參與處理請求,它是相對于基于注解編程模型的另一種選擇,這種編程模型叫做函數(shù)式端點(diǎn),functional endpoints,是構(gòu)建于上面提到的WebHandler之上的。

我是使用HandlerFunction來處理一個(gè)HTTP請求的,這是一個(gè)函數(shù)式接口,也稱處理函數(shù):

  1. @FunctionalInterface 
  2. public interface HandlerFunction<T extends ServerResponse> { 
  3.     reactor.core.publisher.Mono<T> handle(ServerRequest request); 

帶有一個(gè)ServerRequest參數(shù),返回一個(gè)Mono,其中request和response對象都是不可變的,HandlerFunction就等價(jià)于Controller中的@RequestMapping標(biāo)記的方法。

實(shí)際當(dāng)中,請求很多,處理函數(shù)也很多,如何知道一個(gè)請求過來后,該由哪個(gè)處理函數(shù)去處理呢?

這自然要用到我的另一個(gè)函數(shù)式接口RouterFunction來搞定,稱為路由函數(shù):

  1. @FunctionalInterface 
  2. public interface RouterFunction<T extends ServerResponse> { 
  3.     reactor.core.publisher.Mono<HandlerFunction<T>> route(ServerRequest request); 

帶有一個(gè)ServerRequest參數(shù),返回一個(gè)Mono。就是它把一個(gè)請求路由到一個(gè)HandlerFunction的,當(dāng)路由函數(shù)匹配時(shí),就返回一個(gè)處理函數(shù),否則返回一個(gè)空的Mono。RouterFunction等價(jià)于@RequestMapping注解,但主要不同的是路由函數(shù)提供的不僅是數(shù)據(jù),還有行為。

下面通過一些示例,來更加直觀的幫助大家認(rèn)識這兩個(gè)函數(shù)式接口。

因處理函數(shù)是函數(shù)式接口,所以可以直接用一個(gè)lambda表達(dá)式來處理請求,如下:

  1. HandlerFunction<ServerResponse> handler = request -> Response.ok().body("Hello World"); 

這就表示當(dāng)任何一個(gè)請求過來時(shí),都返回Hello World作為響應(yīng)。

在實(shí)際應(yīng)用中,處理邏輯一般都很復(fù)雜,肯定不是一個(gè)lambda表達(dá)式能搞定的,此時(shí)希望把處理方法專門寫到一個(gè)類里,就叫處理器類,和MVC里的Controller差不多一回事。

下面就是一個(gè)Person的處理器類:

  1. public class PersonHandler { 
  2.  
  3.     public Mono<ServerResponse> listPeople(ServerRequest request) { 
  4.         // ... 
  5.     } 
  6.  
  7.     public Mono<ServerResponse> createPerson(ServerRequest request) { 
  8.         // ... 
  9.     } 
  10.  
  11.     public Mono<ServerResponse> getPerson(ServerRequest request) { 
  12.         // ... 
  13.     } 

此時(shí)就可以通過處理函數(shù),引用這些處理器方法了,如下:

  1. PersonHandler handler = new PersonHandler(); 
  2.  
  3. HandlerFunction<ServerResponse> list = handler::listPeople; 
  4.  
  5. HandlerFunction<ServerResponse> create = handler::createPerson; 
  6.  
  7. HandlerFunction<ServerResponse> get = handler::getPerson; 

要想使請求能夠正確被路由,首先要定義好路由函數(shù),如下:

  1. RouterFunction<ServerResponse> route = RouterFunctions.route() 
  2.     .GET("/person/{id}", get) 
  3.     .GET("/person", list) 
  4.     .POST("/person"create
  5.     .build(); 

它表示當(dāng)以GET方法請求/person/{id}時(shí),最終會由getPerson方法處理。當(dāng)以GET方法請求/person時(shí),最后會由listPeople方法處理。同理,以POST方法請求/person時(shí),會由createPerson方法處理。

可見,一個(gè)路由函數(shù)可以包含多個(gè)路由規(guī)則,實(shí)際當(dāng)中,可以定義多個(gè)路由函數(shù),這些路由函數(shù)可以組合在一起。

路由函數(shù)是按順序計(jì)算的,如果第一個(gè)路由不匹配,計(jì)算第二個(gè),等等。因此,把更加具體的路由放到通用路由前面是非常有意義的。注意這和基于注解的不同。

怎么樣,關(guān)掉濾鏡的我是不是更加真實(shí)了。我相信你也看明白了,至少要記住,這是基于函數(shù)式的一種編程模型,叫做函數(shù)式端點(diǎn)。

雨露均沾

像我這樣的幸運(yùn)兒,你們一定以為Spring爸爸對我非常溺愛吧,告訴你,確實(shí)是這樣的。不過考慮到大家伙一路走來對Spring的不離不棄,爸爸也設(shè)身處地為你們著想。

為此,我除了支持函數(shù)式端點(diǎn)這種編程模型之外,還支持一種編程模型叫基于注解的控制器,annotated controllers,沒錯(cuò),就是MVC里的那個(gè)。

話說的再白一些,就是大家已經(jīng)非常熟悉的Spring MVC那套東西,我百分之百的完全支持,妥妥的,放心使用。

但是,并不是所有的控制器方法參數(shù)都支持響應(yīng)式類型,只有一些支持,如WebSession,java.security.Principal,@RequestBody,HttpEntity,@RequestPart等。

下面看一個(gè)示例:

 

  1. @PostMapping("/"
  2. public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) {  
  3.     // ... 
  4.  
  5. @PostMapping("/"
  6. public String handle(@RequestBody Flux<Part> parts) {  
  7.     // ... 
  8.  
  9. @PostMapping("/accounts"
  10. public void handle(@RequestBody Mono<Account> account) { 
  11.     // ... 

不過對于控制器方法的所有返回值,都是支持響應(yīng)式類型的。

各有千秋

Spring MVC和Spring WebFlux可以一起使用,從設(shè)計(jì)上講,它們互為繼續(xù)、互為一致。

它們的關(guān)系,請看下圖,既有共同的部分,也有互相獨(dú)立的部分。

 

 

 

Spring MVC的特點(diǎn)就是,它是命令式編程,代碼非常容易寫,也好理解和調(diào)試。但是它是同步的,會有人覺得它性能不好。

但是我要說的是,響應(yīng)式和非阻塞通常來講也不會使應(yīng)用運(yùn)行的更快。相反,非阻塞方式要求做更多的事情,而且還會稍微增加一些必要的處理時(shí)間。也就是說,還可能稍稍變慢一點(diǎn),what,那為啥還要用它呢?

響應(yīng)式和非阻塞的關(guān)鍵好處是,在使用很少固定數(shù)目的線程和較少的內(nèi)存情況下的擴(kuò)展能力。

這使應(yīng)用在負(fù)載下更有適應(yīng)能力,因?yàn)樗鼈円砸粋€(gè)更加具有可預(yù)見性的方式在擴(kuò)展。

為了能夠觀察到這些好處,你需要有一些延遲才行,比如一個(gè)既不可靠且速度又慢的網(wǎng)絡(luò)I/O,這才是響應(yīng)式開始展示它強(qiáng)勁的地方,帶來的差異(驚喜)可能是巨大的哦。

其實(shí)技術(shù)無好壞,各有各的適用場景罷了。

 

責(zé)任編輯:武曉燕 來源: 編程新說
相關(guān)推薦

2023-09-04 11:52:53

SpringMVC性能

2023-11-02 18:01:24

SpringMVC配置

2022-07-04 09:15:10

Spring請求處理流程

2023-02-09 08:01:12

核心組件非阻塞

2022-11-04 08:39:46

SpringWebFlux

2020-03-06 18:43:58

Spring MVC控制器Java

2020-07-07 07:00:00

Spring WebFREST APIReactive AP

2019-03-04 08:48:23

Spring WebFJavaIO

2024-03-06 07:52:21

Spring框架響應(yīng)式編程微服務(wù)架構(gòu)

2022-09-26 08:54:39

Spring函數(shù)式編程

2021-07-15 11:16:31

Spring WebWebFlux架構(gòu)

2020-05-25 07:00:00

雙因素認(rèn)證身份認(rèn)證密碼

2024-01-10 09:59:19

虛擬線程信息

2012-06-17 20:19:29

2017-07-20 09:23:32

Springjavacio

2011-05-24 09:22:44

Spring3異常處理

2023-07-10 08:00:13

架構(gòu)Rest返回值

2020-03-18 14:55:01

GitHubnpm開源

2011-12-05 13:44:34

JavaSpringMVC

2009-06-11 10:37:58

netbeans spMVC基礎(chǔ)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號