前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Netty模拟发生OOM

使用Netty模拟发生OOM

作者头像
书唐瑞
发布2022-06-02 14:00:20
4820
发布2022-06-02 14:00:20
举报
文章被收录于专栏:Netty历险记

我们模拟这么一个场景,客户端和服务端都使用Netty进行通信,客户端无限循环地向服务端发送数据,过了一会客户端就会出现OOM,我们分析OOM产生的原因,给我们排查线上问题提供一个思路和角度.

本文适合对Netty要有一定的基础

代码位置(https://github.com/infuq/Netty-OOM)

设置的客户端虚拟机参数如下

代码语言:javascript
复制
-XX:MetaspaceSize=18M
-XX:MaxMetaspaceSize=18M
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:\heapdump.hprof
-Xmx1000M -XX:+PrintGC
-XX:+PrintGCDetails

为了讲解方便,我把一些主要代码粘贴如下

客户端代码

代码语言:javascript
复制
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线程的任务队列中.

代码语言:javascript
复制
// 源码位置: 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发生.

我们改写客户端代码

代码语言:javascript
复制
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("达到高水位,暂时不可写");
            }
        }
    }
}

以上代码也只是作为一个思路.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Netty历险记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档