webFlux&Reactor


  配置springcloud的gateway的時候,需要用到webflux,所以需要學習一下。以下是目前我的理解,可能不正確,但是會持續修正。

  什么是webflux?目前的認知是異步非阻塞IO的webMVC,因為之前的Springmvc是基於同步阻塞IO模型的Servlet實現的,包括tomcat,jetty等傳統的servlet容器,因為他們的servlet不支持異步非阻塞,所以,每個請求在獲取資源的時候,系統資源都在該請求的名下,顯而易見,這是會浪費很多資源的,因為在進行資源IO的時候,如果資源阻塞,線程等資源需要等待被請求資源的IO喚醒。所以傳統的tomcat,jetty在性能方面有很大的限制,這也是springmvc面臨的問題。所以springwebflux伴隨spring5出現了。

 以及下圖sringboot2.0中webflux和mvc技術棧的對比:

  

  可以看出響應式Reactive技術棧是其新的技術要點。

Router Functions: 對應@Controller,@RequestMapping等標准的Spring MVC注解,提供一套函數式風格的API,用於創建Router,Handler和Filter。
Webflux:核心模塊,聯系Router Functions模塊和Reactive Streams模塊,提供響應式編程。
Reactive Streams:一種支持背壓( Backpressure)機制的異步數據流處理標准,主要實現有RxJava和Reactor,springwebflux采用的是 Reactor
響應式編程:
  響應式編程就是基於reactor的思想,當你做一個帶有一定延遲的才能夠返回的io操作時,不會阻塞,而是立刻返回一個流,並且訂閱這個流,當這個流上產生了返回數據,可以立刻得到通知並調用回調函數處理數據。
Backpressure機制:
  生產速度大於消費速度,所以需要 Buffer;

  外部條件有限制,所以 Buffer 需要有上限;

  Buffer 達到上限這個現象,有一個簡化的等價詞叫做 Backpressure;

  Backpressure 的出現其實是一種危險邊界,唯一的選擇是丟棄新事件。

Reactor:

  Reactor模式也叫反應器模式,大多數IO組件如reids,netty都在使用的IO模式,以此來解決高性能並發。Reactor被分為handlerreactor兩個部分,前者負責業務處理,后者負責io接受分發。

  回顧一下IO歷史:

    連軸轉:一個while處理全部請求(單線程)。一個請求阻塞全部阻塞。

    connection per thread:一個請求一個線程。一個線程只能處理一個請求,即使語法上允許一個線程處理多個請求,但是一個線程上的一個請求被阻塞,其他也會阻塞;每個線程都是系統的資源,耗費資源巨大,創建銷毀線程也需要消耗資源。

    Java.NIO: (NIO的Selector網絡通訊就是一個單線程版的Reactor)。一個典型的NIO代碼:

 1 static class Server
 2     {
 3 
 4         public static void testServer() throws IOException
 5         {
 6 
 7             // 1、獲取Selector選擇器
 8             Selector selector = Selector.open();
 9 
10             // 2、獲取通道
11             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
12             // 3.設置為非阻塞
13             serverSocketChannel.configureBlocking(false);
14             // 4、綁定連接
15             serverSocketChannel.bind(new InetSocketAddress(SystemConfig.SOCKET_SERVER_PORT));
16 
17             // 5、將通道注冊到選擇器上,並注冊的操作為:“接收”操作
18             serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
19 
20             // 6、采用輪詢的方式,查詢獲取“准備就緒”的注冊過的操作
21             while (selector.select() > 0)
22             {
23                 // 7、獲取當前選擇器中所有注冊的選擇鍵(“已經准備就緒的操作”)
24                 Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
25                 while (selectedKeys.hasNext())
26                 {
27                     // 8、獲取“准備就緒”的時間
28                     SelectionKey selectedKey = selectedKeys.next();
29 
30                     // 9、判斷key是具體的什么事件
31                     if (selectedKey.isAcceptable())
32                     {
33                         // 10、若接受的事件是“接收就緒” 操作,就獲取客戶端連接
34                         SocketChannel socketChannel = serverSocketChannel.accept();
35                         // 11、切換為非阻塞模式
36                         socketChannel.configureBlocking(false);
37                         // 12、將該通道注冊到selector選擇器上
38                         socketChannel.register(selector, SelectionKey.OP_READ);
39                     }
40                     else if (selectedKey.isReadable())
41                     {
42                         // 13、獲取該選擇器上的“讀就緒”狀態的通道
43                         SocketChannel socketChannel = (SocketChannel) selectedKey.channel();
44 
45                         // 14、讀取數據
46                         ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
47                         int length = 0;
48                         while ((length = socketChannel.read(byteBuffer)) != -1)
49                         {
50                             byteBuffer.flip();
51                             System.out.println(new String(byteBuffer.array(), 0, length));
52                             byteBuffer.clear();
53                         }
54                         socketChannel.close();
55                     }
56 
57                     // 15、移除選擇鍵
58                     selectedKeys.remove();
59                 }
60             }
61 
62             // 7、關閉連接
63             serverSocketChannel.close();
64         }
65 
66         public static void main(String[] args) throws IOException
67         {
68             testServer();
69         }
70     }

從上面代碼可知,NIO通過一個線程就完成了傳統IO需要大量資源提供的IO操作,且效率極高。但是缺點也很明顯,任何IO資源的操作都有可能導致阻塞,進而導致這個NIO系統阻塞。這在web項目中是不能容忍的。所以也就沒有單線程的Reactor。

  多線程的Reactor:

    1. Handler資源處理器的執行被放入線程池中進行,以多線程的方式進行資源處理。(主要)

    2. 而對於Reactor而言,可以仍為單個線程。如果服務器為多核的CPU,為充分利用系統資源,可以將Reactor拆分為兩個線程。(升級)

Reactor模式的優點:

1)響應快,不必為單個同步時間所阻塞,雖然Reactor本身依然是同步的;

2)編程相對簡單,可以最大程度的避免復雜的多線程及同步問題,並且避免了多線程/進程的切換開銷;

3)可擴展性,可以方便的通過增加Reactor實例個數來充分利用CPU資源;

4)可復用性,reactor框架本身與具體事件處理邏輯無關,具有很高的復用性; 

參考文章: https://www.jianshu.com/p/4e02c35152a9


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM