核心概念的理解
Netty對於網路層進行了自己的抽象,用Channel表示連線,讀寫就是Channel上發生的事件,ChannelHandler用來處理這些事件,ChannelPipeline基於unix哲學提供了一種優雅的組織ChannelHandler的方式,用管道解耦不同層面的處理。現在回過頭來看看,真的是非常天才和優雅的設計,是我心中API設計的典範之一了。
TCP半包、粘包
使用Netty內建的LineBasedFrameDecoder
或者LengthFieldBasedFrameDecoder
,我們只要在pipeline中新增,就解決了這個問題。
Writtable問題
有時候,由於TCP的send buffer滿了,向channel的寫入會失敗。我們需要檢查channel().isWritable()
標記來確定是否執行寫入。
處理耗時任務
Netty In Action以及網上的一些資料中,都沒有很直接的展示如何在Netty中去處理耗時任務。其實也很簡單,只要給handler指定一個事件迴圈就可以,例如
public class MyChannelInitializer extends ChannelInitializer<Channel> {
private static EventExecutorGroup longTaskGroup = new DefaultEventExecutorGroup(5);
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
...
pipeline.addLast(longTaskGroup, new PrintHandler());
}
}
複製程式碼
Pitfall
Netty的ChannelPipeline只有一條雙向鏈,訊息入站,經過一串InBoundHandler之後,以相反的順序再經過OutBoundHandler出站.因此,我們自定義的handler一般會處於pipeline的末尾!
舉個例子,當以如下順序新增handler時,如果呼叫ChannelHandlerContext上的writeAndFlush方法,出站訊息是無法經過StringEncoder的
public class MyChannelInitializer extends ChannelInitializer<Channel> {
private static EventExecutorGroup longTaskGroup = new DefaultEventExecutorGroup(5);
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new LineBasedFrameDecoder(64 * 1024));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(longTaskGroup, new PrintHandler());
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
}
}
複製程式碼
這個問題有兩個解決方式
- 調整handler的順序
- 呼叫channel上的writeAndFlush方法,強制使訊息在整個pipeline上流動
調整handler的順序
public class MyChannelInitializer extends ChannelInitializer<Channel> {
private static EventExecutorGroup longTaskGroup = new DefaultEventExecutorGroup(5);
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new LineBasedFrameDecoder(64 * 1024));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(longTaskGroup, new PrintHandler());
}
}
複製程式碼
呼叫Channel上的writeAndFlush方法
public class PrintHandler extends SimpleChannelInboundHandler<String> {
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// ctx.writeAndFlush(msg);
ctx.channel().writeAndFlush(msg);
System.out.println(msg);
}
}
複製程式碼
參考
http://www.voidcn.com/article/p-yhpuvvkx-mm.html https://stackoverflow.com/questions/37474482/dealing-with-long-time-task-such-as-sql-query-in-netty 《Netty In Action》