zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 3
    display:inline-block的运用
    图解单片机8位PWM、16位PWM中“位”的含义!
    UVA10006
    [置顶] CF 86D Powerful array 分块算法入门,n*sqrt(n)
    俗人解释 三维渲染 在工作过程
    HDU 4414 Finding crosses(dfs)
    Codeforces 35E Parade 扫描线 + list
    hdu 4374 单调队列
    LeetCode OJ平台Sort Colors讨论主题算法
  • 原文地址:https://www.cnblogs.com/YsirSun/p/12547550.html
Copyright © 2011-2022 走看看