中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

netty填坑----論填坑,我是專業的

發布時間:2020-06-14 20:22:56 來源:網絡 閱讀:2126 作者:sq5d41a7a774d48 欄目:建站服務器

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

netty填坑----論填坑,我是專業的

由于代碼1處執行完后直接進入2、3,那么netty服務端就會關閉退出。

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

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


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

生產環境用netty作為客戶端,為了提高性能,客戶端與服務端創建多條鏈路,同時客戶端創建一個TCP連接池。結果業務高峰期OOM

netty填坑----論填坑,我是專業的

從異常日志和線程資源占用來看,導致內存泄漏的原因是應用創建了大量的EventLoopGroup線程池。這就是一個TCP連接對應一個NIO線程的模式。錯誤之在就是采用BIO模式來調用NIO通信框架,不僅沒優化效果,還發生了OOM。

正確操作是

netty填坑----論填坑,我是專業的

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

netty填坑----論填坑,我是專業的

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


3、netty居然會產生內存池泄漏問題

netty填坑----論填坑,我是專業的

在調用ctx.writeAndFlush方法時,當消息發送完成,Netty會主動幫助應用釋放內存,內存釋放場景如下

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

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


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

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

  1. ChannelInboundHandler繼承自SimpleChannelInboundHandler,實現它的抽象方法channelRead0,ByteBuf的釋放業務不用關心,由SimpleChannelInboundHandler負責釋放

  2. 在業務ChannelInboundHandler中調用ctx.fireChannelRead(msg),讓請求繼續向后執行,直至調用DefaultChannelPipeline的內部類TailContext,由它負責釋放請求信息

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

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

(3)基于內存池的響應ByteBuf,根據之前的分析,只要調用了writeAndFlush或flush方法,在消息發送完成后都會由Netty框架進行內存釋放,業務不需要主動釋放內存

(4)基于非內存池的響應ByteBuf,業務不需要主動釋放內存


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


對于內存池泄露可以的監控可以配置啟動參數

netty填坑----論填坑,我是專業的

不同參數信息如下:?

  1. DISABLED?完全關閉內存泄露檢測,并不建議

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

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

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

最后,悄悄告訴你,網上的你些netty入門demo大都存在內存池泄露問題,只不過數據量傳輸少,可能運行大半年才會出現LEAK,就連《netty權威指南》入門demo也存在這個問題,也許就只是個入門demo,所以不弄得太復雜。什么你不信,你可以在入門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填坑----論填坑,我是專業的

這就是為什么很多人照抄網上的demo仍會出現內存池泄露的原因


4、Netty發送隊列積壓導致內存泄漏

客戶端頻繁發送消息可以導致發送隊列積壓,進而內存增大,響應時間長,CPU占用高。

netty填坑----論填坑,我是專業的

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

netty填坑----論填坑,我是專業的

netty填坑----論填坑,我是專業的

此外,除了客戶端消息隊列積壓也可能因網絡鏈接處理能力、服務器讀取速度小于己方發送速度有關。所以在日常監控中,需要將Netty的鏈路數、網絡讀寫速度等指標納入監控系統,發現問題之后需要及時告警。


5、API網關高并發壓測性能波動

?服務端轉發請求

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

結果發現內存和CPU占用高,同時QPS下降,停止壓測一段時間,CPU占用和內存下降,QPS恢復正常。

用MAT分析

netty填坑----論填坑,我是專業的

netty填坑----論填坑,我是專業的

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

netty填坑----論填坑,我是專業的


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

原因:在handler里面是直接處理業務信息,導致IO的操作阻塞,無法讀取Client端發來的消息

建議將業務操作將由另一個線程處理,而不應放在IO線程里處理

推薦線程的計算公式:

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

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


7、Netty3.x升級到Netty4.x后產生"bug"

1、版本升級后偶現服務端發送給客戶端的應答數據被篡改?

netty升級4后,線程模型發生變化,響應消息的編碼由NioEventLoop線程異步執行,業務線程返回。這時如果編碼操作在修改應答消息的業務邏輯后執行,則運行結果錯誤,數據被篡改。


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

Netty4修改了outbound的線程模型,正好影響了業務消息發送時的上下文傳遞,最終導致業務線程變量丟失


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

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

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


8、為什么netty自帶的業務線程池沒有提高性能,netty有bug?

server端使用netty自帶的線程池來處理業務

netty填坑----論填坑,我是專業的

而client端如下

netty填坑----論填坑,我是專業的

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

netty填坑----論填坑,我是專業的

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

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


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

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

接收端代碼如下:

????????//?配置服務端的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();
????????????//?等待服務端監聽端口關閉
????????????f.channel().closeFuture().sync();
????????}?finally?{
????????????//?優雅退出,釋放線程池資源
????????????bossGroup.shutdownGracefully();
????????????workerGroup.shutdownGracefully();
????????}


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

古丈县| 锡林浩特市| 台北市| 庆城县| 南京市| 淄博市| 临武县| 巢湖市| 定兴县| 黄陵县| 来宾市| 浦城县| 高邑县| 青海省| 南召县| 高雄市| 龙海市| 丰都县| 阜宁县| 昌吉市| 兴城市| 商水县| 华宁县| 邢台市| 浦东新区| 英山县| 广元市| 芜湖县| 广德县| 永福县| 开远市| 小金县| 习水县| 平武县| 盈江县| 修文县| 定安县| 弥渡县| 宜阳县| 南涧| 阆中市|