網(wǎng)站設(shè)計(jì)制作過程拒絕使用模板建站;使用PHP+MYSQL原生開發(fā)可交付網(wǎng)站源代碼;符合網(wǎng)站優(yōu)化排名的后臺管理系統(tǒng);成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作收費(fèi)合理;免費(fèi)進(jìn)行網(wǎng)站備案等企業(yè)網(wǎng)站建設(shè)一條龍服務(wù).我們是一家持續(xù)穩(wěn)定運(yù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時(shí)才會執(zhí)行下面的語句
解決二、把代碼2和3移到operationComplete里面,那么也只有channel關(guān)閉時(shí)才會讓netty的兩個線程組關(guān)閉
生產(chǎn)環(huán)境用netty作為客戶端,為了提高性能,客戶端與服務(wù)端創(chuàng)建多條鏈路,同時(shí)客戶端創(chuàng)建一個TCP連接池。結(jié)果業(yè)務(wù)高峰期OOM
從異常日志和線程資源占用來看,導(dǎo)致內(nèi)存泄漏的原因是應(yīng)用創(chuàng)建了大量的EventLoopGroup線程池。這就是一個TCP連接對應(yīng)一個NIO線程的模式。錯誤之在就是采用BIO模式來調(diào)用NIO通信框架,不僅沒優(yōu)化效果,還發(fā)生了OOM。
正確操作是
注意: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)閉時(shí),只需要關(guān)閉并釋放Channel本身即可,不能同時(shí)銷毀NioEventLoop和所在線程組EventLoopGroup,下方是錯誤代碼?
Bootstrap不是線程安全的,因此在多個線程中并發(fā)操作Bootstrap是比較危險(xiǎn)而且沒有意義。
在調(diào)用ctx.writeAndFlush方法時(shí),當(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é)釋放
為了在實(shí)際項(xiàng)目中更好地管理ByteBuf,下面分4種場景說明
(1)基于內(nèi)存池的請求ByteBuf,這類主要包括PooledDirectByteBuf和PooledHeapByteBuf,它由NioEventLoop線程在處理Channel讀操作時(shí)分配,需要在業(yè)務(wù)ChannelInboundHandler處理完請求消息后釋放(通常在解碼之后),它的釋放策略如下:
ChannelInboundHandler繼承自SimpleChannelInboundHandler,實(shí)現(xiàn)它的抽象方法channelRead0,ByteBuf的釋放業(yè)務(wù)不用關(guān)心,由SimpleChannelInboundHandler負(fù)責(zé)釋放
在業(yè)務(wù)ChannelInboundHandler中調(diào)用ctx.fireChannelRead(msg),讓請求繼續(xù)向后執(zhí)行,直至調(diào)用DefaultChannelPipeline的內(nèi)部類TailContext,由它負(fù)責(zé)釋放請求信息
直接調(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ù)
不同參數(shù)信息如下:?
DISABLED
?完全關(guān)閉內(nèi)存泄露檢測,并不建議
SIMPLE
?以1%的抽樣率檢測是否泄露,默認(rèn)級別
ADVANCED
?抽樣率同SIMPLE
,但顯示詳細(xì)的泄露報(bào)告
PARANOID
?抽樣率為100%,顯示報(bào)告信息同ADVANCED
最后,悄悄告訴你,網(wǎng)上的你些netty入門demo大都存在內(nèi)存池泄露問題,只不過數(shù)據(jù)量傳輸少,可能運(yùn)行大半年才會出現(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); ????????}
?妥妥的
這就是為什么很多人照抄網(wǎng)上的demo仍會出現(xiàn)內(nèi)存池泄露的原因
客戶端頻繁發(fā)送消息可以導(dǎo)致發(fā)送隊(duì)列積壓,進(jìn)而內(nèi)存增大,響應(yīng)時(shí)間長,CPU占用高。
此時(shí)我們可以為客戶端設(shè)置高低水位機(jī)制,防止自身隊(duì)列消息積壓
此外,除了客戶端消息隊(duì)列積壓也可能因網(wǎng)絡(luò)鏈接處理能力、服務(wù)器讀取速度小于己方發(fā)送速度有關(guān)。所以在日常監(jiān)控中,需要將Netty的鏈路數(shù)、網(wǎng)絡(luò)讀寫速度等指標(biāo)納入監(jiān)控系統(tǒng),發(fā)現(xiàn)問題之后需要及時(shí)告警。
?服務(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ù)邏輯處理耗時(shí) ????????????????TimeUnit.MICROSECONDS.sleep(100); ????????????}catch?(Exception?e) ????????????{ ????????????????e.printStackTrace(); ????????????} ????????}); ????}
結(jié)果發(fā)現(xiàn)內(nèi)存和CPU占用高,同時(shí)QPS下降,停止壓測一段時(shí)間,CPU占用和內(nèi)存下降,QPS恢復(fù)正常。
用MAT分析
得出是線程池的char[]積壓,進(jìn)入老年代,導(dǎo)致頻繁full gc。究其原因是,每次都創(chuàng)建64kb的char來存放處理消息,哪怕實(shí)際接收消息有?100字節(jié)。修改char大小為消息大小,問題得到解決
原因:在handler里面是直接處理業(yè)務(wù)信息,導(dǎo)致IO的操作阻塞,無法讀取Client端發(fā)來的消息
建議將業(yè)務(wù)操作將由另一個線程處理,而不應(yīng)放在IO線程里處理
推薦線程的計(jì)算公式:
(1) 線程數(shù)量=(線程總時(shí)間/瓶頸資源時(shí)間)*瓶頸資源的線程并行數(shù)
(2)QPS=1000/線程總時(shí)間*線程數(shù)
1、版本升級后偶現(xiàn)服務(wù)端發(fā)送給客戶端的應(yīng)答數(shù)據(jù)被篡改?
netty升級4后,線程模型發(fā)生變化,響應(yīng)消息的編碼由NioEventLoop線程異步執(zhí)行,業(yè)務(wù)線程返回。這時(shí)如果編碼操作在修改應(yīng)答消息的業(yè)務(wù)邏輯后執(zhí)行,則運(yùn)行結(jié)果錯誤,數(shù)據(jù)被篡改。
2、升級后為什么上下文丟失問題?
Netty4修改了outbound的線程模型,正好影響了業(yè)務(wù)消息發(fā)送時(shí)的上下文傳遞,最終導(dǎo)致業(yè)務(wù)線程變量丟失
3、升級后沒有像官方描述那樣性能得到提升,反而下降了?
可將耗時(shí)的反序列操作放到業(yè)務(wù)線程里,而不是ChannelHandler,因?yàn)镹etty4只有一個NioEventLoop線程來處理這個操作,業(yè)務(wù)耗時(shí)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ā)度。同時(shí),將業(yè)務(wù)上耗時(shí)的操作從ChannelHandler移除,放入業(yè)務(wù)線程池處理。對于不合適轉(zhuǎn)移到業(yè)務(wù)線程處理的一些耗時(shí)邏輯,也可以通過為ChannelHandler綁定線程池的方式提升性能。Netty3的Downstream由業(yè)務(wù)線程執(zhí)行,意味著某一時(shí)刻有多個業(yè)務(wù)線程同時(shí)操作ChannelHandler,用戶需要并發(fā)保護(hù)。
server端使用netty自帶的線程池來處理業(yè)務(wù)
而client端如下
實(shí)際結(jié)果server端的QPS只有個位數(shù),究其原因是一個tcp連接對應(yīng)一個channel,一個channel就對應(yīng)一個DefaultEventExecutor(業(yè)務(wù)線程)?執(zhí)行,所以它雖然給channel綁定線程池,但一個channel還是一個業(yè)務(wù)線程在處理。解決辦法是在ChannelHandler里面再創(chuàng)建一個線程池,此時(shí)就能利用線程池的并行處理能力。
當(dāng)然,?server端使用netty自帶的線程池來處理業(yè)務(wù),它的用法是當(dāng)建立多個tcp連接時(shí),每個連接能對應(yīng)一個線程來處理ChannelHandler。所以它在多tcp連接時(shí)能提高業(yè)務(wù)的并行處理能力。
Netty提供的業(yè)務(wù)線程池能降低了鎖競爭,提升了系統(tǒng)的并發(fā)處理性能。如果使用業(yè)務(wù)自定義實(shí)現(xiàn)的線程池,如果追求更高的性能,就要在消除或減輕鎖競爭上下工夫(ThreadPoolExecutor采用的是“一個阻塞隊(duì)列+N個工作線程”的模型,如果業(yè)務(wù)線程數(shù)比較多,就會形成激烈的鎖競爭)
可用流量×××方案, 流量×××和流控的最大區(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://m.rwnh.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)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)