从netty-example分析Netty组件( 二 )


childHandler(ChannelHandler childHandler)方法,设置用来处理请求的channelHandler 。
3. ChannelInitializer一种特殊的ChannelInboundHandler
当Channel注册到它的eventLoop中时,ChannelInitializer提供了一个方便初始化channel的方法 。该类的实现通常用来设置ChannelPipeline的channel,通常使用在Bootstrap#handler(ChannelHandler),ServerBootstrap#handler(ChannelHandler)和ServerBootstrap#childHandler(ChannelHandler)三个场景中 。示例:
public class MyChannelInitializer extends ChannelInitializer{ public void initChannel({@link Channel} channel) { channel.pipeline().addLast("myHandler", new MyHandler()); } }然后:
ServerBootstrap bootstrap = ...;...bootstrap.childHandler(new MyChannelInitializer());注意:这个类标示为可共享的,因此实现类重用时需要时安全的 。
4. ChannelPipeline相关
理解ChannelPipeline需要先理解ChannelHandler,
4.1 ChannelHandler
处理一个IO事件或者翻译一个IO操作,并且传递给ChannelPineline的下一个handler 。
你可以使用ChannelHandlerAdapter来替代它
因为这个接口有太多接口需要实现,因此你只有实现ChannelHandlerAdapter就可以代替实现这个接口 。
Context对象
ChannelHandlerContext封装了ChannelHandler 。ChannelHandler应该通过context对象与它所属的ChannelPipeLine进行交互 。通过使用context对象,ChannelHandler可以传递上行或者下行事件,或者动态修改pipeline,或者存储特定handler的信息(使用AttributeKey) 。
状态管理
一个channelHandler通常需要存储一些状态信息 。最简单最值得推荐的方法是使用member变量:
public interface Message { // your methods here }public class DataServerHandler extends SimpleChannelInboundHandler<Message> {private boolean loggedIn;{@code @Override} protected void messageReceived( ChannelHandlerContext ctx, Message message) { Channel ch = e.getChannel(); if (message instanceof LoginMessage) { authenticate((LoginMessage) message); loggedIn = true; } else (message instanceof GetDataMessage) { if (loggedIn) { ch.write(fetchSecret((GetDataMessage) message)); } else { fail(); } } } ... }注意:handler的状态附在ChannePipelineContext上,因此可以增加相同的handler实例到不同的pipeline上:
public class DataServerInitializer extends ChannelInitializer<Channel> {private static final DataServerHandler SHARED = new DataServerHandler();@Override public void initChannel(Channel channel) { channel.pipeline().addLast("handler", SHARED); } }@Sharable注解
在上面的示例中,使用了一个AttributeKey,你可能注意到了@Sharable注解 。
如果一个ChannelHandler使用@sharable进行注解,那就意味着你仅仅创建了一个handler一次,可以添加到一个或者多个ChannelPipeline多次而不会产生竞争 。
如果没有指定该注解,你必须每次都创建一个新的handler实例,并且增加到一个ChannelPipeline,因为它没有像member变量一样,它有一个非共享的状态 。
4.2 ChannelPipeline
ChanelPipeline是一组ChanelHandler的集合,它处理或者解析Channel的Inbound事件和OutBound操作 。ChannelPipeline的实现是Intercepting Filter的一种高级形式,这样用户可以控制事件如何处理,一个pipeline内部ChannelHandler如何交互 。
 pipeline事件流程

从netty-example分析Netty组件

文章插图
 
上图描述了IO事件如何被一个ChannelPipeline的ChannelHandler处理的 。一个IO事件被一个ChannelInBoundHandler处理或者ChannelOutboundHandler,然后通过调用在ChannelHandlerContext中定义的事件传播方法传递给最近的handler,传播方法有ChannelHandlerContext#filreChannelRead(Object)和ChannelHandlerContext#write(Object) 。
一个Inbound事件通常由Inbound handler来处理,如上如左上 。一个Inbound handler通常处理在上图底部IO线程产生的Inbound数据 。Inbound数据通过真实的输入操作如SocketChannel#read(ByteBuffer)来获取 。如果一个inbound事件越过了最上面的inbound handler,该事件将会被抛弃到而不会通知你或者如果你需要关注,打印出日志 。
一个outbound事件由上图的右下的outbound handler来处理 。一个outbound handler通常由outbound流量如写请求产生或者转变的 。如果一个outbound事件越过了底部的outbound handler,它将由channel关联的IO线程处理 。IO线程通常运行的是真实的输出操作如SocketChannel#write(byteBuffer).
示例,假设我们创建下面这样一个pipeline:
ChannelPipeline} p = ...; p.addLast("1", new InboundHandlerA()); p.addLast("2", new InboundHandlerB()); p.addLast("3", new OutboundHandlerA()); p.addLast("4", new OutboundHandlerB()); p.addLast("5", new InboundOutboundHandlerX());


推荐阅读