Netty源码—一、server启动(1)

Netty作为一个Java生态中的网络组件有着举足轻重的位置,各种开源中间件都使用Netty进行网络通信,比如Dubbo、RocketMQ。可以说Netty是对Java NIO的封装,比如ByteBuf、channel等的封装让网络编程更简单。

在介绍Netty服务器启动之前需要简单了解两件事:

reactor线程模型

linux中的IO多路复用

reactor线程模型

关于reactor线程模型请参考这篇文章,通过不同的配置Netty可以实现对应的三种reactor线程模型

reactor单线程模型

reactor多线程模型

reactor主从多线程模型

// reactor单线程模型,accept、connect、read、write都在一个线程中执行 EventLoopGroup group = new NioEventLoopGroup(1); bootStrap.group(group); // reactor多线程,accept在bossGroup中的一个线程执行,IO操作在workerGroup中的线程执行 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); bootStrap.group(bossGroup , workerGroup); // reactor主从多线程,用来accept连接的是在一个线程池中执行,这个时候需要bind多个port,因为Netty一个bind的port会启动一个线程来accept EventLoopGroup bossGroup = new NioEventLoopGroup(2); EventLoopGroup workerGroup = new NioEventLoopGroup(); bootStrap.group(bossGroup , workerGroup);

注意:本文后面的介绍如无特别说明都是基于reactor多线程模型

linux中的IO多路复用

linux中的网络编程模型也是在不断演变的,下面是依次演变的顺序(具体可参考《UNIX网络编程卷1:套接字联网API》第三版的第4、6章)

accept

阻塞等待连接,接收到新的连接后新起线程来处理接收到的连接,然后在新的线程中阻塞等待新的数据到来

select

根据入参的不同有三种情况

永远等下去,直到监听的描述符有任意的IO事件才返回

等待一段固定时间,如果时间到之前有IO事件则提前返回,否则等待超时后返回

不等待,检查描述符后立即返回,称为轮询

select会返回就绪的文件描述符的个数,需要轮询所有socket,判断每个socket的状态来确定是否有事件、是什么事件

poll

相比较于selectpoll是阻塞等待的,只有有读写事件的时候才会返回,返回的是有读写事件的socket个数,并且将对应的socket的事件置位,自己从所有socket中找到具体的socket

epoll

相比较于poll,epoll可以将只有确实有IO事件的描述符返回,大并发下只有少量活跃连接的情况下使用

较poll的优势

不用开发者重新准备文件描述符集合(较poll入参简单)

无需遍历所有监听的描述符,只要遍历哪些被内核IO事件异步唤醒而加入ready队列的描述符集合

Java NIO在linux的实现就是基于epoll的。epoll的编程模型:

创建socket,socket方法

绑定服务器ip,port,bind方法

监听绑定了ip:port的文件描述符,listen方法

创建epoll句柄(文件描述符),配置最大监听的文件描述符个数,epoll_create方法

配置epoll监听的文件描述符的事件:注册、修改、删除某个文件描述符对应的事件

监听所有已配置的描述符,epoll_wait

有新的事件的时候遍历返回的描述符,处理对应的事件

如果是来自客户端的连接,则将accept到的文件描述符注册到epoll中

如果是读写事件则分别处理

注意:Netty封装的Java NIO是跨平台的,后面还是以linux平台为例来介绍

接下来言归正传,来看看Netty的服务器启动过程做了什么事情。Netty作为一个网络框架,和普通网络编程做的事情基本上一样,对应于上面epoll的编程模型,Netty的启动过程为

初始化线程池,初始化selector

初始化NioServerSocketChannel

绑定服务器ip:port

将NioServerSocketChannel注册到selector中

配置NioServerSocketChannel监听的事件

使用selector.select等待新的IO事件

如果是来自客户端的连接则将NioSocketChannel注册到selector上(如果是新的线程则是新的selector)

如果是普通IO事件则在worker线程中处理

线程池初始化

在介绍NioEventLoopGroup之前先看下NioEventLoop

Netty源码—一、server启动(1)

可以看到NioEventLoop继承自SingleThreadEventExecutor,是一个单线程的executor,在线程中死循环监听IO事件。主要方法有

// 初始化selector io.netty.channel.nio.NioEventLoop#openSelector // 将channel注册到selector io.netty.channel.nio.NioEventLoop#register // 监听selector上的事件 io.netty.channel.nio.NioEventLoop#select

一个NioEventLoop会初始化一个selector,处理selector上注册的channel。

NioEventLoopGroup从名字上就可以看出来是由多个NioEventLoop组成,类关系图如下

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsffps.html