本篇文章着重於淺析一下Netty的事件處理流程,Netty版本爲netty-3.6.6.Final。java
Netty定義了很是豐富的事件類型,表明了網絡交互的各個階段。而且當各個階段發生時,觸發相應的事件交給pipeline中定義的handler處理。bootstrap
舉個例子,以下一段簡單的代碼:網絡
ChannelFactory factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); ServerBootstrap bootstrap = new ServerBootstrap(factory); bootstrap.setPipelineFactory(new PipelineFactory()); bootstrap.setOption("child.tcpNoDelay", true); bootstrap.setOption("child.keepAlive", true); bootstrap.bind(new InetSocketAddress(7080));
Netty中觸發事件幾乎都是靠Channels類中的幾個靜態fire函數,所以經過在這些函數中加上Sysout方法,就能夠看出這一個簡單的bind方法觸發了多少事件,以下:eclipse
fireChannelOpen(final Channel channel) upstream
bind(final Channel channel, final SocketAddress localAddress) downstream
fireChannelBound(final Channel channel, final SocketAddress localAddress) upstreamtcp
因而可知由這幾個函數觸發了OPEN、BOUND和BIND事件。函數
Netty中的事件大體能夠分爲upstream事件和downstream事件。簡單的說,upstream事件是內獲取外資源時觸發的事件如messageReceived等等,而downstream事件則是內向外發送資源時觸發的事件如write、connect等等。this
與之相對應的,處理upstream事件的是upstreamhandler,處理downstream事件的是downstreamhandler,也有能夠處理兩類事件的channelhandler。咱們能夠經過繼承handler來實現本身的業務邏輯。spa
Upstream事件的典型是messageReceived,在Netty中抽象爲MessageEvent
,即接收到了消息。而downstream事件的典型是write,在Netty中也抽象爲MessageEvent
,即發送消息。一個比較完整的事件表以下:.net
upstream事件包括:線程
downstream事件包括
Netty經過pipeline來存放upstreamhandler和downstreamhandler,在pipeline中添加handler的源代碼以下:
public class PipelineFactory implements ChannelPipelineFactory { public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); //並不具體處理事件,只是輸出相關事件的string pipeline.addLast("1", new UpStreamHandler1()); //單純的丟棄事件 pipeline.addLast("2", new DiscardServer()); return pipeline; } }
在Netty中,upstreamhandler的處理順序是從前向後,而downstreamhandler的順序是從後往前。根本緣由是pipeline中維護了一個雙向鏈表,handler的處理順序不一樣是由於upstream是從head->tail遍歷,而downstream事件是從tail->head遍歷。
以DefaultChannelPipeline爲例,如下分別是添加handler至鏈表的代碼和訪問upstreamhandler的代碼
public synchronized void addLast(String name, ChannelHandler handler) { if (name2ctx.isEmpty()) { init(name, handler); } else { checkDuplicateName(name); //一段典型的插入到鏈表尾部並更新尾指針的代碼 DefaultChannelHandlerContext oldTail = tail; DefaultChannelHandlerContext newTail = new DefaultChannelHandlerContext(oldTail, null, name, handler); callBeforeAdd(newTail); oldTail.next = newTail; tail = newTail; name2ctx.put(name, newTail); callAfterAdd(newTail); } }
public void sendUpstream(ChannelEvent e) { //從頭部開始遍歷,相對的是,downstream則是從尾部開始遍歷 DefaultChannelHandlerContext head = getActualUpstreamContext(this.head); if (head == null) { if (logger.isWarnEnabled()) { logger.warn( "The pipeline contains no upstream handlers; discarding: " + e); } return; } sendUpstream(head, e); }
前文已經說過,Netty中經過Channels中的靜態方法來觸發事件,這些靜態函數列舉以下:
1.fireChannelOpen;2.fireChannelBound;3.fireChannelConnected等等。
直接來看fireChannelOpen的源碼,看看Netty究竟是怎麼作的。
public static void fireChannelOpen(final Channel channel) { // Notify the parent handler. if (channel.getParent() != null) { fireChildChannelStateChanged(channel.getParent(), channel); } channel.getPipeline().sendUpstream( new UpstreamChannelStateEvent( channel, ChannelState.OPEN, Boolean.TRUE)); }
這個sendUpstream究竟是幹嗎的了?
void sendUpstream(final DefaultChannelHandlerContext ctx, final ChannelEvent e) { try { //從鏈表頭部開始,取出每一個節點中的handler直接對channelevent進行處理 ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e); } catch (Throwable t) { notifyHandlerException(e, t); } }
而後具體到handler又是怎麼處理各個事件的了?以SimpleChannelUpstreamHandler爲例,以下:
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e) throws Exception { //根據事件類型進行不一樣的處理 if (e instanceof MessageEvent) { messageReceived(ctx, (MessageEvent) e); } else if (e instanceof WriteCompletionEvent) { WriteCompletionEvent evt = (WriteCompletionEvent) e; writeComplete(ctx, evt); } else if (e instanceof ChildChannelStateEvent) { ...... } } public void messageReceived( final ChannelHandlerContext ctx, final MessageEvent e) throws Exception { //直接將事件傳至下一個handler進行處理 ctx.sendUpstream(e); }
源碼看到如今已經很明顯了,在Netty裏,pipeline中維護了一個handler的鏈表。每當事件觸發時,就會從雙向鏈表的頭部(對於downstream事件則是尾部)開始遍歷,這樣每一個handler都會對事件進行處理。在handler裏,能夠根據事件類型作相應的處理後傳至下一個handler繼續處理(甚至能夠截斷處理鏈)。
須要注意的是,單次流程是在一個線程中實現的,是串行的。所以若是其中一個handler是阻塞的,就會影響總體的效果。
固然netty也已經提供瞭解決方案,能夠經過繼承ExecutionHandler的handler來處理這類耗時的操做。而這麼作的原理是什麼,請期待下一篇文章。