netty的pipeline和数据读写

前言

netty采用了类似责任链模式,inbound和outbound分别入栈和出栈顺序执行每个channelHanderContext。整体分为三个类:

1>ChannelPipe 操作(添加删除)context;控制context流动

2>ChannelHandlerContext  持有前一个、后一个ChannelHandlerContext;持有ChannelHandler处理事件

3>ChannelHandler 处理当前业务逻辑

                                                  I/O Request
                                              via Channel or
                                          ChannelHandlerContext
                                                        |
    +---------------------------------------------------+---------------+
    |                           ChannelPipeline         |               |
    |                                                  \|/              |
    |    +---------------------+            +-----------+----------+    |
    |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
    |    +----------+----------+            +-----------+----------+    |
    |              /|\                                  |               |
    |               |                                  \|/              |
    |    +----------+----------+            +-----------+----------+    |
    |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
    |    +----------+----------+            +-----------+----------+    |
    |              /|\                                  .               |
    |               .                                   .               |
    | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
    |        [ method call]                       [method call]         |
    |               .                                   .               |
    |               .                                  \|/              |
    |    +----------+----------+            +-----------+----------+    |
    |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
    |    +----------+----------+            +-----------+----------+    |
    |              /|\                                  |               |
    |               |                                  \|/              |
    |    +----------+----------+            +-----------+----------+    |
    |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
    |    +----------+----------+            +-----------+----------+    |
    |              /|\                                  |               |
    +---------------+-----------------------------------+---------------+
                    |                                  \|/
    +---------------+-----------------------------------+---------------+
    |               |                                   |               |
    |       [ Socket.read() ]                    [ Socket.write() ]     |
    |                                                                   |
    |  Netty Internal I/O Threads (Transport Implementation)            |
    +-------------------------------------------------------------------+

注:其中channel.write和channelHandlerContext.write区别在于channel.write会从tai开始,遍历所有的outboundHandler;channelHandlerContext.write会从当前channelContext开始,遍历余下的outboundHandler。

一.读取数据

NioEventLoop处理读事件,NioEventLoo的processSelectedKey方法,

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            final EventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable ignored) {
                // If the channel implementation throws an exception because there is no event loop, we ignore this
                // because we are only trying to determine if ch is registered to this event loop and thus has authority
                // to close ch.
                return;
            }
            // Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
            // and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
            // still healthy and should not be closed.
            // See https://github.com/netty/netty/issues/5125
            if (eventLoop == this) {
                // close the channel if the key is not valid anymore
                unsafe.close(unsafe.voidPromise());
            }
            return;
        }

        try {
            int readyOps = k.readyOps();
            // We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
            // the NIO JDK channel implementation may throw a NotYetConnectedException.
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }

            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }

            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
            // to a spin loop
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                //处理读事件
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

unsafe.read()分两种:

1.OP_ACCEPT:bossgroup连接事件,后续注册读写到workergroup;处理的类为AbstractNioMessageChannel

2.OP_READ:workgroup的读事件,获取客户端的输入并传入channelPipeline处理业务逻辑;处理的类为AbstractNioByteChannel

处理读写事件,进入DefaultChannelPipeline(包含head和tail的ChannelHandlerContext)中,读取事件从head开始,写时间从tail开始。

下面是责任链读取执行的过程:

 

 

 如果中间的ChannelHandler一直调用fireChannelRead传递下去则会到TailContext,然后直接释放掉buf

 二.输出数据

和读取数据逻辑几乎一致,注意其中channel.write和channelHandlerContext.write区别在于channel.write会从tai开始,遍历所有的outboundHandler;channelHandlerContext.write会从当前channelContext开始,遍历余下的outboundHandler。

输出若不flush写入到ChannelOutboundBuffer类中,是一个增加的链表,默认大小为

                INT DEFAULT_HIGH_WATER_MARK = 64 * 1024

可以通过设置WRITE_BUFFER_HIGH_WATER_MARK调整大小

            bootstrap.childOption(WRITE_BUFFER_HIGH_WATER_MARK, 1024);

调用writeAndFlush会将之前的ChannelOutboundBuffer内容一起输出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值