2 深入理解Netty, 线程篇1, 前置知识reactor模型

        netty 是基于 java nio 上封装的框架,所以它的核心功能一定也是依赖 java nio的核心组件。因为原生java nio太原始,如果要在生产上使用,程序员要花大量的时间在非业务上的处理。如写半包,jdk select的空轮询等等。

        netty 从 并行,池化,网络通信等方面帮助我们优化程序。并行可以让我们程序充分利用cpu,今天我们浅析一下netty的线程模型。

这篇文章我们先讲讲什么是 reactor模型:

        reactor反应堆的引入是为了更好的管理IO。通俗来来讲,我们将每个IO 绑定对应的回调,比如写回调,读回调,然后放在一个集合里。这样一个个被绑定的结构体组合起来,我们称为反应堆,里面被绑定的结构体叫反应粒子。来一个事件反应一下,来一个事件反应一下。

       

reactor 反应堆的三种模式:

一 单reactor线程模式:

        单reactor线程简单来讲,即接收IO事件,也处理事件回调。我们来直接看图

         eventLoop可以简单理解为,下面伪代码的 for(;;)。

我用伪代码说明一下流程:

   //将普通事件封装为绑定了回调函数的数据结构 
   class EventItem{
        public Object writeCallBack();
        public Object readCallBack();
        public Object acceptCallBack();
        Event event;
        String type;
    }
    // Selector的包装类,可以直接返回EventItem集合
    WrapperSelector wrapperSelector = WrapperSelector.open();
    ServerSocketWrapper serverSocketWrapper = new ServerSocketWrapper();
    // 将 serverSocket注册到 wrapperSelector
    serverSocketWrapper.initAndregister(wrapperSelector);
    for(;;){
      // 取出就绪事件
      Set<EventItem> eventItemSet = wrapperSelector.select();
      // 调用就绪事件的回调函数
      for(EventItem eventItem:eventItemSet){
          // 判断是否为 accept 事件,下面的条件判断如此类推
          if(eventItem.type.equals("accept")){
              eventItem.acceptCallBack();
          }
          if(eventItem.type.equals("read")){
              eventItem.readCallBack();
          }
          if(eventItem.type.equals("write")){
              eventItem.writeCallBack();
          }
      }
    }

这个伪代码的逻辑是:

1 将普通事件封装为EventItem

2 循环取出就绪事件

3 根据就绪事件的类型调用回调函数

二 单reactor线程,多业务线程:

        所谓的单reactor线程,多业务线程指的是,reactor线程只处理 accept 事件,read事件和write事件分发给业务线程

我们来看图:

这个伪代码可以这么来写:

    //将普通事件封装为绑定了回调函数的数据结构
    class EventItem{
        public Object writeCallBack();
        public Object readCallBack();
        public Object acceptCallBack();
        Event event;
        String type;
    }
    // 初始化一个 线程池
    ExecutorService dispatcherPool = Executors.newFixedThreadPool(10);
    // Selector的包装类,可以直接返回EventItem集合
    WrapperSelector wrapperSelector = WrapperSelector.open();
    ServerSocketWrapper serverSocketWrapper = new ServerSocketWrapper();
    // 将 serverSocket注册到 wrapperSelector
    serverSocketWrapper.initAndregister(wrapperSelector);

    for(;;){
      // 取出就绪事件
      Set<EventItem> eventItemSet = wrapperSelector.select();
      // 调用就绪事件的回调函数
      for(EventItem eventItem:eventItemSet){
           // 判断是否为 accept 事件,下面的条件判断如此类推
          if(eventItem.type.equals("accept")){
              eventItem.acceptCallBack();
          }
          if(eventItem.type.equals("read")){
               dispatcherPool.execute(new Runnable() {
                  @Override
                  public void run() {
                      eventItem.readCallBack();
                  }
              });
          }
          if(eventItem.type.equals("write")){
              dispatcherPool.execute(new Runnable() {
                  @Override
                  public void run() {
                      eventItem.writeCallBack();
                  }
              });
          }
      }
    }

这个伪代码的逻辑跟 单reactor线程模式 不同点在于,我在处理 read 和 write 事件的时候使用线程池。

三 主从模式:

主从模式可以说是netty 的线程模型了。主从模式可以这么来理解:

1 初始化主从两个线程池

2 主线程池负责处理接收事件

3 从线程池负责处理耗时的 write/read 等事件

我们继续来看图:

 我们继续来看看伪代码:

  // 主线程池
    ExecutorService masterPool = Executors.newFixedThreadPool(10);
    // 从线程池
    ExecutorService slavePool = Executors.newFixedThreadPool(10);
    ServerSocketWrapper serverSocketWrapper = new ServerSocketWrapper();
    // 将 serverSocket注册到 masterSelector
    serverSocketWrapper.initAndregister(masterSelector);
    
    class EventItem{
        public Object writeCallBack();
        public Object readCallBack();
        public Object acceptCallBack();
        Event event;
        String type;
    }

    // master线程池主要监听的 accept 事件,acceptCallBack 主要逻辑就是 将 返回的fd 注册到 salveSelector,并监听 write/read等事件
    masterPool.execute(new Runnable() {
        WrapperSelector masterSelector;
        Set<WrapperSelector> salveSelectorSet;
        @Override
        public void run() {
            for(;;){
                Set<EventItem> eventItemSet =  masterSelector.select();
                if(eventItem.type.equals("accept")){
                    eventItem.acceptCallBack(salveSelectorSet.getOne());
                }
            }
        }
    });
     // 从 线程池负责 处理耗时的业务
     salveSelector.execute(new Runnable() {
        WrapperSelector salveSelector;
        @Override
        public void run() {
            for(;;){
                Set<EventItem> eventItemSet =  salveSelector.select();
                if(eventItem.type.equals("read")){
                    eventItem.readCallBack();
                }
                if(eventItem.type.equals("write")){
                    eventItem.writeCallBack();
                }
            }
        }
    });

上面例子的 masterPool.execute 执行执行了一个线程,实际上可以 执行任意多个。

主要逻辑是:

1 线程池的线程的Selector是独立的

2 主线程池线程接收到 accept 事件,将对应的 fd 注册到从的 selector,并监听相应的事件

3 从线程池的线程负责处理耗时的任务

参考资源:

netty权威指南2 

腾讯课堂零声学院

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值