什么样的网站需要数据库,淘宝官方网站主页,机械加工网厂,wordpress安卓显示图片一、前言 前面学习了Netty的ByteBuf#xff0c;接着学习ChannelHandler和ChannelPipeline。 二、ChannelHandler和ChannelPipeline 2.1 ChannelHandler 在ChannelPipeline中#xff0c;ChannelHandler可以被链在一起处理用户逻辑。 1. Channel生命周期 Channel接口定义了一个…一、前言 前面学习了Netty的ByteBuf接着学习ChannelHandler和ChannelPipeline。 二、ChannelHandler和ChannelPipeline 2.1 ChannelHandler 在ChannelPipeline中ChannelHandler可以被链在一起处理用户逻辑。 1. Channel生命周期 Channel接口定义了一个简单但是强大的状态模型该模型与ChannelInboundHandler API紧密联系Channel有如下四种状态。 Channel的生命周期如下图所示。 当状态发生变化时就会产生相应的事件。 2. ChannelHandler的生命周期 ChannelHandler定义的生命周期如下图所示。 Netty定义了ChannelHandler的两个重要的子类 · ChannelInboundHandler处理各种入站的数据和状态的变化。 · ChannelOutboundHandler处理出站数据并允许拦截的所有操作。 3. ChannelInboundHandler接口 下图展示了ChannelInboundHandler接口生命周期中的方法当接受到数据或者其对应的Channel的状态发生变化则会调用方法 当ChannelInboundHandler的实现覆盖channelRead()方法时它负责显式释放与池的ByteBuf实例相关联的内存可以使用ReferenceCountUtil.release() 方法进行释放。如下代码展示了该方法的使用。 public class DiscardHandler extends ChannelInboundHandlerAdapter {Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ReferenceCountUtil.release(msg);}
} 上述显式的释放内存空间会显得比较麻烦而如下代码则无需显式释放内存空间。 Sharable
public class SimpleDiscardHandlerextends SimpleChannelInboundHandlerObject {Overridepublic void channelRead0(ChannelHandlerContext ctx,Object msg) {// No need to do anything special}
} 上述代码中SimpleChannelInboundHandler会自动释放资源因此无需显式释放。 4. ChannelOutboundHandler接口 ChannelOutboundHandler处理出站操作和数据它的方法会被Channel、ChannelPipeline、ChannelHandlerContext触发。ChannelOutboundHandler可按需推迟操作或事件。例如对远程主机的写入被暂停你可以延迟刷新操作并在稍后重启。 5. ChannelHandler适配器 可以使用ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter类作为自己的ChannelHandler程序的起点这些适配器提供了ChannelInboundHandler和ChannelOutboundHandler的简单实现它们继承了共同父类接口ChannelHandler的方法其继承结构如下图所示 ChannelHandlerAdapter还提供了isSharable方法如果有Sharable注释则会返回true这也表明它可以被添加至多个ChannelPipiline中。ChannelInboundHandlerAdapter and ChannelOutboundHandlerAdapter的方法体中会调用ChannelHandlerContext对应的方法因此可以将事件传递到管道的下个ChannelHandler中。 6. 资源管理 无论何时调用ChannelInboundHandler.channelRead()和ChannelOutboundHandler.write()方法都需要保证没有资源泄露由于Netty使用引用计数来管理ByteBuf因此当使用完ByteBuf后需要调整引用计数。 为了诊断可能出现的问题Netty提供了ResourceLeakDetector它将抽取应用程序大约1的缓冲区分配来检查内存泄漏其额外的开销很小内存检测有如下四种级别 可以通过java -Dio.netty.leakDetectionLevelADVANCED 设置内存检测级别。 当读取数据时可以在readChannel方法中调用ReferenceCountUtil.release(msg)方法释放资源或者实现SimpleChannelInboundHandler会自动释放资源而当写入数据时可以在write方法中调用ReferenceCountUtil.release(msg)释放资源具体代码如下 Sharable
public class DiscardOutboundHandlerextends ChannelOutboundHandlerAdapter {Overridepublic void write(ChannelHandlerContext ctx,Object msg, ChannelPromise promise) {ReferenceCountUtil.release(msg);promise.setSuccess();}
} 不仅需要释放资源并且需要通知ChannelPromise否则ChannelFutureListener可能收不到事件已经被处理的通知。如果消息到达实际的传输层就可以在写操作完成或者关闭通道时会自动释放资源。 2.2 ChannelPipeline接口 如果将ChannelPipeline视为ChannelHandler实例链可拦截流经通道的入站和出站事件即可明白ChannelHandler之间的交互是如何构成应用程序数据和事件处理逻辑的核心的。当创建一个新的Channel时都会分配了一个新的ChannelPipeline该关联是永久的该通道既不能附加另一个ChannelPipeline也不能分离当前的ChannelPipeline。 一个事件要么被ChannelInboundHander处理要么被ChannelOutboundHandler处理随后它将通过调用ChannelHandlerContext的实现来将事件转发至同一超类型的下一个处理器。ChannelHandlerContext允许ChannelHandler与其ChannelPipeline和其他ChannelHandler进行交互一个处理器可以通知ChannelPipeline中的下一个处理器甚至可以修改器隶属于的ChannelPipeline。 下图展示了ChannelHandlerPipeline、ChannelInboundHandler和ChannelOutboundHandler之间的关系 可以看到ChannelPipeline是由一系列ChannelHandlers组成其还提供了通过自身传播事件的方法当进站事件触发时其从ChannelPipeline的头部传递到尾部而出站事件会从右边传递到左边。 当管道传播事件时其会确定下一个ChannelHandler的类型是否与移动方向匹配若不匹配则会跳过并寻找下一个直至找到相匹配的ChannelHandler一个处理器可以会同时实现ChannelInboundHandler和ChannelOutboundHandler。 1. 修改ChannelPipeline ChannelHandler可实时修改ChannelPipeline的布局如添加、删除、替换其他ChannelHandler其可从ChannelPipeline中移除自身如如下图所示的方法。 通常ChannelPipeline中的每个ChannelHandler通过其EventLoopI / O线程处理传递给它的事件不要阻塞该线程因为它会对I/O的整体处理产生负面影响。 2.3 ChannelHandlerContext接口 ChannelHandlerContext代表了ChannelHandler与ChannelPipeline之间的关联当ChannelHandler被添加至ChannelPipeline中时其被创建ChannelHandlerContext的主要功能是管理相关ChannelHandler与同一ChannelPipeline中的其他ChannelHandler的交互。 ChannelHandlerContext中存在很多方法其中一些也存在于ChannelHandler和ChannelPipeline中但是差别很大。如果在ChannelHandler或者ChannelPipeline中调用该方法它们将在整个管道中传播而如果在ChannelHandlerContext中调用方法那么会仅仅传递至下个能处理该事件的ChannelHandler。 1. 使用ChannelHandlerContext ChannelHandlerContext、ChannelHandler、ChannelHandlerContext、Channel之间的关系如下图所示 可以通过ChannelHandlerContext来访问Channel并且当调用Channel的write方法时写事件会在管道中传递代码如下 ChannelHandlerContext ctx ..;
Channel channel ctx.channel();
channel.write(Unpooled.copiedBuffer(Netty in Action,
CharsetUtil.UTF_8)); 除了使用Channel的write方法写入数据外还可以使用ChannelPipeline的write方法写入数据代码如下 ChannelHandlerContext ctx ..;
ChannelPipeline pipeline ctx.pipeline();
pipeline.write(Unpooled.copiedBuffer(Netty in Action,
CharsetUtil.UTF_8)); 上述两段代码在管道中产生的效果相同如下图所示。 其中两种方法的写事件都会通过ChannelHandlerContext在管道中传播。 若想从指定的ChannelHandler开始传递事件那么需要引用到指定ChannelHandler之前的一个ChannelHandlerContext该ChannelHandlerContext将调用其关联的ChannelHandler。 如下图所示展示了从指定ChannelHandler开始处理事件。 2. ChannelHandler和ChannelHandlerContext的高级用法 可以通过调用ChannelHandlerContext的pipeline方法获得其对应的ChannelPipeline引用这可以在运行中管理ChannelHandler如添加一个ChannelHandler。 另一种高级用法是缓存ChannelHandlerContext的引用以供之后使用。如下代码展示了用法 public class WriteHandler extends ChannelHandlerAdapter {private ChannelHandlerContext ctx;Overridepublic void handlerAdded(ChannelHandlerContext ctx) {this.ctx ctx;}public void send(String msg) {ctx.writeAndFlush(msg);}
} 因为ChannelHandler可以属于多个ChannelPipeline所以它可以绑定到多个ChannelHandlerContext实例当使用时必须使用Sharable注释否则当将其添加至多个ChannelPipeline时会抛出异常。如下代码所示 Sharable
public class SharableHandler extends ChannelInboundHandlerAdapter {Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {System.out.println(Channel read message: msg);ctx.fireChannelRead(msg);}
} Sharable注释没有任何状态而如下代码会出现错误。 Sharable
public class UnsharableHandler extends ChannelInboundHandlerAdapter {private int count;Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {count;System.out.println(channelRead(...) called the count time);ctx.fireChannelRead(msg();}
} 由于上述代码包含了状态即count计数将此类的实例添加到ChannelPipeline时在并发访问通道时很可能会产生错误。可通过在channelRead方法中进行同步来避免错误。 2.4 异常处理 在出站和进站时可能会发生异常Netty提供了多种方法处理异常。 1. 处理进站异常 当处理进站事件时发生异常它将从ChannelInboundHandler中被触发的位置开始流过ChannelPipeline为处理异常需要在实现ChannelInboundHandler接口是重写exceptionCaught方法。如下示例所示。 public class InboundExceptionHandler extends ChannelInboundHandlerAdapter {Overridepublic void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) {cause.printStackTrace();ctx.close();}
} 由于异常会随着进站事件在管道中传递包含异常处理的ChannelHandler通常放在了管道的尾部因此可以保证无论异常发生在哪个ChannelHandler中其最终都会被处理。 2. 处理出站异常 在出站操作中处理的正常完成和处理异常都基于以下通知机制。 · 每个出站操作返回一个ChannelFuture在ChannelFuture注册的ChannelFutureListeners在操作完成时通知成功或错误。 · ChannelOutboundHandler的几乎所有方法都会传递ChannelPromise实例ChannelPromise是ChannelFuture的子类其也可以为异步通知分配监听器并ChannelPromise还提供可写的方法来提供即时通知。可通过调用ChannelFuture实例的addListener(ChannelFutureListener)方法添加一个ChannelFutureListener最常用的是调用出站操作所返回的ChannelFuture的addListener方法如write方法具体代码如下所示。 ChannelFuture future channel.write(someMessage);
future.addListener(new ChannelFutureListener() {Overridepublic void operationComplete(ChannelFuture f) {if (!f.isSuccess()) {f.cause().printStackTrace();f.channel().close();}}
}); 第二种方法是将ChannelFutureListener添加到ChannelPromise中并将其作为参数传递给ChannelOutboundHandler的方法具体代码如下所示 public class OutboundExceptionHandler extends ChannelOutboundHandlerAdapter {Overridepublic void write(ChannelHandlerContext ctx, Object msg,ChannelPromise promise) {promise.addListener(new ChannelFutureListener() {Overridepublic void operationComplete(ChannelFuture f) {if (!f.isSuccess()) {f.cause().printStackTrace();f.channel().close();}}});}
} 三、总结 本篇博文讲解了ChannelHandler以及其与ChannelPipeline、ChannelHandlerContext之间的关系及其之间的交互同时还了解了如何处理进站与出站时所抛出的异常。谢谢各位园友的观看~转载于:https://www.cnblogs.com/leesf456/p/6901189.html