核心概念的理解
Netty對(duì)于網(wǎng)絡(luò)層進(jìn)行了自己的抽象,用Channel表示連接,讀寫(xiě)就是Channel上發(fā)生的事件,ChannelHandler用來(lái)處理這些事件,ChannelPipeline基于unix哲學(xué)提供了一種優(yōu)雅的組織ChannelHandler的方式,用管道解耦不同層面的處理?,F(xiàn)在回過(guò)頭來(lái)看看,真的是非常天才和優(yōu)雅的設(shè)計(jì),是我心中API設(shè)計(jì)的典范之一了。
TCP半包、粘包
使用Netty內(nèi)置的LineBasedFrameDecoder或者LengthFieldBasedFrameDecoder,我們只要在pipeline中添加,就解決了這個(gè)問(wèn)題。
Writtable問(wèn)題
有時(shí)候,由于TCP的send buffer滿了,向channel的寫(xiě)入會(huì)失敗。我們需要檢查channel().isWritable()標(biāo)記來(lái)確定是否執(zhí)行寫(xiě)入。
處理耗時(shí)任務(wù)
Netty In Action以及網(wǎng)上的一些資料中,都沒(méi)有很直接的展示如何在Netty中去處理耗時(shí)任務(wù)。其實(shí)也很簡(jiǎn)單,只要給handler指定一個(gè)事件循環(huán)就可以,例如
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只有一條雙向鏈,消息入站,經(jīng)過(guò)一串InBoundHandler之后,以相反的順序再經(jīng)過(guò)OutBoundHandler出站.因此,我們自定義的handler一般會(huì)處于pipeline的末尾!
舉個(gè)例子,當(dāng)以如下順序添加handler時(shí),如果調(diào)用ChannelHandlerContext上的writeAndFlush方法,出站消息是無(wú)法經(jīng)過(guò)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));
}
}
這個(gè)問(wèn)題有兩個(gè)解決方式
- 調(diào)整handler的順序
- 調(diào)用channel上的writeAndFlush方法,強(qiáng)制使消息在整個(gè)pipeline上流動(dòng)
調(diào)整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());
}
}
調(diào)用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》