dubbo处理调用请求涉及缓存问题

一.场景
dubbo底层使用netty,一个boss线程,核心线程+1个worker。
读写io在worker线程去做。
每个worker会处理一部分socket读写。
高并发时,io线程不太会不足,原因是默认是核心线程+1。高并发时每个线程都会不断的在处理读写事件,cpu一直是处于忙的状态。这时增加io线程更多反而会因为cpu切换线程上下文而影响性能。

一般业务线程池不足可能性比较多,默认有200个线程。当然如果出现请求过多处理不过来,直接就抛错了,问题倒比较好查。

当然也可能会是网络出现瓶颈,内存及cpu出现瓶颈,需要用相应的工具命令查看。

之后会做实际测试。
而这里主要讲,worker线程在获取到socket读请求时,会从heap缓存中获取一个buffer。由于是堆缓存,不可避免一次内核到堆内存的拷贝。

二.原理

服务方处理消费方的调用流程如下:
1.一个读请求过来时,worker线程,首先从尝试从direct缓存区获取一个缓存,最多存储8块,是softReference类型的一个ByteBuf数组。
注:direct缓存默认在full gc时才会进行回收。如果没有full gc
用softReference不用担心由于缓存的原因。

注:这里如果没有则会创建一块direct缓存。
这里的direct缓存区是每个worker都有一个,所以不存在线程竞争问题。

2.将对应socket数据写入到这块direct缓存

3.创建一块heap内存,然后将direct缓存的数据写到这个堆内存

4.将之前创建的direct缓存放回到缓存池

5.将这个heap buffer交给后面的channelHandler处理。
后面会解码,channel handler处理也在io线程。

6.handler都处理完成后,会将这个解码后的消息交给另外一个线程池去做。这个线程池默认200个线程。

实际上还是发生了一次从direct缓存拷贝到堆内存。

源码:
private boolean read(SelectionKey k) {
        final SocketChannel ch = (SocketChannel) k.channel();
        final NioSocketChannel channel = (NioSocketChannel) k.attachment();

        final ReceiveBufferSizePredictor predictor =
            channel.getConfig().getReceiveBufferSizePredictor();
        final int predictedRecvBufSize = predictor.nextReceiveBufferSize();

        int ret = 0;
        int readBytes = 0;
        boolean failure = true;

        ByteBuffer bb = recvBufferPool.acquire(predictedRecvBufSize);
        try {
            while ((ret = ch.read(bb)) > 0) {
                readBytes += ret;
                if (!bb.hasRemaining()) {
                    break;
                }
            }
            failure = false;
        } catch (ClosedChannelException e) {
            // Can happen, and does not need a user attention.
        } catch (Throwable t) {
            fireExceptionCaught(channel, t);
        }

        if (readBytes > 0) {
            bb.flip();

            final ChannelBufferFactory bufferFactory =
                channel.getConfig().getBufferFactory();
            final ChannelBuffer buffer = bufferFactory.getBuffer(readBytes);
            buffer.setBytes(0, bb);
            buffer.writerIndex(readBytes);

            recvBufferPool.release(bb);

            // Update the predictor.
            predictor.previousReceiveBufferSize(readBytes);

            // Fire the event.
            fireMessageReceived(channel, buffer);
        } else {
            recvBufferPool.release(bb);
        }

        if (ret < 0 || failure) {
            k.cancel(); // Some JDK implementations run into an infinite loop without this.
            close(channel, succeededFuture(channel));
            return false;
        }

        return true;
    }




二.问题
1.最坏的情况下,这里direct缓存最大内存占用有多少?
底层用的是动态预计大小,
类:AdaptiveReceiveBufferSizePredictor

AdaptiveReceiveBufferSizePredictor最大限制是:6k。
也就是说direct dubbo用到最多6k*当前worker个数。

2.如果优化怎么优化?
在处理读请求时,最终还是会有一次direct缓存到heap堆的拷贝。
优化的话,这里要换成netty4,使用direct缓存池。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值