netty填坑----論填坑,我是專業(yè)的

1、為什么netty服務(wù)端啟動后又無異常地自動退出?

netty填坑----論填坑,我是專業(yè)的

網(wǎng)站設(shè)計制作過程拒絕使用模板建站;使用PHP+MYSQL原生開發(fā)可交付網(wǎng)站源代碼;符合網(wǎng)站優(yōu)化排名的后臺管理系統(tǒng);成都網(wǎng)站設(shè)計、網(wǎng)站制作收費合理;免費進(jìn)行網(wǎng)站備案等企業(yè)網(wǎng)站建設(shè)一條龍服務(wù).我們是一家持續(xù)穩(wěn)定運營了10年的創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司。

由于代碼1處執(zhí)行完后直接進(jìn)入2、3,那么netty服務(wù)端就會關(guān)閉退出。

解決一、直接在代碼1后面處加上同步阻塞sync,那么只有服務(wù)端正常關(guān)閉channel時才會執(zhí)行下面的語句

解決二、把代碼2和3移到operationComplete里面,那么也只有channel關(guān)閉時才會讓netty的兩個線程組關(guān)閉

2、netty客戶端連接池資源OOM

生產(chǎn)環(huán)境用netty作為客戶端,為了提高性能,客戶端與服務(wù)端創(chuàng)建多條鏈路,同時客戶端創(chuàng)建一個TCP連接池。結(jié)果業(yè)務(wù)高峰期OOM

netty填坑----論填坑,我是專業(yè)的

從異常日志和線程資源占用來看,導(dǎo)致內(nèi)存泄漏的原因是應(yīng)用創(chuàng)建了大量的EventLoopGroup線程池。這就是一個TCP連接對應(yīng)一個NIO線程的模式。錯誤之在就是采用BIO模式來調(diào)用NIO通信框架,不僅沒優(yōu)化效果,還發(fā)生了OOM。

正確操作是

netty填坑----論填坑,我是專業(yè)的

注意:Bootstrap自身不是線程安全的,但執(zhí)行Bootstrap的連接操作是串行執(zhí)行的。connect方法它會創(chuàng)建一個新的NioSocketChannel,并從初始構(gòu)造的EventLoopGroup中選擇一個NioEventLoop線程執(zhí)行真正的Channel連接操作,與執(zhí)行Boostrap的線程無關(guān)。在同一個Boostrap創(chuàng)建多個客戶端連接,EventLoopGroup是共享的,這些連接共用同一個NIO線程組EventLoopGroup,當(dāng)某個鏈路發(fā)生異?;蜿P(guān)閉時,只需要關(guān)閉并釋放Channel本身即可,不能同時銷毀NioEventLoop和所在線程組EventLoopGroup,下方是錯誤代碼?

netty填坑----論填坑,我是專業(yè)的

Bootstrap不是線程安全的,因此在多個線程中并發(fā)操作Bootstrap是比較危險而且沒有意義。

3、netty居然會產(chǎn)生內(nèi)存池泄漏問題

netty填坑----論填坑,我是專業(yè)的

在調(diào)用ctx.writeAndFlush方法時,當(dāng)消息發(fā)送完成,Netty會主動幫助應(yīng)用釋放內(nèi)存,內(nèi)存釋放場景如下

(1)如果是堆內(nèi)存(PooledHeapByteBuf),則將HeapByteBuffer轉(zhuǎn)換成DirectByteBuffer,并釋放PooledHeapByteBuf到內(nèi)存池。

(2)如果是DirectByteBuffer,則不需要轉(zhuǎn)換,在消息發(fā)送完成后,由ChannelOutboundBuffer的remove方法負(fù)責(zé)釋放

為了在實際項目中更好地管理ByteBuf,下面分4種場景說明

(1)基于內(nèi)存池的請求ByteBuf,這類主要包括PooledDirectByteBuf和PooledHeapByteBuf,它由NioEventLoop線程在處理Channel讀操作時分配,需要在業(yè)務(wù)ChannelInboundHandler處理完請求消息后釋放(通常在解碼之后),它的釋放策略如下:

  1. ChannelInboundHandler繼承自SimpleChannelInboundHandler,實現(xiàn)它的抽象方法channelRead0,ByteBuf的釋放業(yè)務(wù)不用關(guān)心,由SimpleChannelInboundHandler負(fù)責(zé)釋放

  2. 在業(yè)務(wù)ChannelInboundHandler中調(diào)用ctx.fireChannelRead(msg),讓請求繼續(xù)向后執(zhí)行,直至調(diào)用DefaultChannelPipeline的內(nèi)部類TailContext,由它負(fù)責(zé)釋放請求信息

  3. 直接調(diào)用在channelRead方法里調(diào)用ReferenceCountUtil.release(reqMsg)

(2) 基于非內(nèi)存池的請求ByteBuf,它也是需要按照內(nèi)存池的方式釋放內(nèi)存

(3)基于內(nèi)存池的響應(yīng)ByteBuf,根據(jù)之前的分析,只要調(diào)用了writeAndFlush或flush方法,在消息發(fā)送完成后都會由Netty框架進(jìn)行內(nèi)存釋放,業(yè)務(wù)不需要主動釋放內(nèi)存

(4)基于非內(nèi)存池的響應(yīng)ByteBuf,業(yè)務(wù)不需要主動釋放內(nèi)存

當(dāng)然非內(nèi)存池也不一定要手動釋放,但最好手動釋放。Netty里有4種主力的ByteBuf,其中UnpooledHeapByteBuf底下的byte[]能夠依賴JVM GC自然回收;而UnpooledDirectByteBuf底下是DirectByteBuffer,是Java堆外內(nèi)存,除了等JVM GC,最好也能主動進(jìn)行回收,否則導(dǎo)致內(nèi)存不足也產(chǎn)生內(nèi)存泄露的假象;而PooledHeapByteBuf和PooledDirectByteBuf,則必須要主動將用完的byte[]/ByteBuffer放回池里,否則內(nèi)存就要爆掉。

對于內(nèi)存池泄露可以的監(jiān)控可以配置啟動參數(shù)

netty填坑----論填坑,我是專業(yè)的

不同參數(shù)信息如下:?

  1. DISABLED?完全關(guān)閉內(nèi)存泄露檢測,并不建議

  2. SIMPLE?以1%的抽樣率檢測是否泄露,默認(rèn)級別

  3. ADVANCED?抽樣率同SIMPLE,但顯示詳細(xì)的泄露報告

  4. PARANOID?抽樣率為100%,顯示報告信息同ADVANCED

最后,悄悄告訴你,網(wǎng)上的你些netty入門demo大都存在內(nèi)存池泄露問題,只不過數(shù)據(jù)量傳輸少,可能運行大半年才會出現(xiàn)LEAK,就連《netty權(quán)威指南》入門demo也存在這個問題,也許就只是個入門demo,所以不弄得太復(fù)雜。什么你不信,你可以在入門demo的TimeClientHandler或TimeServerHandler加上下面這坨代碼。

????????ByteBuf?firstMessage?=?null;????????
????????for?(int?j?=?0;?j?<?Integer.MAX_VALUE;?j++)?{
????????????firstMessage?=?Unpooled.buffer(1024);????????????
????????????for?(int?i?=?0;?i?<?firstMessage.capacity();?i?++)?{
????????????????firstMessage.writeByte((byte)?i);
????????????}
????????????ctx.writeAndFlush(firstMessage);
????????}

?妥妥的

netty填坑----論填坑,我是專業(yè)的

這就是為什么很多人照抄網(wǎng)上的demo仍會出現(xiàn)內(nèi)存池泄露的原因

4、Netty發(fā)送隊列積壓導(dǎo)致內(nèi)存泄漏

客戶端頻繁發(fā)送消息可以導(dǎo)致發(fā)送隊列積壓,進(jìn)而內(nèi)存增大,響應(yīng)時間長,CPU占用高。

netty填坑----論填坑,我是專業(yè)的

此時我們可以為客戶端設(shè)置高低水位機制,防止自身隊列消息積壓

netty填坑----論填坑,我是專業(yè)的

netty填坑----論填坑,我是專業(yè)的

此外,除了客戶端消息隊列積壓也可能因網(wǎng)絡(luò)鏈接處理能力、服務(wù)器讀取速度小于己方發(fā)送速度有關(guān)。所以在日常監(jiān)控中,需要將Netty的鏈路數(shù)、網(wǎng)絡(luò)讀寫速度等指標(biāo)納入監(jiān)控系統(tǒng),發(fā)現(xiàn)問題之后需要及時告警。

5、API網(wǎng)關(guān)高并發(fā)壓測性能波動

?服務(wù)端轉(zhuǎn)發(fā)請求

public?void?channelRead(ChannelHandlerContext?ctx,?Object?msg)?{
????????ctx.write(msg);
????????char?[]?req?=?new?char[64?*?1024];
????????executorService.execute(()->
????????{
????????????char?[]?dispatchReq?=?req;
????????????//簡單處理之后,轉(zhuǎn)發(fā)請求消息到后端服務(wù),代碼省略
????????????try
????????????{
????????????????//模擬業(yè)務(wù)邏輯處理耗時
????????????????TimeUnit.MICROSECONDS.sleep(100);
????????????}catch?(Exception?e)
????????????{
????????????????e.printStackTrace();
????????????}
????????});
????}

結(jié)果發(fā)現(xiàn)內(nèi)存和CPU占用高,同時QPS下降,停止壓測一段時間,CPU占用和內(nèi)存下降,QPS恢復(fù)正常。

用MAT分析

netty填坑----論填坑,我是專業(yè)的

netty填坑----論填坑,我是專業(yè)的

得出是線程池的char[]積壓,進(jìn)入老年代,導(dǎo)致頻繁full gc。究其原因是,每次都創(chuàng)建64kb的char來存放處理消息,哪怕實際接收消息有?100字節(jié)。修改char大小為消息大小,問題得到解決

netty填坑----論填坑,我是專業(yè)的

6、為什么Netty Server端無法收到Client端發(fā)來的消息?

原因:在handler里面是直接處理業(yè)務(wù)信息,導(dǎo)致IO的操作阻塞,無法讀取Client端發(fā)來的消息

建議將業(yè)務(wù)操作將由另一個線程處理,而不應(yīng)放在IO線程里處理

推薦線程的計算公式:

(1) 線程數(shù)量=(線程總時間/瓶頸資源時間)*瓶頸資源的線程并行數(shù)

(2)QPS=1000/線程總時間*線程數(shù)

7、Netty3.x升級到Netty4.x后產(chǎn)生"bug"

1、版本升級后偶現(xiàn)服務(wù)端發(fā)送給客戶端的應(yīng)答數(shù)據(jù)被篡改?

netty升級4后,線程模型發(fā)生變化,響應(yīng)消息的編碼由NioEventLoop線程異步執(zhí)行,業(yè)務(wù)線程返回。這時如果編碼操作在修改應(yīng)答消息的業(yè)務(wù)邏輯后執(zhí)行,則運行結(jié)果錯誤,數(shù)據(jù)被篡改。

2、升級后為什么上下文丟失問題?

Netty4修改了outbound的線程模型,正好影響了業(yè)務(wù)消息發(fā)送時的上下文傳遞,最終導(dǎo)致業(yè)務(wù)線程變量丟失

3、升級后沒有像官方描述那樣性能得到提升,反而下降了?

可將耗時的反序列操作放到業(yè)務(wù)線程里,而不是ChannelHandler,因為Netty4只有一個NioEventLoop線程來處理這個操作,業(yè)務(wù)耗時ChannelHandler被I/O線程串行執(zhí)行,所以執(zhí)行效率低。Netty3在消息發(fā)送線程模型上,充分利用業(yè)務(wù)線程的并行編碼和ChanelHandler的優(yōu)勢,在一個周期T內(nèi)可以處理N條業(yè)務(wù)消息。

性能優(yōu)化建議:適當(dāng)高大work線程組的線程數(shù)(NioEventLoopGroup),分擔(dān)每個NioEventLoop線程的負(fù)載,提升ChannelHandler執(zhí)行的并發(fā)度。同時,將業(yè)務(wù)上耗時的操作從ChannelHandler移除,放入業(yè)務(wù)線程池處理。對于不合適轉(zhuǎn)移到業(yè)務(wù)線程處理的一些耗時邏輯,也可以通過為ChannelHandler綁定線程池的方式提升性能。Netty3的Downstream由業(yè)務(wù)線程執(zhí)行,意味著某一時刻有多個業(yè)務(wù)線程同時操作ChannelHandler,用戶需要并發(fā)保護。

8、為什么netty自帶的業(yè)務(wù)線程池沒有提高性能,netty有bug?

server端使用netty自帶的線程池來處理業(yè)務(wù)

netty填坑----論填坑,我是專業(yè)的

而client端如下

netty填坑----論填坑,我是專業(yè)的

實際結(jié)果server端的QPS只有個位數(shù),究其原因是一個tcp連接對應(yīng)一個channel,一個channel就對應(yīng)一個DefaultEventExecutor(業(yè)務(wù)線程)?執(zhí)行,所以它雖然給channel綁定線程池,但一個channel還是一個業(yè)務(wù)線程在處理。解決辦法是在ChannelHandler里面再創(chuàng)建一個線程池,此時就能利用線程池的并行處理能力。

netty填坑----論填坑,我是專業(yè)的

當(dāng)然,?server端使用netty自帶的線程池來處理業(yè)務(wù),它的用法是當(dāng)建立多個tcp連接時,每個連接能對應(yīng)一個線程來處理ChannelHandler。所以它在多tcp連接時能提高業(yè)務(wù)的并行處理能力。

Netty提供的業(yè)務(wù)線程池能降低了鎖競爭,提升了系統(tǒng)的并發(fā)處理性能。如果使用業(yè)務(wù)自定義實現(xiàn)的線程池,如果追求更高的性能,就要在消除或減輕鎖競爭上下工夫(ThreadPoolExecutor采用的是“一個阻塞隊列+N個工作線程”的模型,如果業(yè)務(wù)線程數(shù)比較多,就會形成激烈的鎖競爭)

9、發(fā)送端和接收端處理速度不均衡怎么辦?

可用流量×××方案, 流量×××和流控的最大區(qū)別在于,流控會拒絕消息,流量×××不拒絕和丟棄消息,無論接收量多大,它總能以近似恒定的速度下發(fā)消息,跟變壓器的原理和功能類似。

接收端代碼如下:

????????//?配置服務(wù)端的NIO線程組
????????EventLoopGroup?bossGroup?=?new?NioEventLoopGroup();
????????EventLoopGroup?workerGroup?=?new?NioEventLoopGroup();
????????try?{
????????????ServerBootstrap?b?=?new?ServerBootstrap();
????????????b.group(bossGroup,?workerGroup)
????????????????????.channel(NioServerSocketChannel.class)
????????????????????.option(ChannelOption.SO_BACKLOG,?100)
????????????????????.handler(new?LoggingHandler(LogLevel.INFO))
????????????????????.childHandler(new?ChannelInitializer<SocketChannel>()?{
????????????????????????@Override
????????????????????????public?void?initChannel(SocketChannel?ch)
????????????????????????????????throws?Exception?{
????????????????????????????ch.pipeline().addLast("Channel?Traffic?Shaping",?new?ChannelTrafficShapingHandler(1024?*?1024,?1024?*?1024,?1000));
????????????????????????????ByteBuf?delimiter?=?Unpooled.copiedBuffer("$_"
????????????????????????????????????.getBytes());
????????????????????????????ch.pipeline().addLast(
????????????????????????????????????new?DelimiterBasedFrameDecoder(2048?*?1024,
????????????????????????????????????????????delimiter));
????????????????????????????ch.pipeline().addLast(new?StringDecoder());
????????????????????????????ch.pipeline().addLast(new?TrafficShapingServerHandler());
????????????????????????}
????????????????????});
????????????//?綁定端口,同步等待成功
????????????ChannelFuture?f?=?b.bind(port).sync();
????????????//?等待服務(wù)端監(jiān)聽端口關(guān)閉
????????????f.channel().closeFuture().sync();
????????}?finally?{
????????????//?優(yōu)雅退出,釋放線程池資源
????????????bossGroup.shutdownGracefully();
????????????workerGroup.shutdownGracefully();
????????}

當(dāng)前標(biāo)題:netty填坑----論填坑,我是專業(yè)的
轉(zhuǎn)載來于:http://muchs.cn/article30/jcjdpo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、網(wǎng)站營銷、關(guān)鍵詞優(yōu)化、域名注冊靜態(tài)網(wǎng)站、軟件開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)