在上例中,inbound开头的handler意味着它是一个inbound handler 。outbound开头的handler意味着它是一个outbound handler 。上例的配置中当一个事件进入inbound时handler的顺序是1,2,3,4,5;当一个事件进入outbound时,handler的顺序是5,4,3,2,1.在这个最高准则下,ChannelPipeline跳过特定handler的处理来缩短stack的深度:
3,4没有实现ChannelInboundHandler,因而一个inbound事件的处理顺序是1,2,5.
1,2没有实现ChannelOutBoundhandler,因而一个outbound事件的处理顺序是5,4,3
若5同时实现了ChannelInboundHandler和channelOutBoundHandler,一个inbound和一个outbound事件的执行顺序分别是125和543.
一个事件跳向下一个handler
如上图所示,一个handler触发ChannelHandlerContext中的事件传播方法,然后传递到下一个handler 。这些方法有:
inbound 事件传播方法:
ChannelHandlerContext#fireChannelRegistered() ChannelHandlerContext#fireChannelActive() ChannelHandlerContext#fireChannelRead(Object) ChannelHandlerContext#fireChannelReadComplete() ChannelHandlerContext#fireExceptionCaught(Throwable) ChannelHandlerContext#fireUserEventTriggered(Object) ChannelHandlerContext#fireChannelWritabilityChanged() ChannelHandlerContext#fireChannelInactive() ChannelHandlerContext#fireChannelUnregistered()outbound事件传播方法:
ChannelHandlerContext#bind(SocketAddress, ChannelPromise)ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)ChannelHandlerContext#write(Object, ChannelPromise)ChannelHandlerContext#flush()ChannelHandlerContext#read()ChannelHandlerContext#disconnect(ChannelPromise)ChannelHandlerContext#close(ChannelPromise)ChannelHandlerContext#deregister(ChannelPromise)下面的示例展示了事件是如何传播的:
public class MyInboundHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext} ctx) { System.out.println("Connected!"); ctx.fireChannelActive(); } }public clas MyOutboundHandler extends ChannelOutboundHandlerAdapter { @Override public void close(ChannelHandlerContext} ctx, ChannelPromise} promise) { System.out.println("Closing .."); ctx.close(promise); } }创建一个pipeline
在pipeline中,一个用户一般由一个或者多个ChannelHandler来接收IO事件(例如读)和IO操作请求(如写或者close) 。例如,一个典型的服务器pipeline通常具有以下几个handler,但最多有多少handler取决于协议和业务逻辑的复杂度:
Protocol Decoder--将二进制数据(如ByteBuffer)转换成一个java对象
Protocol Encoder--将一个java对象转换成二进制数据 。
Business Logic Handler--处理真实的业务逻辑(如数据库访问) 。
让我们用下面的示例展示:
static final EventExecutorGroup group = new DefaultEventExecutorGroup(16); ...ChannelPipeline} pipeline = ch.pipeline();pipeline.addLast("decoder", new MyProtocolDecoder()); pipeline.addLast("encoder", new MyProtocolEncoder());// Tell the pipeline to run MyBusinessLogicHandler's event handler methods // in a different thread than an I/O thread so that the I/O thread is not blocked by // a time-consuming task. // If your business logic is fully asynchronous or finished very quickly, you don't // need to specify a group. pipeline.addLast(group, "handler", new MyBusinessLogicHandler());线程安全
因为ChannelPipeline是线程安全的,一个channelhandler可以在任意时间内增加或者删除 。例如,当有敏感信息交换时,你可以插入一个加密handler,然后当信息交换结束后删除该handler 。
4.3 Channel
Channel是网络socket的一个纽带或者一个处理IO操作如读、写、连接、绑定的组件 。一个Channel提供如下信息:
当前channel的状态,如它是否开启?是否连接?
Channel的ChannelConfig的配置参数,如接受缓存大小;
channel支持的IO操作,如读、写、连接、绑定;
channel支持的ChannelPipeline,它处理所有的IO事件和channel关联的请求 。
所有的IO操作都是异步的 。
在Netty中所有的IO操作都是异步的 。这意味着所有的IO调用将立即返回,但不保证在调用结束时请求的IO操作都已经执行完毕 。而是在请求操作处理完成、失败或者取消时返回一个ChannelFuture来通知 。
Channel是继承性的 。
一个Channel可以它如何创建的来获取它的父Channel(#parent()方法) 。例如:一个由ServerSocketChannel接受的SocketChannel调用parent()方法时返回ServerSocketChannel 。
继承的结构依赖于Channel的所属transport实现 。例如,你可以新写一个Channel实现,它创建了一个共享同一个socket连接的子channel,如BEEP和SSH
向下去获取特定transport操作 。
一些transport会暴露一些该transport特定的操作 。Channel向下转换到子类型可以触发这些操作 。例如:老的IO datargram transport,DatagramChannel提供了多播的join和leave操作 。
推荐阅读
- Redis主从复制机制详解
- 好茉莉茶多少钱斤 好茉莉花茶可以从它的种类来选择
- 招聘|国家体育总局2022事业单位招聘应届毕业生报考情况分析
- 春茶和秋茶的区别
- 什么面料的衣服看起来高档 衣服面料档次从高到低
- 买苹果、华为、OV还是小米?听完手机店老板的分析,谜团解开了
- 绿茶用开水泡 大错特错
- 冬季喝什么茶对身体好?红枣茶蜂蜜菊花茶
- MySQL数据库性能优化之thread pool 原理分析,值得收藏
- 梦见有人从楼上掉下去摔死了满地血 梦见有人从楼上掉下去摔死了周公解梦