准备将Netty的源码过一下,一来对本身是个总结消化的过程,二来但愿对那些打算看Netty源码的人(已经熟悉Netty的Reactor模型)能有一些帮助。目前所看Netty版本是4.1.3.Final。tomcat
看下一个简单的Netty服务器端的例子安全
public static void main(String[] args){ EventLoopGroup bossGroup=new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap=new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 200) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(80,0,4,0,4)); ch.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8"))); ch.pipeline().addLast(new TcpServerHandler()); } }); ChannelFuture f=serverBootstrap.bind(8080).sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } }
先来简单说说上述遇到的类:服务器
它主要包含2个方面的功能,注册Channel和执行一些Runnable任务。微信
功能1:先来看看注册Channel,即将Channel注册到Selector上,由Selector来调度Channel的相关事件,如读、写、Accept等事件。多线程
而EventLoopGroup的设计是,它包含多个EventLoop(每个EventLoop一般内部包含一个线程),在执行上述注册过程当中是须要选择其中的一个EventLoop来执行上述注册行为,这里就出现了一个选择策略的问题,该选择策略接口是EventExecutorChooser,你也能够自定义一个实现。并发
从上面能够看到,EventLoopGroup作的工做大部分是一些整体性的工做如初始化上述多个EventLoop、EventExecutorChooser等,具体的注册Channel仍是交给它内部的EventLoop来实现。异步
功能2:执行一些Runnable任务ide
EventLoopGroup继承了EventExecutorGroup,EventExecutorGroup也是EventExecutor的集合,EventExecutorGroup也是掌管着EventExecutor的初始化工做,EventExecutorGroup对于Runnable任务的执行也是选择内部中的一个EventExecutor来作具体的执行工做。oop
netty中不少任务都是异步执行的,一旦当前线程要对某个EventLoop执行相关操做,如注册Channel到某个EventLoop,若是当前线程和所要操做的EventLoop内部的线程不是同一个,则当前线程就仅仅向EventLoop提交一个注册任务,对外返回一个ChannelFuture。.net
总结:EventLoopGroup含有上述2种功能,它更多的是一个集合,可是具体的功能实现仍是选择内部的一个item元素来执行相关任务。 这里的内部item元素一般即实现了EventLoop,又实现了EventExecutor,如NioEventLoop等
上述EventLoopGroup能够将一个Channel注册到内部的一个EventLoop的Selector上,而后对于这个Channel的相关读写等事件,Netty专门设计了一个ChannelPipeline来进行处理。每个Channel都有一个ChannelPipeline来处理该Channel的读写等事件。
上述serverBootstrap的bind过程以下:
建立出你所指定的NioServerSocketChannel,而后初始化一些Socket方面的参数
为上述Channel的ChannelPipeline配置一个ChannelHandler,该ChannelHandler的做用就是在该Channel成功注册到Selector上的时候,初始化一些逻辑,即initChannel方法中执行一些逻辑,该逻辑就是向ChannelPipeline中添加一个新的ChannelHandler即ServerBootstrapAcceptor
而后开始将该Channel注册到上述EventLoopGroup bossGroup中,该EventLoopGroup bossGroup会选择内部的一个EventLoop来执行实际的注册行为(这个时候就是当前线程和操做的EventLoop不是同一个线程,即该过程是异步提交一个Runnable),一旦注册完成,就执行上述ChannelHandler的initChannel方法
至此,就完成了整个bind过程。一旦EventLoop内部的Selector检测到NioServerSocketChannel有新的链接到来的事件,则会交给NioServerSocketChannel的ChannelPipeline来处理,重点就是ChannelPipeline中的上述ServerBootstrapAcceptor,ServerBootstrapAcceptor作以下操做:
1 为新的Channel的ChannelPipeline配置咱们上述代码中的childHandler指定的ChannelHandler
2 将新的Channel注册到了上述EventLoopGroup workerGroup中
bind方法返回的是一个ChannelFuture,从上面咱们也知道该过程是异步的,sync方法则是一直等待到该异步过程结束。
再看下f.channel().closeFuture().sync()这个方法
每个ChannelFuture都是和一个Channel绑定的,因此能够经过ChannelFuture来获取对应绑定的Channel对象
每个Channel对象都有一个CloseFuture closeFuture对象,上述closeFuture方法并非去执行close方法而是获取到这个CloseFuture closeFuture对象,而后调用它的sync方法即等待这个Future的结束。通常正常状况下是不会调用这个Future的结束方法的,只是在上述过程或者其余过程出现问题的时候,如注册到EventLoop失败等才会去调用这个Feture的结束方法,因此正常状况下主线程会一直阻塞在CloseFuture closeFuture的sync方法上。
上述的bossGroup的建立问题。
咱们都知道bossGroup是用来accept链接,而后将链接绑定到workerGroup中的,通常状况下bossGroup设置线程数为1便可(基本只能为1),咱们同时知道Ractor模型中可使用多个Acceptor线程来执行accept操做,加快accept的速度。
若是你想加快accept的速度,想开启多线程来accept,这时候想设置bossGroup的线程数为多个的话,就大错特错了,是根本没效果的。
结合上面的原理,只有在bind端口的时候才会建立一个ServerSocketChannel,而后注册到bossGroup内部的一个EventLoop中,仍然是单线程负责ServerSocketChannel的accept工做,而bossGroup中的多线程仅仅是为bind多个端口服务的。
咱们来看下tomcat是如何容许多个Acceptor线程来执行accept操做的:
1 建立了一个ServerSocketChannel serverSock,并bind到某个端口
2 开启多个Acceptor线程,每一个线程逻辑都是执行上述serverSock的accept方法
没有使用Selector来执行accept操做,能够多线程并发执行上述serverSock的accept方法。
一旦使用了Selector,基本上就至关于将ServerSocketChannel serverSock绑定到了Selector所在线程上了(Selector不是线程安全的,只能在一个线程中被调度执行)
下一篇就要详细描述下EventLoopGroup了。
欢迎关注微信公众号:乒乓狂魔