设为首页 加入收藏

TOP

也谈Reactor模式(一)
2019-09-02 23:09:12 】 浏览:53
Tags:也谈 Reactor 模式

何谓Reactor模式?它是实现高性能IO的一种设计模式。网上资料有很多,有些写的也很好,但大多不知其所以然。这里博主按自己的思路简单介绍下,有不对的地方敬请指正。


BIO

Java1.4(2002年)以前,IO都是Blocking的,也就是常说的BIO,它在等待请求、读、写(返回)三个环节都是阻塞的。在等待请求阶段,系统无法知道请求何时到达,因此需要一个主线程一直守着,当有请求进来时,将请求分发给读写线程。如图:

代码如下:

    ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//线程池
    ServerSocket serverSocket = new ServerSocket();
    serverSocket.bind(8088); 
    while(!Thread.currentThread.isInturrupted()){//主线程死循环等待新连接到来        
        Socket socket = serverSocket.accept();
        executor.submit(new ConnectIOnHandler(socket));//为新的连接创建新的线程 
    }
class ConnectIOnHandler extends Thread{ private Socket socket; public ConnectIOnHandler(Socket socket){ this.socket = socket; } public void run(){ while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){//死循环处理读写事件 String someThing = socket.read()....//读取数据 if(someThing!=null){
           ......//处理数据
           
socket.write()....//写数据 } } }

需知,请求进来(accept),并不表示数据马上达到了,可能隔一段时间才会传进来,这个时候socket.read()也是一直阻塞的状态。socket.write()也同理,当向磁盘或其它socket写数据时,也要等对方准备好才能写入,在对方准备阶段,socket.write()也是阻塞的。这两个环节可能的无效阻塞导致读写线程的低效。


NIO

Java1.4开始,引入了NIO。NIO有三个概念:Selector、Buffer、Channel。与BIO的区别是,请求进来后,并不会马上分派IO线程,而是依靠操作系统底层的多路复用机制(select/poll/epoll等),在监听到socket读写就绪之后,再分配IO线程(实际可由当前线程[使用Buffer和Channel]直接读写,因为读写本身的效率很高),这就避免了线程等待。且与BIO多线程方式相比,使用I/O多路复用技术,系统不必创建和维护庞大的线程池,从而大大减小了开销。这部分工作是NIO的核心,由Selector负责,本质上是多路复用的Java封装。而Buffer和Channel又封装了一层socket的读写,应该为的是将IO与业务代码彻底分离。以下图示为本人理解:

如图示,与BIO中监听线程职责不同,Selector监听的不只是连接请求,还有读写就绪事件,当某个事件发生时,即通知注册了该事件的Channel,由Channel操作socket读写Buffer。虚线表示需要具体的NIO框架或业务代码自己处理,比如Channel如何注册以及注册何种事件,Channel处理IO的方式(如在当前线程处理还是新开线程,若新开线程,则可看作是AIO模式)等。NIO只是提供了一套机制,具体使用还是需要编程实现(Reactor模式就是OO的一种实现)。

示例代码(摘自Java NIO详解

服务端:

 1 package cn.blog.test.NioTest;
 2 
 3 
 4 import java.io.IOException;
 5 import java.net.InetSocketAddress;
 6 import java.nio.ByteBuffer;
 7 import java.nio.channels.*;
 8 import java.nio.charset.Charset;
 9 import java.util.Iterator;
10 import java.util.Set;
11 
12 
13 public class MyNioServer {
14     private Selector selector;          //创建一个选择器
15     private final static int port = 8686;
16     private final static int BUF_SIZE = 10240;
17 
18     private void initServer() throws IOException {
19         //创建通道管理器对象selector
20         this.selector=Selector.open();
21 
22         //创建一个通道对象channel
23         ServerSocketChannel channel = ServerSocketChannel.open();
24         channel.configureBlocking(false);       //将通道设置为非阻塞
25         channel.socket().bind(new InetSocketAddress(port));       //将通道绑定在8686端口
26 
27         //将上述的通道管理器和通道绑定,并为该通道注册OP_ACCEPT事件
28         //注册事件后,当该事件到达时,selector.select()会返回(一个key),如果该事件没到达selector.select()会一直阻塞
29         SelectionKey selectionKey = channel.register(selector,SelectionKey.OP_ACCEPT);
30 
31         while (true){       //轮询
32             selector.select();          //这是一个阻塞方法,一直等待直到有数据可读,返回值是key的数量(可以有多个)
33             Set keys = selector.selectedKeys();         //如果channel有数据了,将生成的key访入keys集合中
34             Iterator iterator = keys.iterator();        //得到这个keys集合的迭代器
35             while (iterator.hasNext()){             //使用迭代器遍历集合
36                 SelectionKey key = (SelectionKey) iterator.next();       //得到集合中的一个key实例
37                 iterator.remove();          //拿到当前key实例之后记得在迭代器中将这个元素删除,非常重要,否则会出错
38                 if (key.isAcceptable()){         //判断当前key所代表的channel是否在Acceptable状态,如果是就进行接收
39                     doAccept(key
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇工厂方法和抽象工厂模式. 下一篇挑战常规--这样写单例是错的!

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目