本文是阅读以下两篇文章时:
http://seeallhearall.blogspot.com/2012/05/netty-tutorial-part-1-introduction-to.html
http://seeallhearall.blogspot.com/2012/06/netty-tutorial-part-15-on-channel.html
我的一些笔记
=================笔记1
static class ServerDateHandler extends SimpleChannelHandler {
Random random = new Random(System.nanoTime());
public void messageReceived(ChannelHandlerContext ctx,MessageEvent e) throws Exception {
Date date = (Date)e.getMessage();
// Here's the REALLY important business service at the end of the pipeline
long newTime = (date.getTime() + random.nextInt());
Date newDate = new Date(newTime);
slog("Hey Guys ! I got a date ! [" + date + "] and I modified it to [" + newDate + "]");
// Send back the reponse
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);
}
}
为什么不直接这样:ctx.getChannel().write(newDate)?
作者是这样解释的:
If we wrote to the Channel directly, it would start from the "top" of the pipeline
and might be pushed through handlers that are not indented to be called with the return Date.
不是很理解,看看源码:
e.getChannel().write最终调用DefaultChannelPipeline,从最后一个ChannelHandler(tail)往前,
消息会经过所有的DownstreamHandler:
public void sendDownstream(ChannelEvent e) {
DefaultChannelHandlerContext tail = getActualDownstreamContext(this.tail);
if (tail == null) {
try {
getSink().eventSunk(this, e);
return;
} catch (Throwable t) {
notifyHandlerException(e, t);
return;
}
}
sendDownstream(tail, e);
}
ctx.sendDownstream则只从当前的ChannelHandler往前走,也就是,
只有在当前ChannelHandler之前的ChannelHandler,才会经过:
public void sendDownstream(ChannelEvent e) {
DefaultChannelHandlerContext prev = getActualDownstreamContext(this.prev);
if (prev == null) {
try {
getSink().eventSunk(DefaultChannelPipeline.this, e);
} catch (Throwable t) {
notifyHandlerException(e, t);
}
} else {
DefaultChannelPipeline.this.sendDownstream(prev, e);
}
}
这样可以避免消息穿过不必要的ChannelHandler,当然,如果清楚地知道,
那些“不必要的ChannelHandler”不会修改消息,
那直接e.getChannel().write也未尝不可
=================笔记2
a Channel provides the interface to connect and write to the destination represented by the Channel.
No read ? you might ask ? Nope. Remember, it's like the asynchronous getWidgetCountmethod mentioned above.
There's no return value.
Channel没有read方法?那怎么读取消息?
原来,读取到的消息,都封装在MessageEvent里面了,
它帮你读取好了,再fireMessageReceived通知你处理——这就是所谓的“异步”。详见NioWorker的read
=================笔记3
In the server, there is one boss thread allocated per listening socket.
For example, if you opened two server ports such as 80 and 443, you will have two boss threads.
In the client, there is only one boss thread*
=================笔记4
Please note that this decoder must be used with a proper FrameDecoder such as DelimiterBasedFrameDecoder
if you are using a stream-based transport such as TCP/IP.
// Decoders
pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(80, Delimiters.lineDelimiter()));
pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
为什么 StringDecoder要和FrameDecoder在一起使用?
因为任何消息的到来,刚开始都是byte[],如何进行“分段”( segment the byte stream,
分段方案有很多,例如length+content、或者delimiter等等),是第一个ChannelHandler的工作。
StringDecoder并没有“分段”的功能,它没有extends FrameDecoder
因此要在它前面安排一个FrameDecoder
=================笔记5
自定义delimiter:
ChannelBuffer delimiter = ChannelBuffers.wrappedBuffer("THEBIG-D".getBytes())
=================笔记6
In the server, there is one boss thread allocated per listening socket. In the client, there is only one boss thread*
1.The boss thread can be released when there is no work to do and is created lazily,
but it may be more efficient to pool a small number of threads than
create a new one when required and destroying it when idle.
2.It is possible that one might want to create several different channel factories
and rather than giving each one their own boss pool, they can all share one.
为什么client只需要一个boss thread但仍然采用Excutor?
除了作者说的两个原因之外,我认为还有个原因是出于扩展性的考虑,允许你传入不同的Executor
=================笔记7
The Worker Threads: Worker threads perform all the asynchronous I/O. They are not general purpose threads and developers should take precautions not to assign unrelated tasks to threads in this pool which may cause the threads to block, be rendered unable to perform their real work which in turn may cause deadlocks and an untold number of performance issues.
第一次看这段话不理解,现在看来作者的意思应该是:worker thread是专门为IO而生,且是异步的。开发者不应该把其他工作交给worker thread运行
=================笔记8
Once you're done with a ChannelFactory, be sure to callreleaseExternalResources() on the factory. This will ensure that all its resources are released.
要记得调用 ChannelFactory的releaseExternalResources来释放资源:
bootstrap.releaseExternalResources();
public class Bootstrap...{
public void releaseExternalResources() {
ChannelFactory factory = this.factory;
if (factory != null) {
factory.releaseExternalResources();
}
}
}