使用Netty模擬發生OOM
我們模擬這麼一個場景,客戶端和服務端都使用Netty進行通訊,客戶端無限迴圈地向服務端傳送資料,過了一會客戶端就會出現OOM,我們分析OOM產生的原因,給我們排查線上問題提供一個思路和角度.
以下所有的分析都是基於以上描述的場景
本文適合對Netty要有一定的基礎
程式碼放在了github上
設定的客戶端虛擬機器引數
-XX:MetaspaceSize=18M
-XX:MaxMetaspaceSize=18M
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:\heapdump.hprof
-Xmx1000M
-XX:+PrintGC
-XX:+PrintGCDetails
為了講解方便,我把一些主要程式碼貼上如下
客戶端程式碼
EventLoopGroup group = new NioEventLoopGroup();
EventLoopGroup businessGroup = new NioEventLoopGroup(8);
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ChannelPipeline channelPipeline = ch.pipeline();
channelPipeline.addLast(new StringDecoder());// Netty自帶的字串解碼器
channelPipeline.addLast(new StringEncoder());// Netty自帶的字串編碼器
channelPipeline.addLast(businessGroup, new ClientHandler());// 自定義處理器
}
});
---
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// Channel啟用之後,便無限迴圈地向服務端傳送資料
int i = 0;
for (;;) {
ctx.writeAndFlush("這個是客戶端傳送的第" + ++i + "個訊息");
}
}
}
由於服務端只是接收資料,沒有特殊地方,這裡就不貼上程式碼了.
先啟動服務端,在啟動客戶端.
客戶端就會連線服務端,通道建立之後,業務執行緒就會無限迴圈地向服務端傳送資料.
你也可以通過JDK自帶的工具觀察記憶體的變化.
當程式執行一會之後,就會出現OOM異常
我們這裡通過MAT工具分析下堆空間資訊
匯入檔案.(至於怎麼使用MAT工具這裡不做介紹)
我們會發現taskQueue中有非常多的Task,這是因為向對端寫資料的操作必須是IO執行緒來完成,業務執行緒只能把它的需求封裝成一個Task放在IO執行緒的任務佇列中.
// 原始碼位置: io.netty.channel.AbstractChannelHandlerContext#write(java.lang.Object, boolean, io.netty.channel.ChannelPromise)
private void write(Object msg, boolean flush, ChannelPromise promise) {
final AbstractChannelHandlerContext next = findContextOutbound(flush ?
(MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
final Object m = pipeline.touch(msg, next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {// 判斷當前執行緒是否是IO執行緒
if (flush) {
next.invokeWriteAndFlush(m, promise);
} else {
next.invokeWrite(m, promise);
}
} else {
final AbstractWriteTask task;
if (flush) {
// 由於當前執行緒不是IO執行緒,所以只能封裝成一個Task,放入到佇列中
task = WriteAndFlushTask.newInstance(next, m, promise);
} else {
task = WriteTask.newInstance(next, m, promise);
}
// Task放入到佇列
if (!safeExecute(executor, task, promise, m)) {
task.cancel();
}
}
}
由於業務執行緒是無限迴圈地寫入資料,導致佇列中的Task一直增多,最後導致OOM
一方面可能是服務端處理的比較慢,導致服務端TCP緩衝區滿了,那麼客戶端的TCP緩衝區也會被寫滿,Netty就不能成功的寫入TCP緩衝區,那麼資料只能放在佇列中,最後導致OOM.(當然我們這裡不是因為這個原因,我們的服務端只是接收資料,沒有任何業務耗時操作)
也有可能是網路等原因,導致客戶端IO執行緒傳送的比較慢(業務執行緒生成的資料比較快).
或者也有其他的原因.
Netty給我們提供了高低水位機制,當我們業務執行緒向Netty寫入的資料過多的時候,一旦達到了高水位值(這個值我們可以設定),Netty就會設定Channel不可能.但是這裡注意了,這裡只是設定成不可能,我們還是依然可以向Netty中寫入資料.但是如果我們忽略它,有可能造成上面這種OOM情況.
因此我們可以基於Netty提供的這種機制,控制我們的業務執行緒向Netty寫入資料的速率.如果達到了高水位值,我們就暫時不要向Netty中寫入資料,也就不會導致OOM發生.
我們改寫客戶端程式碼
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 設定高水位值(當然不一定非要在此處設定)
ctx.channel().config().setWriteBufferHighWaterMark(20 * 1024 * 1024);
int i = 0;
for (;;) {
// 通道可寫
if (ctx.channel().isWritable()) {
ctx.writeAndFlush("這個是客戶端傳送的第" + ++i + "個訊息");
} else {// 通道不可寫
System.out.println("達到高水位,暫時不可寫");
}
}
}
}
以上程式碼也只是作為一個思路.
公眾號
相關文章
- Vivado DDS IP核使用和模擬(一、單通道訊號發生器)
- Java使用程式碼模擬高併發操作Java
- 在 Node 服務中發生 OOM 時,如何監控記憶體?OOM記憶體
- 光學數字孿生系統模擬
- 精通併發與 Netty (一)如何使用Netty
- 一次生產環境OOM排查OOM
- 使用OkHttp模擬登陸LeetCodeHTTPLeetCode
- 使用 Netcat 模擬 HTTP 請求HTTP
- 使用 node 模擬請求介面
- Vivado使用技巧(18):模擬功能概述
- 爬蟲方式(模擬使用者)爬蟲
- 使用KepServerEx進行資料模擬Server
- iOS開發實踐-OOM治理iOSOOM
- 突發:當機崩潰OOMOOM
- 使用 Spring Boot 構建可重用的模擬模組Spring Boot
- 模擬併發及生成唯一
- 併發模擬-程式碼CountDownLatch,SemaphoreCountDownLatch
- 8、SSH埠轉發情景模擬
- Laravel 高併發搶購模擬Laravel
- 如何使用RunnerGo模擬使用者分流負載Go負載
- 鴻蒙Flutter實戰:12-使用模擬器開發除錯鴻蒙Flutter除錯
- iOS開發中,whistle的安裝使用及模擬器代理配置iOS
- 模擬
- netty系列之:在netty中使用proxy protocolNettyProtocol
- Mac 安卓Studio使用外部模擬器Mac安卓
- Dynamics 365 Web API模擬使用者WebAPI
- 使用mumu模擬器抓包 andriod appAPP
- Mock 工具使用 - 模擬弱網測試Mock
- 使用 mock 模擬登入介面資料Mock
- 如何使用ChatGPT模擬MySQL資料庫ChatGPTMySql資料庫
- 【原始碼】使用MATLAB進行ECG模擬原始碼Matlab
- Netty的LengthFieldBasedFrameDecoder使用Netty
- 10.6 模擬賽(NOIP 模擬賽 #9)
- 2024.11.20 NOIP模擬 - 模擬賽記錄
- 使用IDEA模擬git命令使用的常見場景IdeaGit
- netty系列之:在netty中使用protobuf協議Netty協議
- netty系列之:使用netty搭建websocket客戶端NettyWeb客戶端
- netty系列之:使用netty搭建websocket伺服器NettyWeb伺服器