专栏名称: 爬蜥
目录
相关文章推荐
51好读  ›  专栏  ›  爬蜥

Netty源码分析之一次请求是如何到达channelRead的?

爬蜥  · 掘金  ·  · 2019-03-16 15:08

正文

阅读 32

Netty源码分析之一次请求是如何到达channelRead的?

以下分析只讲NIO

使用java nio做网络编程大致流程如下

这个流程有哪些可以优化的空间?

java nio使用简介
java nio 启动源码分析

Netty是对java网络框架的包装,它本身肯定也会有类似的处理流程。必定在这个方面做了自己的优化处理

Netty 使用入门
Netty Hello world源码分析

获得Selector

使用Netty的时候都会用到对应的EventLoopGroup,它实际上就完成了Selector的初始化过程

Netty自定义了SelectionKey的集合,做了层包装,实际将Selector只有1个SelectorKey的集合换成了默认的两个集合

获得Channel

使用Netty时会执行channel的类型,然后在执行bind方法时,此处就会对channel实行初始化

构建的方式为 class.newInstance() ,以NioServerSocketChannel为例,它执行的就是对应的无参构造函数。

 public NioServerSocketChannel() {
 		//newSocket即返回java的ServerSocketChannel
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
 }
 public NioServerSocketChannel(ServerSocketChannel channel) {
 		//指定当前channel用来接收连接请求,并在父类中指定为非阻塞
        super(null, channel, SelectionKey.OP_ACCEPT);
        //javaChannel()即这里的参数channel
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
复制代码

紧接着Netty开始channel的初始化,在NioServerSocketChannel的pipeline最后添加了一个 ChannelInboundHandlerAdapter ServerBootstrapAcceptor ,它会执有 childGroup childHandler ,childHandler即用户自定义的channelHandler,而childGroup则是处理请求所用的EventLoop,此时整个pipeline的结构为

childGroup为源码中字段的命名,对应为group中传递的worker线程池

channel的注册与监听端口地址关联

注册即建立channel和Selector的关系,值得注意的是,注册使用的线程池为 group ,对应用户传入的线程池即boss线程池,注册和端口、地址关联则顺着Netty的启动流程进行

至此可以看到,java nio所需要的准备工作都已经准备好了,剩下的就是等待事件发生以及处理发生的事件。与普通java nio的不同之处在于

  • Netty准备了两个线程池,channel注册、端口绑定监听的只用到了其中同一个线程池

等待事件发生

NioEventLoop实现了Executor,意味着它接受其它地方提交任务给它执行,execute的大致结构如下

//判断当前正在执行的线程是否是Netty自己的eventLoop中保存的线程
boolean inEventLoop = inEventLoop();
  if (inEventLoop) {
    //往队列里添加任务
  	addTask(task);
  } else {
  	//这里即运行NioEventLoop自身的run方法
	startThread();
  	addTask(task);
  }
复制代码

NioEventLoop启动线程执行run方法,整体结构如下

for (;;) {
 if (hasTasks()) {
    selectNow();
   } else {
    select(oldWakenUp);
   }
  processSelectedKeys();
  runAllTasks();
}
复制代码

run循环处理的流程如下







请到「今天看啥」查看全文