關(guān)于Netty的一些理解、實(shí)踐與陷阱

核心概念的理解

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è)解決方式

  1. 調(diào)整handler的順序
  2. 調(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》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容