What makes ChannelHandler even more powerful is that you can chain them so each ChannelHandler implementation can fulfill small tasks. This helps you write clean and reusable implementations.

clipboard.png
ChannelPipeline和ChannelHandlerContext也实现了ChannelInboundInvoker和ChannelOutboundInvoker借口;Channel接口继承了ChannelOutboundInvoker接口
即,调用ChannelInboundInvoker接口定义的方法,会触发ChannelPipeline中下一个ChannelInboundHandler中对应的回调方法;ChannelOutboundInvoker同理

【P89 ChannelPipeline】
For each new channel, a new ChannelPipeline is created and attached to the channel. Once attached, the coupling between the channel and the ChannelPipeline is permanent.

Modifications on the ChannelPipeline can be done on-the-fly, which means you can add/remove/replace ChannelHandler even from within another ChannelHandler or have it remove itself. This allows writing flexible logic, such as multiplexer, but Ill go into more detail later in this chapter.

Normally each ChannelHandler that is added to the ChannelPipeline will process the event that is passed through it in the IO-Thread, which means you MUST NOT block as otherwise you block the IO-Thread and so affect the overall handling of IO.
Sometimes its needed to block as you may need to use legancy APIs which only offers a blocking API. For example this is true for JDBC. For exactly this use-case Netty allows to pass a EventExecutorGroup to each of the ChannelPipeline.add* methods. If a custom
EventExecutorGroup is passed in the event will be handled by one oft he EventExecutor contained in this EventExecutorGroup and so moved. A default implementation which is called DefaultEventExecutorGroup comes as part of Netty.

【P95 ChannelHandlerContext】
The ChannelHandlerContext never changes for an added ChannelHandler so its safe to get cached.

The ChannelHandlerContext does implement ChannelInboundInvoker and ChannelOutboundInvoker. It has many methods that are also present on the Channel or the ChannelPipeline itself. The difference is that if you call them on the Channel or ChannelPipeline they always flow through the complete ChannelPipeline. In contrast, if you call a method on the ChannelHandlerContext, it starts at the current position and notify the closes ChannelHandler in the ChannelPipeline that can handle the event.

Now if youd like to have the event flow through the whole ChannelPipeline, there are two different ways of doing so:

  • Invoke methods on the Channel.
  • Invoke methods on the ChannelPipeline

You should also notice that the Channel and the
ChannelPipeline are accessible via the ChannelHandlerContext

Event在ChannelPipeline中的传输:
clipboard.png
而对于如下代码:
clipboard.png
过程是这样的:
clipboard.png

You can also use the ChannelHandlerContext from outside, because its thread-safe.

Please note that a ChannelHandler instance can be added to more than one ChannelPipeline if its annotated with the @Sharable. This means that a single ChannelHandler instance can have more than one ChannelHandlerContext, and therefore the single instance can be invoked with a different ChannelHandlerContext.
If you try to add a ChannelHandler to more then one ChannelPipeline that is not annotated with @Sharable an exception is thrown. Be aware that once you annotate a ChannelHandler with @Sharable it must be safe to use from different threads and also be safe to use with different channels (connections) at the same time. Lets look at how it should be used. The following listing shows the correct use of the @Sharable annotation.

【P100 The state model】
Netty has a simple but powerful state model that maps perfectly to the ChannelInboundHandler methods.
即Netty拥有一个简单且强大的状态模型,它们对应着ChannelInboundHandler中的方法:
clipboard.png
clipboard.png
【P101 状态之间的迁移】

【6.4 ChannelHandlers and their types】
Netty uses a well-defined type-hierarchy to represent the different handler types. The parent of all of them is the ChannelHandler. It provides lifecycle operations that are called after a ChannelHandler is added or removed from its ChannelPipeline.
clipboard.png

Netty provides a skeleton implementation for ChannelHandler called ChannelHandlerAdapter. This provides base implementations for all of its methods so you can implement (override) only the methods youre interested in. Basically what it does is forward the event to the next ChannelHandler in the ChannelPipeline until the end is hit.

【6.4.2 Inbound handlers】

  1. ChannelInboundHandler extends ChannelHandler(是接口)
    Every method of ChannelInboundHandler is a counterpart of a method that can be invoked on the ChannelInboundInvoker, which is extended by ChannelHandlerContext and ChannelPipeline.
    Netty provides a skeleton implementation for ChannelInboundHandler implementations called ChannelInboundHandlerAdapter. This provides base implementations for all of its methods and allows you to implement (override) only the methods youre interested in. All of these methods implementations, by default, forward the event to the next ChannelInboundHandler in the ChannelPipeline by calling the same method on the ChannelHandlerContext.
    Important to note is that the ChannelInboundHandler which handles received messages and so @Override the channelRead(…) method is responsible to release resources. This is especially important as Netty uses pooled resources for ByteBuf and so if you forgot to release the resource you will end up with a resource leak. Fortunately Netty will log resources which was missed to be released with WARN loglevel, so it should be quite easy for you to figure out when you missed to do so.
    clipboard.png
    clipboard.png
    As releasing the resources by hand can be cumbersome there is also SimpleChannelInboundHandler, which will take care of it for you. This way you will not need to care about it at all. The only important thing here is to remember that if you use SimpleChannelInboundHandler, it will release the message once it was processed and so you MUST NOT store a reference to it for later usage:
    clipboard.png
    Often you may want to decode bytes to a custom message type, and you may be tempted to implement either ChannelInboundHandler or extend ChannelInboundhandlerAdapter. But hold on a second, theres an even better solution to this problem. Those kinds of functionality are easily implemented using the codec framework, which Ill explain later in the chapter.
    If you use ChannelInboundHandler, ChannelInboundHandlerAdapter or SimpleChannelInboundhandler depends on your needs. Most of the times you use SimpleChannelInboundHandler in the case of handling messages and ChannelInboundHandlerAdapter for other inbound events / state changes.
    【P106 Inbound Message handling and reference counting】
    因为Netty使用引用计数来管理ByteBuf,从而实现ByteBuf池来减少内存的allocation和release,所以使用ChannelInboundHandlerAdapter时,如果channelRead(ctx, msg)所在的Handler是最后一个处理这个msg的Handler(即不会调用ctx.fireChannelRead(msg)),则必须在方法返回之前release这个msg(如果msg实现了ReferenceCounted接口);如果使用的是SimpleChannelInboundhandler,在从channelRead(ctx, msg)返回前(执行后续Handler之后)会自动release这个msg(见源码,在finally子句中)
  2. ChannelInitializer
    It does exactly what its name states. It allows you to init the Channel once its registered with its EventLoop and ready to process I/O. The ChannelInitializer is mainly used to set up the ChannelPipeline for each Channel that is created. This is done as part of the bootstrapping

【6.4.3 Outbound handlers】

  1. ChannelOutboundHandler
    The ChannelOutboundHandler provides methods that are called when outbound operations are requested. These are the methods listed in the ChannelOutboundInvoker interface thats extended by Channel, ChannelPipeline, and ChannelHandlerContext.
    Allmost all of the methods take a ChannelPromise as an argument that MUST be notified once the request should stop to get forwarded through the ChannelPipeline.
    Netty provides a skeleton implementation for ChannelOutboundHandler implementations called ChannelOutboundHandlerAdapter. This provides base
    implementations for all of its methods and allows you to implement (override) only the methods youre interested in. All these method implementations, by default, forward the event to the next ChannelOutboundHandler in the ChannelPipeline by calling the same method on the ChannelHandlerContext.
    Its important to remember to release resources and notify the ChannelPromise. If the ChannelPromise is not notified it may lead to situations where a ChannelFutureListener is not notified about a handled message.
    如果msg已经被当前Handler处理且不需要在ChannelPipeline中继续传递,应该及时release,否则到达ChannelPipeline的最前端(到达Transport)会被自动release

bedew
278 声望3 粉丝