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技术栈是其新的技术要点。
外部条件有限制,所以 Buffer 需要有上限;
Buffer 达到上限这个现象,有一个简化的等价词叫做 Backpressure;
Backpressure 的出现其实是一种危险边界,唯一的选择是丢弃新事件。
Reactor:
Reactor模式也叫反应器模式,大多数IO组件如reids,netty都在使用的IO模式,以此来解决高性能并发。Reactor被分为handler和reactor两个部分,前者负责业务处理,后者负责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框架本身与具体事件处理逻辑无关,具有很高的复用性;