準備將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了。
歡迎關注微信公衆號:乒乓狂魔