netty的一点学习笔记
主要参考在这篇文章,写的非常好.
netty主要是一个异步的nioserver/clientframework
它采用了eventdriven模式
netty主要针对异步(asynchronous)请求的处理
nettychannel可以理解为传输请求的另外一种抽象,是netty最核心的概念,它既可以表示一个socket,也可以表示一个file,总之就是I/O的一个抽象.
channel提供了一个网络连接和向目标对象写数据的接口,而对于异步方式来说,没有读的概念(通过listener来监听返回数据),Channel的返回结果会放在ChannelFuture中
netty不仅实现异步,也有同步实现,不过是内部也是采用的future机制
channel分server和client两种
channel由ChannelFactory创建,它需要提供两个线程池:boss线程池,worker线程池,boss线程池用来监听连接,将接受到的数据传给worker线程池处理,一般只会有一个bossthread.
workerthread应该注意的一点是千万别被block和deadlock,否则会导致整个吞吐量上不去.尽量保证workerthread简单专一,剥离无关内容,能快速完成处理.
bossthread只有一个,但是为什么要用threadpool?
1.可能有多个channelfactory,这些factory可以共用一个bossthreadpool来创建bossthread.
2.bossthread可以中途进行释放,为了避免重复create的开销,可以用一个小型的threadpool来管理
所有资源的分配都是通过channelfactory来完成,因此如果需要释放资源,需要调用realseExternalResources()
channel的使用三部曲:
- 创建
- 监听socket
- 调用write(Objectmessage)接受数据.不过这里只能接受ChannelBuffer类型的数据.
这里需要提到Channel另外一个组成部分,ChannelPipeline.它主要完成对传入的内容的解析处理.为了保证这个解析的灵活性,或包含一系列的,各种类型的解析器,这些解析器最后将以ChannelHandler的方式进行组织.ChannelPipeline则包含多种ChannelHandler,一般第一个ChannelHandler接受ChannelBuffer,最后一个ChannelHandler(通常命名为sink)用来去掉payload信息.ChannelHandler是一个标志接口,灵活性非常强,它一般会通过接受ChannelEvent(内含ChannelBuffer)来完成对数据的处理.
ChannelHandler主要包含两类:Encoder(将数据流转换成ChannelBuffer),Decoder
除了编解码之外,还有一些其他类型的channelHandler,比如将channelevent丢到threadpool中处理的ExecutionHandler,用于同步处理的BlockingReadHandler,用于打日志的LoggingHandler
clientbootstrap是connect,serverbootstrap是bind
如果让client传上来的channelbuffer经过处理之后,再原路返回一些消息给client,在ChannelHandler中一般这样写:
Channel channel = e.getChannel(); ChannelFuture channelFuture = Channels.future(e.getChannel()); ChannelEvent responseEvent = new DownstreamMessageEvent(channel, channelFuture, newDate, channel.getRemoteAddress()); ctx.sendDownstream(responseEvent); // But still send it upstream because there might be another handler super.messageReceived(ctx, e);
这里返回数据需要注意两点,一个是可能返回的handler之后还有handler,我们还需要继续将修改后的内容继续往后传递,另外一个就是返回的数据可能不希望再被其中的某些handler处理.
handler可以分为upstreamhandler(channelpipeline向外面发数据),downstreamhandler(外面向channelpipeline发数据)和bothhandler.我觉得这个up,down名字取得不好,应该是out=up,in=down,不过如果将channelpipeline想象成一个口袋,可能更好理解.或者理解成:readup,writedown
pipeline中的handler可以动态调整,比如下面的这个,就是根据传入的内容的大小来决定是否需要使用压缩handler来进行压缩处理
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) { // If the message is not a ChannelBuffer, hello ClassCastException ! ChannelBuffer cb = (ChannelBuffer)e.getMessage(); // Check to see if we already removed the handler boolean pipelineContainsCompressor = ctx.getPipeline().getContext(nameOfCompressionHandler)!=null; if(cb.readableBytes() < sizeThreshold) { if(pipelineContainsCompressor) { // The payload is too small to be compressed but the pipeline contains the compression handler // so we need to remove it. compressionHandler = ctx.getPipeline().remove(nameOfCompressionHandler); } } else { // We want to compress the payload, let's make sure the compressor is there if(!pipelineContainsCompressor) { // Oops, it's not there, so lets put it in ctx.getPipeline().addAfter(ctx.getName(), nameOfCompressionHandler , compressionHandler); } } }
ChannelHandlerContext可以看成handler和pipeline之间的adapter.用来方便在handler处理过程中调用pipeline
对于ObjectEncoder是无状态的,而ObjectDecoder是有状态的.之所有有状态是因为解码的过程中需要的ChannelBuffer是不连续的,可能需要多次解码才能得到最终完成的对象,而在处理的过程中需要保存一些状态信息.
如果连接数小于1000,netty推荐采用传统的OIO(blocking)模式
ChannelLocal提供了一种类似ThreadLocal的机制在一个Channel中访问共享的内容.