《Netty in Action》chapter 6 : ChannelHandler and ChannelPipeline

来源:互联网 发布:win2008端口转发 编辑:程序博客网 时间:2024/05/16 09:24

6.1 The ChannelHandler family

6.1.1 The Channel lifecycle

Channel lifecycle states

State Description ChannelUnregistered Channel被创建但没有注册到EventLoop中 ChannelRegistered Channel被注册到EventLoop时 ChannelActive Channel被激活,此时可以接收和发送数据 ChaanlInactive Channel没有连接到remote peer

这里写图片描述


6.1.2 The ChannelHandler lifecycle

ChannelHandler lifecycle methods

Type Description handlerAdded 添加到ChannelPipeline时调用 handlerRemoved 从ChannelPipeline 删除时调用 exceptionCaught 处理时如果ChannelPipeline中有错误时调用

6.1.3 Interface ChannelInboundHandler

这里写图片描述

ChannelInboundHandler负责明确地释放ByteBuf实例池相关的内存。可以是用netty提供的ReferenceCountUtil.release()
例如:

@Sharablepublic class DiscardHandler extends ChannelInboundHandlerAdapter {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) {        ReferenceCountUtil.release(msg);    }}

SimpleChannelInboundHandler会自动调用ReferenceCountUtil.release(msg),故不用再去手动释放资源。


6.1.4 Interface ChannelOutboundHandler

这里写图片描述
这里写图片描述


6.1.5 ChannelHandler adapters

ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter针对ChannelInboundHandlerChannelOutboundHandler各自简单的实现,可以直接调用并override自己感兴趣的方法。
这里写图片描述


6.1.6 Resource management

当调用ChannelInboundHandler.channelRead(),ChannelOutboundHandler.write()可能会有内存泄漏,可以使用netty自带的 Class ResourceLeakDetector来进行内存泄露检查。检查级别如下:
这里写图片描述
使用方法:
java -Dio.netty.leakDetectionLevel=ADVANCED


6.2 Interface ChannelPipeline

ChannelPipeline 是一系列的ChannelHandler。ChannelPipeline还提供了一下传播event穿过它自己的方法。
这里写图片描述
I/O线程是不允许被阻塞的,也就是不能在ChannelHandler中进行任何阻塞式的处理,但是对此我们也有相应的解决方法.就是在把ChannelHanders添加到ChannelPipeline的时候,指定一个EventExecutorGroup,ChannelHandler中所有的方法都将会在这个指定的EventExecutorGroup中运行。而这个EVentExecutorGroup运行的线程与I/O线程不同,达到不阻塞I/O的目的。
这里写图片描述

这里写图片描述

这里写图片描述

Table 6.10 The ChannelHandlerContext API

Method name Description bind Binds to the given SocketAddress and returns a ChannelFuture channel Returns the Channel that is bound to this instance close Closes the Channel and returns a ChannelFuture connect Connects to the given SocketAddress and returns a ChannelFuture deregister Deregisters from the previously assigned EventExecutor and returns a ChannelFuture disconnect Disconnects from the remote peer and returns a ChannelFuture executor Returns the EventExecutor that dispatches events fireChannelActive Triggers a call to channelActive() (connected) on the nextChannelInboundHandler fireChannelInactive Triggers a call to channelInactive() (closed) on the next ChannelInboundHandler fireChannelRead Triggers a call to channelRead() (message received) on the next ChannelInboundHandler fireChannelReadComplete Triggers a channelWritabilityChanged event to the next ChannelInboundHandler handler Returns the ChannelHandler bound to this instance isRemoved Returns true if the associated ChannelHandler was removed from the ChannelPipeline name Returns the unique name of this instance pipeline Returns the associated ChannelPipeline read Reads data from the Channel into the first inbound buffer; triggers a channelRead event if successful and notifies the handler of channelReadComplete write Writes a message via this instance through the pipeline

6.3.1 Using ChannelHandlerContext

这里写图片描述

Listing 6.6 Accessing the Channel from a ChannelHandlerContext

ChannelHandlerContext ctx = ..;Channel channel = ctx.channel();channel.write(Unpooled.copiedBuffer("Netty in Action",CharsetUtil.UTF_8));

Listing 6.7 Accessing the ChannelPipeline from a ChannelHandlerContext

ChannelHandlerContext ctx = ..;ChannelPipeline pipeline = ctx.pipeline();pipeline.write(Unpooled.copiedBuffer("Netty in Action",CharsetUtil.UTF_8));

6.6和6.7是完全等效的。另外,write相当于一个event,从它被添加到ChannelHandlerContext时起,会遍历pipeline从头到尾的所有环节。
这里写图片描述
应该从ChannelPipeline中某个合适的点加入event,原因如下:
为了减少even在pipeline中对该event不该兴趣的ChannelHandler中传输的开销;
为了避免对该event感兴趣的ChannelHandler对该event进行处理。
如下面的例子:
Listing 6.8 Calling ChannelHandlerContext write()

ChannelHandlerContext ctx = ..;ctx.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8));

这个例子中,event是从下一个ChannelHandler进行处理,而不是从头到尾。
这里写图片描述


6.3.2 Advanced uses of ChannlHandler and ChannelHandlerContext

高级用法之一,从ChannlHandler中通过ChannelHandlerContext获取pipeline,然后动态地往pipeline中添加ChannlHander以完成各种复杂的需求,比如通过网pipeline中添加ChannlHandler支持动态协议变化。
高级用法之二,在ChannelHandler中缓存ChannelHandlerContext以供后用。而且可以在不同线程中执行。
Listing 6.9 Caching a ChannelHandlerContext

public class WriteHandler extends ChannelHandlerAdapter {    private ChannelHandlerContext ctx;    @Override    public void handlerAdded(ChannelHandlerContext ctx) {        this.ctx = ctx;    }    public void send(String msg) {        ctx.writeAndFlush(msg);    }}

由于一个ChannelHandler可以绑定到不同的ChannelPipeline,所以它可以绑定不同的ChannelHandlerContext实例。这种情况,必须注解为@Shareable,否则会报异常。而且此时ChannelHandler必须为线程安全的。
Listing 6.10 A sharable ChannelHandler

@Sharablepublic class SharableHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {    //Log method calls and forwards to next ChannelHandler    System.out.println("Channel read message: " + msg);    ctx.fireChannelRead(msg);    }}

Listing 6.11 Invalid usage of @Sharable

@Sharablepublic class UnsharableHandler extends ChannelInboundHandlerAdapter {    private int count;    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) {        //有自己的状态,非线程安全(可是使用synchronized解决)        count++;        System.out.println("channelRead(...) called the " + count + " time");        ctx.fireChannelRead(msg();    }}

注:这种方式可以用于统计多个pipeline中消息数量。


6.4 Exception handling

6.4.1 Handling inbound exceptions

Listing 6.12 Basic inbound exception handling

public class InboundExceptionHandler extends ChannelInboundHandlerAdapter {@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {    cause.printStackTrace();    ctx.close();    }}

总结:
1. ChannelHandler.exceptionCaught()默认实现是将当前的异常传到pipeline中的下一个;
2. 如果到pipeline的最后没有处理,netty会打异常未处理日志;
3. 如果要自己处理异常,可以override exceptionCaught()


6.4.2 Handling outbound exceptions

两种方式:
Listing 6.13 Adding a ChannelFutureListener to a ChannelFuture

ChannelFuture future = channel.write(someMessage);future.addListener(new ChannelFutureListener() {    @Override    public void operationComplete(ChannelFuture f) {        if (!f.isSuccess()) {            f.cause().printStackTrace();            f.channel().close();        }    }});

Listing 6.14 Adding a ChannelFutureListener to a ChannelPromise

public class OutboundExceptionHandler extends ChannelOutboundHandlerAdapter {    @Override    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {        promise.addListener(new ChannelFutureListener() {            @Override            public void operationComplete(ChannelFuture f) {                if (!f.isSuccess()) {                    f.cause().printStackTrace();                    f.channel().close();                }            }        });    }}
阅读全文
0 0
原创粉丝点击