事件和异常的传播 · 农场主的黑科技.

inBound事件的传播

  • 何为inBound事件以及ChannelInboundHandler
  • ChannelRead事件的传播
    ChannelRead是典型的inbound事件,以他为例了解inbound事件的传播
  • SimpleInBoundHandler处理器

何为inBound事件以及ChannelInboundHandler

ChannelHandler的继承关系

  • ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter.
    用户代码中常见.平时自定义channelHandler时都会继承与他们
  • ChannelHandler,所有处理器的抽象
  • ChannelHandlerAdapter
    ChannelHandler的默认实现
  • ChannelInboundHandler,ChannelOutboundHandler
    在ChannelHandler的基础上,自定义功能

ChannelHandler定义了哪些功能

123456789101112131415161718
public interface  {

    void handlerAdded(ChannelHandlerContext ctx) throws Exception;    //handler被pipeline删除时的回调    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;	//出现异常时的回调

    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

 	//一个注解,是否可以被多个pipeline添加    @Inherited    @Documented    @Target(ElementType.TYPE)    @Retention(RetentionPolicy.RUNTIME)    @interface Sharable {        // no value    }}

ChannelInboundHandler在ChannelHandler的基础上扩展了什么

12345678910111213141516171819202122232425
public interface ChannelInboundHandler extends  {	//回调,Handler注册到nioEventLoop的selector上时    void channelRegistered(ChannelHandlerContext ctx) throws Exception;    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;

    //channel的激活或失效时的回调    void channelActive(ChannelHandlerContext ctx) throws Exception;    void channelInactive(ChannelHandlerContext ctx) throws Exception;

	//channel读到数据或接收到连接时的回调    //对于服务端而言是连接,对于客户端channel则是bytebuf的数据    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

	//trigger一些用户自定义的事件    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

    //可写状态发生了改变    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;

    //出现异常时的回调    @Override    @SuppressWarnings("deprecation")    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;}

这次由ChannelRead为例,看一下inbound事件是如何传播的.

ChannelRead事件的传播

这里创建3个自定义的InboundHandler测试ChannelRead事件

12345678910111213141516171819202122232425262728
public class InBoundHandlerA extends ChannelInboundHandlerAdapter {

    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        System.out.println("InBoundHandlerA: " + msg);        ctx.fireChannelRead(msg);    }}public class InBoundHandlerB extends ChannelInboundHandlerAdapter {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        System.out.println("InBoundHandlerB: " + msg);//打印        ctx.fireChannelRead(msg);//继续传播    }

    @Override    public void channelActive(ChannelHandlerContext ctx) {        //channel被激活时取到pipeline,激活channelRead事件        ctx.channel().pipeline().fireChannelRead("hello world");    }}public class InBoundHandlerC extends ChannelInboundHandlerAdapter {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        System.out.println("InBoundHandlerC: " + msg);        ctx.fireChannelRead(msg);    }}

服务端启动代码添加childHandler的部分为

12345678
.childHandler(new ChannelInitializer<SocketChannel>() {    @Override    public void initChannel(SocketChannel ch) {        ch.pipeline().addLast(new InBoundHandlerA());        ch.pipeline().addLast(new InBoundHandlerB());        ch.pipeline().addLast(new InBoundHandlerC());    }});

此时启动后在控制台执行telnet 127.0.0.1 8888,它的后台输出结果为

123
InBoundHandlerA: hello worldInBoundHandlerB: hello worldInBoundHandlerC: hello world

如果把添加childHandler的部分改为下面的顺序

123
ch.pipeline().addLast(new InBoundHandlerA());ch.pipeline().addLast(new InBoundHandlerC());ch.pipeline().addLast(new InBoundHandlerB());

那么输出是

123
InBoundHandlerA: hello worldInBoundHandlerC: hello worldInBoundHandlerB: hello world

可以推测出,inbound和添加顺序相关.在InboundHandlerBchannelActive()中打个断点.看一下它的fireChannelRead("hello world")的逻辑.会看到它会调用:

12345678910111213141516171819202122
@Overridepublic final ChannelPipeline fireChannelRead(Object msg) {    //head节点    AbstractChannelHandlerContext.invokeChannelRead(head, msg);    return this;}---    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);        EventExecutor executor = next.executor();        if (executor.inEventLoop()) {//true            //此时的next为head            next.invokeChannelRead(m);        } else {            executor.execute(new Runnable() {                @Override                public void run() {                    next.invokeChannelRead(m);                }            });        }    }

也就是说,当遇到channelRead事件时它会从调用head的invokeChannelRead.继续看

123456789101112131415
private void invokeChannelRead(Object msg) {    if (invokeHandler()) {        //调用head的channelRead()        ((ChannelInboundHandler) handler()).channelRead(this, msg);    } else {        fireChannelRead(msg);    }}---    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {    //也是调用head的fireChannelRead    // 这里head会把fireChannelRead原封不动的进行传播    ctx.fireChannelRead(msg);}

这个fireChannelRead是io.netty.channel.AbstractChannelHandlerContext#fireChannelRead.它会调用findContextInbound()来获取下一个inboundHandler

1234567891011121314
    @Override    public ChannelHandlerContext fireChannelRead(final Object msg) {        //获取下一个inboundHandler        invokeChannelRead(findContextInbound(), msg);        return this;    }---    private AbstractChannelHandlerContext findContextInbound() {        AbstractChannelHandlerContext ctx = this;        do {            ctx = ctx.next;        } while (!ctx.inbound);//寻找下一个inbound节点        return ctx;    }

获取到下一个inboundHandler,也就是InboundHandlerA后它会调用io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead,这个和之前head调用的是完全相同的方法,他回去调用当前inboundHandler,也就是InboundHandlerA的readChannel方法,它就是我们在用户代码中定义的部分.

12345678
public class InBoundHandlerA extends ChannelInboundHandlerAdapter {

    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        System.out.println("InBoundHandlerA: " + msg);        ctx.fireChannelRead(msg);//继续往下传播    }}

然后InBoundHandlerAchannelRead()又会调用下一个InBoundHandlerBchannelRead(),它又会接着调用InBoundHandlerCchannelRead()

此时回顾一下InBoundHandlerB中的逻辑,会发现.pipeline中inBound的传播方式为:

12
ctx.fireChannelRead(msg);	//从当前节点继续往下传播ctx.channel().pipeline().fireChannelRead("hello world");//从head节点开始往下传播

当传播到最后一个节点(C)时,它会传播到最后的Tail节点,调用它的channelRead().之前说过tial是用来做一些收尾工作,

1234567891011121314151617
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {    //事件一直传播到tail,    //tail会认为没有被处理,这个方法会打印一个logger.debug.来提醒开发者    onUnhandledInboundMessage(msg);}---    protected void onUnhandledInboundMessage(Object msg) {    try {        logger.debug(            "Discarded inbound message {} that reached at the tail of the pipeline. " +            "Please check your pipeline configuration.", msg);    } finally {        //如果时bytebuf就进行释放        ReferenceCountUtil.release(msg);    }}

SimpleInBoundHandler处理器

以下面的自定义Handler为例,看一下它的使用场景

123456789101112131415161718192021
public class AuthHandler extends SimpleChannelInboundHandler<ByteBuf> {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        //如果没有把这里的ByteBuf传播到tail,那么tail节点就不会帮你释放这段ByteBuf        //通常这种情况下你需要手动进行释放,        //而SimpleChannelInboundHandler会帮你自动释放他们    }

    @Override    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf password) throws Exception {        if (paas(password)) {            ctx.pipeline().remove(this);        } else {            ctx.close();        }    }

    private boolean paas(ByteBuf password) {        return false;    }}

看一下SimpleChannelInboundHandlerchannelRead()

12345678910111213141516171819202122
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {    boolean release = true;    try {        //如果是inbound        if (acceptInboundMessage(msg)) {            //转为bytebuf            I imsg = (I) msg;            //抽象方法            //继承SimpleChannelInboundHandler时你可以做一些你想干的,并且不用考虑释放            channelRead0(ctx, imsg);        } else {            release = false;            ctx.fireChannelRead(msg);        }    } finally {        if (autoRelease && release) {            //自动释放            ReferenceCountUtil.release(msg);        }    }}

也就是说使用SimpleChannelInboundHandler时不需要管channelRead()而是通过channelRead0()来做原本在channelRead()中的逻辑.因为SimpleChannelInboundHandler的channelRead()中定义了从执行channelRead0()直到释放的过程,所以当channelRead0()被执行后它会自动帮你去释放

outBound事件的传播

  • 何为outBound事件以及ChannelOutBoundHandler
  • write()事件的传播
    典型的outBound事件

何为outBound事件以及ChannelOutBoundHandler

ChannelOutboundHandler在ChannelHandler的基础上扩展了什么

1234567891011121314151617181920212223
public interface ChannelOutboundHandler extends  {	//端口绑定,服务端启动时的那个    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

    void connect(            ChannelHandlerContext ctx, SocketAddress remoteAddress,            SocketAddress localAddress, ChannelPromise promise) throws Exception;

    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    void read(ChannelHandlerContext ctx) throws Exception;

    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;

    void flush(ChannelHandlerContext ctx) throws Exception;}

相对于InBounder,outBound更像是用户主动发起的操作.而InBounder更类似于事件触发

write()事件的传播

这里创建3个自定义的OutboundHandler测试write事件

12345678910111213141516171819202122232425262728293031323334
public class OutBoundHandlerA extends ChannelOutboundHandlerAdapter {

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
System.out.println("OutBoundHandlerA: " + msg);
ctx.write(msg, promise);
}
}
---
public class OutBoundHandlerB extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
System.out.println("OutBoundHandlerB: " + msg);
ctx.write(msg, promise);
}

@Override
public void handlerAdded(final ChannelHandlerContext ctx) {
//定时器,模拟服务端处理完成数据后给客户端的响应
ctx.executor().schedule(() -> {
ctx.channel().write("hello world");
}, 3, TimeUnit.SECONDS);
}
}
大专栏  事件和异常的传播 · 农场主的黑科技.pan class="line">---

时间: 2024-10-26 13:21:36

事件和异常的传播 · 农场主的黑科技.的相关文章

JavaScript的事件及异常捕获

事件处理 [onClick]单击事件.[onMouseOver]鼠标经过事件.[onMouseOut]鼠标移出事件.[onChange]文本内容改变事件.[onSelect]文本被框选事件.[onFoucus]得到光标事件.[onBlur]光标失去事件.[onLoad]网页加载事件(在body标签中添加).[onUnload]网页关闭事件(在body标签中添加或者使用window.onload=function(){}) 事件注册及监听 1. DOM0级事件处理 在标签中添加onClick或其他

2017-12-19 每日黑科技

[zipedit] [碧桂园信管中心] [资讯与趋势] 1.腾讯京东入股唯品会:垂直电商,唇亡齿寒 阿里京东进一步品类渗透--阿里与京东交火猛烈,从3C家电到服饰.奢侈品,双方进一步品类渗透 老一辈垂直电商纷纷寻求出路--寡头向细分领域的拓展让垂直电商的日子不再风光 电商新势力的崛起--野蛮生长后,社交电商将走向何方? 编者按:寡头垄断市场,怎么看也不是什么好事. http://36kr.com/topics/1625?version=new 2.唯品会站队:互联网场景几乎被巨头瓜分殆尽 为什么

Python3网络爬虫(十一):爬虫黑科技之让你的爬虫程序更像人类用户的行为(代理IP池等)

原文链接: Jack-Cui,http://blog.csdn.net/c406495762 运行平台: Windows Python版本: Python3.x IDE: Sublime text3 1 前言 近期,有些朋友问我一些关于如何应对反爬虫的问题.由于好多朋友都在问,因此决定写一篇此类的博客.把我知道的一些方法,分享给大家.博主属于小菜级别,玩爬虫也完全是处于兴趣爱好,如有不足之处,还望指正. 在互联网上进行自动数据采集(抓取)这件事和互联网存在的时间差不多一样长.今天大众好像更倾向于

试读—增长黑客,创业公司必知的“黑科技”

概述 刚一看到书名,最引起注意的是黑客两个字,那个带着神秘色彩,让无数程序员羡慕嫉妒恨的角色.但仔细一看,增长黑客,创业公司必知的"黑科技",是讲公司如何以切实的依据.低廉的成本.可控的风险来达成用户增长.活跃度上升.收入额增加等知识及案例的,这对于初创公司又没有充足的资金去燃烧以改变用户习惯的情况无疑是雪中送炭.指北之针. 什么是增长黑客? 本书适合哪些读者? 增长 靠原始积累实现增长的时代已经过去,也不适合互联网.移动互联网.互联网+的模式.我们经常能看到类似的新闻"某公

黑科技玩出跨界营销新姿势,泡单词携手百果园深化品牌影响力

1月10日起,广州.佛山.珠海三地的百果园门店,被家长群体给包了场.原因只是其和泡单词联手推出特惠活动,买水果就送科技感十足的AR互动绘本. 一个黑科技就能搅动无数家长的心?没这么简单. 又一次跨界营销?关键在于实现场景及流量互通 据悉,百果园和泡单词都是第一次尝试跨界营销. 一个生鲜.一个教育,从线下到线上,这样的一次活动完全是一场标准的互联网+状态下的跨界营销,亦是近期营销领域从饥饿营销.参与感营销等老套路中跳脱出来,形成的主流营销模式. 就在此前的2016年圣诞节,一嗨租车就和NBA进行过

优云亮相GOPS2017全球运维大会 “黑科技”获全场最高关注

2017年4月21日,GOPS――2017全球运维大会于深圳圣淘沙酒店拉开帷幕.GOPS全球运维大会由高效运维社区(GreatOPS)和开放运维联盟(OOPSA)联合主办,由工信部信通院数据中心联盟(DCA)任指导单位.全球运维大会是国内第一个运维行业大会,面向互联网及传统行业.广大运维技术人员,传播先进技术思想和理念,分享业内最佳实践. 优云软件携运维黑科技"ChatOps"亮相,获得全场最高关注. ChatOps是什么? 优云 ChatOps是一款颠覆性的运维协同产品,基于即时信息

Iphone X黑科技大揭秘,这几大摄像功能是要上天!

北京时间 9 月 13 日 凌晨 1 点,苹果在美国召开了 2017 年秋季新品发布会 今年是 iPhone 诞生 10 周年, iPhone 已经三年没有大变样了,因而今年的关注度也是异常的高,各种爆料也是轰炸刷屏了一整年,这次发布会自然也是万众瞩目. 这场亮点十足的发布会推出了果粉们期待已久的新款iphoneX. 8 和8Plus,还有最Apple Watch 3.新一代Apple TV.iMac Pro,以及全新升级的iOS11 系统.在这里小编要扒一扒新款iphoneX的逆天黑科技,尤其

2017.12.04 今日黑科技

[zipedit] [碧桂园信管中心] [资讯与趋势] 1.乌镇互联网大会首日行业大咖发言总结 http://36kr.com/p/5106103.html 2.马化腾:改掉这七点,让产品自己说话时 马化腾先生认为产品经理常犯的七个错误,看一看这七个错误都是什么以及解决之道,或许能让你少走一些弯路. http://36kr.com/p/5106165.html 3.传统酒店智能升级,机器人在新加坡酒店业刮起了一阵风 新加坡一些当地知名的酒店已经成功将机器人应用到日常工作中,不仅是后台运营,还有在

2017黑科技趋势最具看点的十大新品

腾讯数码讯(Human)作为一年一度的全球消费电子市场风向标,今年同样在拉斯维加斯举办的CES 2017消费电子展,依然吸引了一大批全球各个领域的厂商参展,从科技巨头到初创小团队.从传统汽车厂商再到家电企业,似乎所有能与科技沾边的公司都希望能在CES 2017上好好展示一次自己的风采. 其实每年的CES都有一些明星产品给我们留下深刻的印象,今年的也不例外.而这些明星产品不仅仅只是单单一款产品,更是代表了各自行业在进入到2017年之后的一个发展趋势和方向.而就将这样的变化能否成为未来的主流.或只是