Netty学习——源码篇8 Outbound/Inbound传播方式 备份

接上篇:Netty学习——源码篇7 Pipeline的事件传播机制

1 Outbound事件传播方式

        Outbound事件都是请求事件(Request Event),即请求某件事情的发生,然后通过Outbound事件进行通知。

        Outbound事件的传播方向是从Tail到customContext再到Head。下面以Connect事件为例,分析一下Outbound事件的传播机制。

        首先,当用户调用了Bootstrap的connect方法时,就会触发一个Connect请求事件,此调用会触发调用链,如下图所示。

fc9bce2ba489418d81e6d6f2fe56b78e.jpeg

        继续跟踪,发现AbstractChannel的connect方法又调用了DefaultChannelPipeline的connect 方法,代码如下:

    public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
        return pipeline.connect(remoteAddress, localAddress, promise);
    }

         而pipeline.connect()方法的代码如下:

    public final ChannelFuture connect(
            SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
        return tail.connect(remoteAddress, localAddress, promise);
    }

        可以看到,当Outbound事件(这里是Connect事件)传递到Pipeline后,其实是以Tail为起点开始传播的。

        而tail.connect调用的是AbstractChannelHandlerContext的connect方法。

 public ChannelFuture connect(
            final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {

        if (remoteAddress == null) {
            throw new NullPointerException("remoteAddress");
        }
        if (!validatePromise(promise, false)) {
            // cancelled
            return promise;
        }

        final AbstractChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeConnect(remoteAddress, localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeConnect(remoteAddress, localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }

        顾名思义,findContextOutbound 方法的作用是以当前Context为起点,向Pipeline中Context双向链表的前段寻找第一个Outbound属性为true的Context(即关联ChannelOutboundHandler的Context),然后返回。findContextOutbound()方法实现代码:

    private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

        当找到了一个Outbound的Context后,就调用它的invokeConnect方法,这个方法会调用Context关联的ChannelHandler的connect方法,代码如下:

    private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
        if (invokeHandler()) {
            try {
                ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise);
            } catch (Throwable t) {
                notifyOutboundHandlerException(t, promise);
            }
        } else {
            connect(remoteAddress, localAddress, promise);
        }
    }

        如果用户没有重写ChannelHandler的connect方法,那么会调用ChannelOutboundHandlerAdapter的connect方法,代码如下:

    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception {
        ctx.connect(remoteAddress, localAddress, promise);
    }

        可以看到,ChannelOutboundHandlerAdapter的connect方法仅调用了ctx.connect方法,而这个调用又回到了Cotext.connect方法调用Connect.findContextOutbound方法,然后调用next.invokeConect方法,其次调用handler.connect方法,最后又调用Context.connect方法,如此循环下去,直到Connect事件传递到DefaultChannelPipeline的双向链表的头节点,即Head中。为什么会传递到Head中呢?回想一下,Head实现了ChannelOutboundHandler,因此它的Outbound属性是true。

        因为Head本身即是一个ChannelHandlerCont,又实现了ChannelOutboundHandler接口,所以当connect消息传递到Head后,会将消息传递到对应的ChannelHandler中处理,而Head的handler方法返回的就是Head本身。因此最终Connect事件是在Head中被处理的。Head的Connect事件处理逻辑的代码如下:

       @Override
        public void connect(
                ChannelHandlerContext ctx,
                SocketAddress remoteAddress, SocketAddress localAddress,
                ChannelPromise promise) throws Exception {
            unsafe.connect(remoteAddress, localAddress, promise);
        }

        到这里,整个Connect请求事件就结束了。下图描述了整个 Connect请求事件的处理过程。

60d046ff7adf47fda8970cbbcda3ae7f.jpeg

        仅仅以Connect请求事件为例,分析了Outbound事件的传播过程,但是其实所有的Outbound的事件传播都遵循着一样的传播规律。

2 Inbound事件传播方式

        Inbound和Outbound事件的处理过程是类似的,只是传播方向不同。

        Inbound事件是一个通知事件,即某件事已经发生了,然后通过Inbound事件进行通知。Inbound通常发生在Channel的状态改变或I/O事件就绪时。

        Inbound的热点是其传播方向从Head到customContext再到Tail.

        上面分析了connect()方法其实是一个Outbound事件,那么接着分析connect()事件后会发生什么Inbound事件,并最终找到Outbound和Inbound事件之间的联系。当Connect()事件传播到Unsafe后,其实是在AbstractNioUnsafe的connect方法中进行处理的,代码如下:
 

public final void connect(
                final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }

            try {
                if (connectPromise != null) {
                    // Already a connect in process.
                    throw new ConnectionPendingException();
                }

                boolean wasActive = isActive();
                if (doConnect(remoteAddress, localAddress)) {
                    fulfillConnectPromise(promise, wasActive);
                } else {
                    connectPromise = promise;
                    requestedRemoteAddress = remoteAddress;

                    // Schedule connect timeout.
                    int connectTimeoutMillis = config().getConnectTimeoutMillis();
                    if (connectTimeoutMillis > 0) {
                        connectTimeoutFuture = eventLoop().schedule(new Runnable() {
                            @Override
                            public void run() {
                                ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
                                ConnectTimeoutException cause =
                                        new ConnectTimeoutException("connection timed out: " + remoteAddress);
                                if (connectPromise != null && connectPromise.tryFailure(cause)) {
                                    close(voidPromise());
                                }
                            }
                        }, connectTimeoutMillis, TimeUnit.MILLISECONDS);
                    }

                    promise.addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(ChannelFuture future) throws Exception {
                            if (future.isCancelled()) {
                                if (connectTimeoutFuture != null) {
                                    connectTimeoutFuture.cancel(false);
                                }
                                connectPromise = null;
                                close(voidPromise());
                            }
                        }
                    });
                }
            } catch (Throwable t) {
                promise.tryFailure(annotateConnectException(t, remoteAddress));
                closeIfClosed();
            }
        }

        在AbstractNioUnsafe的connect方法中,先调用doConnect方法进行实际的Socket连接,当连接后会调用fulfillConnectPromise方法,代码如下:

private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
            if (promise == null) {
                
                return;
            }

            boolean active = isActive();

           
            boolean promiseSet = promise.trySuccess();

            if (!wasActive && active) {
                pipeline().fireChannelActive();
            }

           
            if (!promiseSet) {
                close(voidPromise());
            }
        }

        可以看到,在fulfillConnectPromise方法中,会通过调用pipeline.fireChannelActive方法将通道激活的消息(即Socket连接成功)发送出去。而这里,当调用pipeline.fireXXX后,就是Inbound事件的起点。因此当调用pipeline.fireChannelActive方法时,就产生了一个ChannelActive Inbound事件,接下来看一下Inbound事件是怎么传播的,代码如下:

    @Override
    public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }

        在fireChannelActive方法中调用了invokeChannelActive(head)方法,因此可以证明Inbound事件在pipeline中传输的起点是Head。head.invokeChannelActive(head)方法代码如下:

    static void invokeChannelActive(final AbstractChannelHandlerContext next) {
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelActive();
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelActive();
                }
            });
        }
    }

        回想一下在Outbound事件(例如Connect事件)的传输过程中,也有类似的如下操作:

        1、首先调用findContextInbound(),从Pipeline的双向链表中找到第一个Inbound属性为true的Context,然后将其返回。

        2、调用Context的invokeChannelActive()方法,invokeChannelActive方法代码如下:

    private void invokeChannelActive() {
        if (invokeHandler()) {
            try {
                ((ChannelInboundHandler) handler()).channelActive(this);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelActive();
        }
    }

        这个方法和Outbound的对应方法如出一辙。与Outbound一样,如果用户没有重写channelActive方法,就会调用ChannelInboundHandlerAdapter的channelActive方法,代码如下:

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();
    }

        同样的,在ChannelInboundHandlerAdapter的channelActive方法中,仅仅调用了ctx.fireChannelActive方法,因此就调用Context.fireChannelActive方法,其次调用Connect.findContextInbound()方法,然后调用nextContext.invokeChannelActive方法。再然后调用nextHandler.channelActive()方法,最后调用nextContext.fireChannelActive方法,如此循环。下图描述了Inbound事件的传输过程。

3 小结

3.1 Outbound事件传播过程总结如下

        1、Outbound 事件是请求事件(由Context发起一个请求,并最终由Unsafe处理这个请求)。

        2、Outbound事件 发起者是Channel。

        3、Outbound事件的处理者是Unsafe。

        4、Outbound事件在Pipeline中的传输方向是从Tail到Head。

        5、在ChannelHandler中处理事件时,如果这个Handler不是最后一个Handler,则需要调用ctx的方法将此事件继续传播下去。如果不这样做,那么此事件的传播会提前终止。

        6、Outbound事件传播方向是,从Context.OUT_EVT()方法到Connect.findContextOutbound()方法,再到nextContext.invokeOUT_EVT方法,再到nextHandler.OUT_EVT方法,最后到nextContext.OUT_EVT方法。

3.2 Inbound事件传播过程总结如下

        1、Inbound事件为通知事件,当某件事情已经就绪后,会通知上层。

        2、Inbound事件的发起者是Unsafe。

        3、Inbound事件的处理者是Channel,如果用户没有实现自定义的处理方法,那么Inbound事件默认的处理这是TailContext,并且其处理方法是空实现。

        4、Inbound事件在Pipeline中的传输方法是从Head到Tail。

        5、在ChannelHandler中处理事件时,如果这个Handler不是最后一个Handler,则需要调用ctx.fireIN_EVT()方法将此事件继续传播下去,如果不这样做,那么此事件的传播会提前终止。

        6、Inbound事件的传播方向是,从Context.fireIN_EVT()方法到Connect.findContextInbound()方法,再到nextContext.invokeIN_EVT()方法,再到nextHandler.IN_EVT方法,最后到nextContext.fireIN_EVT方法。

  • 30
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
数字乡村和智慧农业的数字化转型是当前农业发展的新趋势,旨在通过应用数字技术,实现农业全流程的再造和全生命周期的管理服务。中国政府高度重视这一领域的发展,提出“数字中国”和“乡村振兴”战略,以提升国家治理能力,推动城乡融合发展。 数字乡村的建设面临乡村治理、基础设施、产业链条和公共服务等方面的问题,需要分阶段实施《数字乡村发展战略纲要》来解决。农业数字化转型的需求包括满足市民对优质农产品的需求、解决产销对接问题、形成优质优价机制、提高农业劳动力素质、打破信息孤岛、提高农业政策服务的精准度和有效性,以及解决农业融资难的问题。 数字乡村建设的关键在于构建“1+3+4+1”工程,即以新技术、新要素、新商业、新农民、新文化、新农村为核心,推进数据融合,强化农业大数据的汇集功能。数字农业大数据解决方案以农业数字底图和数据资源为基础,通过可视化监管,实现区域农业的全面数字化管理。 数字农业大数据架构基于大数据、区块链、GIS和物联网技术,构建农业大数据中心、农业物联网平台和农村综合服务指挥决策平台三大基础平台。农业大数据中心汇聚各类涉农信息资源和业务数据,支持大数据应用。信息采集系统覆盖市、县、乡、村多级,形成高效的农业大数据信息采集体系。 农业物联网平台包括环境监测系统、视频监控系统、预警预报系统和智能控制系统,通过收集和监测数据,实现对农业环境和生产过程的智能化管理。综合服务指挥决策平台利用数据分析和GIS技术,为农业决策提供支持。 数字乡村建设包括三大服务平台:治理服务平台、民生服务平台和产业服务平台。治理服务平台通过大数据和AI技术,实现乡村治理的数字化;民生服务平台利用互联网技术,提供各类民生服务;产业服务平台融合政企关系,支持农业产业发展。 数字乡村的应用场景广泛,包括农业生产过程、农产品流通、农业管理和农村社会服务。农业生产管理系统利用AIoT技术,实现农业生产的标准化和智能化。农产品智慧流通管理系统和溯源管理系统提高流通效率和产品追溯能力。智慧农业管理通过互联网+农业,提升农业管理的科学性和效率。农村社会服务则通过数字化手段,提高农村地区的公共服务水平。 总体而言,数字乡村和智慧农业的建设,不仅能够提升农业生产效率和管理水平,还能够促进农村地区的社会经济发展,实现城乡融合发展,是推动中国农业现代化的重要途径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

geminigoth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值