淺析Netty的異步事件驅動(一)

本篇文章着重於淺析一下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來處理這類耗時的操做。而這麼作的原理是什麼,請期待下一篇文章。

相關文章
相關標籤/搜索