上下文对象使得当前channelhandler能够与其所属的channelpipeline以及其余handler进行交互,能够通知所属channelpipeline中的下一个handler,也可动态修改其所属的channelpipeline,具体功能以下:html
通知。经过调用channelhandlercontext提供的方法能够调用同一个channelpipeline中的相邻的下一个channelhandler,详情能够参照channelpipeline来了解事件使如何流动的。java
修改pipeline 经过调用context的pipeline()
方法能够获得当前handler所属的channelpipeline。能够在程序运行时动态增长、删除、替换channelpipeline中的handler。数据库
保持供后面使用 你能够将context对象存储在handler中,共后面使用。好比在handler提供的方法以外触发一个事件,甚至在不一样的线程中均可以。实例代码以下:api
public class MyHandler extends ChannelHandlerAdapter { private ChannelHandlerContext ctx;//成员变量 public void beforeAdd(ChannelHandlerContext ctx) { this.ctx = ctx;//此处保持该ctx } public void login(String username, password) {//在自定义方法中触发事件 ctx.write(new LoginMessage(username, password)); } ... }
存储状态信息 AttributeMap.attr(AttributeKey)
方法容许你存储和访问与handler和context相关的状态信息。promise
一个handler能够有多个context 因为同一个handler实例能够被添加到多个pipeline中,这也就意味着一个handler能够有一个或多个context对象,所以同一个handler实例若是被添加到一个或多个pipeline一次或者屡次,当其handle方法被调用的时候,传进来的参数context是不一样的。一个handler被添加到pipeline多少次,他就有多少各context对象,不是见到同一个pipeline仍是不一样的pipeline,示例代码以下:安全
@Sharable public class FactorialHandler extends ChannelHandlerAdapter { private final AttributeKey<Integer> counter = AttributeKey.valueOf("counter"); // This handler will receive a sequence of increasing integers starting // from 1. @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Attribute<Integer> attr = ctx.getAttr(counter); Integer a = ctx.getAttr(counter).get(); if (a == null) { a = 1; } attr.set(a * (Integer) msg); } } // Different context objects are given to "f1", "f2", "f3", and "f4" even if // they refer to the same handler instance. Because the FactorialHandler // stores its state in a context object (using an AttributeKey), the factorial is // calculated correctly 4 times once the two pipelines (p1 and p2) are active. FactorialHandler fh = new FactorialHandler(); ChannelPipeline p1 = Channels.pipeline(); p1.addLast("f1", fh);//一个context p1.addLast("f2", fh); //二个context ChannelPipeline p2 = Channels.pipeline(); p2.addLast("f3", fh);//三个context p2.addLast("f4", fh);//四个context
此接口调用ChannelHandler
的事件处理方法,若是此接口的默认实现类不知足要求,用户能够指定一个ChannelHandlerInvoker实现一个自定义线程模型,注意这个接口的方法不是给用户调用的。async
表示一个channel全局惟一的标识符。标识符是根据下列资源产生的:MAC地址,当前进程ID,System.currentTimeMillis()
,System.nanoTime()
,一个随机的32-bit Integer和一个顺序增加的32-bit整数。标识符的全局惟一性有赖于MAC地址和进程ID,系统会在类加载的时候自动检测获取,若是这两个获取失败,系统会记录警告信息,同时将使用随机数代替做为标识符,另外,你能够经过系统属性人工指定这两个参数:io.netty.machineId
48bit或者64bit十六进制表示的整数,能够经过冒号后者连字符分割,io.netty.processId
0到65535之间的一个整数。
ide
channelpipeline由一系列channelhandler组成,用来处理channel的inbound events 和 outbound operations。channelpipeline实现了拦截过滤器模式(Intercepting Filter pattern)的一种高级形式。以使用户可以彻底控制一个时间(event)怎样被处理,以及channelpipeline中的channelhandler之间如何交互。oop
pipeline的建立 每个channel都一个本身的pipeline,channel建立的时候pipeline自动建立ui
事件(event)如何在pipeline中流动 下图展现了一个IO事件被channelpipeline中的channelhandler处理的典型方式,一个IO事件被当前handler处理以后,将被转发到当前handler在channelpipeline中的紧邻的下一个handler处理。若是有必要的话,channelhandler自己也能够触发任意的IO事件。为了触发或者转发IO事件,channelhandler须要调用channelhandlercontext提供的事件传播方法,例如ChannelHandlerContext.fireChannelRead(Object)
和 ChannelHandlerContext.write(Object)
.
I/O Request via Channel or ChannelHandlerContext | +---------------------------------------------------+---------------+ | ChannelPipeline | | | \|/ | | +----------------------------------------------+----------+ | | | ChannelHandler N | | | +----------+-----------------------------------+----------+ | | /|\ | | | | \|/ | | +----------+-----------------------------------+----------+ | | | ChannelHandler N-1 | | | +----------+-----------------------------------+----------+ | | /|\ . | | . . | | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()| | [method call] [method call] | | . . | | . \|/ | | +----------+-----------------------------------+----------+ | | | ChannelHandler 2 | | | +----------+-----------------------------------+----------+ | | /|\ | | | | \|/ | | +----------+-----------------------------------+----------+ | | | ChannelHandler 1 | | | +----------+-----------------------------------+----------+ | | /|\ | | +---------------+-----------------------------------+---------------+ | \|/ +---------------+-----------------------------------+---------------+ | | | | | [ Socket.read() ] [ Socket.write() ] | | | | Netty Internal I/O Threads (Transport Implementation) | +-------------------------------------------------------------------+
如上图左边所示,一个inbound事件自底向上的被channelhandler处理,一个inbound事件一般是由IO线程触发(图底部),以便在channel状态发生变化(例如新建连接或者关闭链接)或者从远端读取数据的时候通知channelhandler。若是inbound事件超过了图中最顶端的channelhandler,该事件会被丢弃,并被记录,取决于日志级别。
如上图右边所示,一个outbound事件自顶向下的被channelhandler处理,一个outbound事件一般是由用户的代码调用IO操做触发,例如写请求或者尝试进行连接。若是一个outbound事件超过了最底端的channelhandler,改事件会被与channel关联的IO线程处理,IO线程一般状况下执行实际的输出操做,好比SocketChannel.write(ByteBuffer)
.
转发事件给下一个handler 就像上面解释的那样,channelhandler经过调用channelhandlercontext的事件传播方法将事件转发给下一个channelhandler,具体方式以下:
Inbound event propagation methods:
Outbound event propagation methods:
下面的代码展现了事件传播的经常使用作法:
public class MyInboundHandler extends ChannelHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { System.out.println("Connected!"); ctx.fireChannelActive();//传播事件 } } public clas MyOutboundHandler extends ChannelHandlerAdapter { @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) { System.out.println("Closing .."); ctx.close(promise);//传播事件 } }
构建pipeline 用户在pipeline中应该有一个或者多个channelhandler来接收IO事件(例如 read)或者请求IO操做(例如write close)。例如,一个典型的server在每个channelpipeline中一般有以下channelhandler,可是具体细节可能不同,这取决于你的协议和业务逻辑的特性和复杂度:
Protocol Decoder - translates binary data (e.g. ByteBuf
) into a Java object.解码,将二进制数据转换成java对象
Protocol Encoder - translates a Java object into binary data. 编码,讲java对象转换成二进制数据
Business Logic Handler - performs the actual business logic (e.g. database access).业务逻辑处理(例如数据库存储)
组装pipeline的示例代码以下:
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.若是业务逻辑是一个耗时任务或者会阻塞,请不要放在IO线程执行。 pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
线程安全 channelpipeline中的channelhandler能够在任什么时候候添加删除,由于channelpipeline是线程安全的。例如你能够在发送敏感信息的时候添加一个加密的handler,在敏感信息发送以后删除这个handler。
此接口表示一个特殊的channelfuture对象,用来展现FileRegion
transfer 的进度
An EventListener listener which will be called once the sending task associated with future is being transferred.
Special ChannelPromise
which will be notified once the associated bytes is transferring.
Special ChannelFuture
which is writable.
一旦channel注册以后,该接口处理全部关于该channel的IO操做,一个EventLoop一般状况下处理一个或多个channel,可是这取决于内部实现细节
他容许注册channel,并在后面eventloop中处理channel
表示经过channel发送的一个文件区块,能够支持zero-copy file transfer.
此接口负责计消息的大小,也就是消息在内存中占多大空间。
负责建立一个接收缓冲区,其大小适中,可以存储inbound data 又不浪费空间
负责接接入的连接请求,并建立一个子channel来接收连接,具体实现能够参照ServerSocketChannel