文档章节

dubbo中的那些“坑"(3)-netty4-rpc网络接口中的高并发的bug

阿阮
 阿阮
发布于 2014/12/02 23:13
字数 673
阅读 9435
收藏 12

在几个月前改造dubbo时,netty4已经稳定很久了,一时手痒,按照netty3-rpc的源码克隆了一套netty4,在修正了大量的包、类型不同之后,基本保持了netty3的风格,并发量小或者数据包很小时,一切都很ok, 在进行大并发测试时,结果和netty3完全不同,基本用惨不忍睹来形容。由于当时急于开发php客户端,就把netty4-rpc当做一个失败的组件存档起来, 前几天php-dubbo开发基本完成之后,返回过来思考netty4-rpc的问题,经过仔细分析数据包的解析过程,单步跟踪源码

NettyCodecAdapter, TelnetCodec, ExchangeCoedec,发现ByteBuf的缓冲区为1024,当数据超过1024时,会调用多次Decoder.messageReceived函数,第一次分析dubbo的协议头时,是正确的,第二次之后数据就错误了,然后dubbo内部缓冲区的数据越来越长,但是仍然分析不到一个完整的dubbo数据包

因此去看netty4的源码,发现AbstractNioByteChannel中有网络数据接收的代码时这么处理ByteBuf的

  ByteBuf byteBuf = null;
            int messages = 0;
            boolean close = false;
            try {
                int totalReadAmount = 0;
                boolean readPendingReset = false;
                do {
                    byteBuf = allocHandle.allocate(allocator);
                    int writable = byteBuf.writableBytes();
                    int localReadAmount = doReadBytes(byteBuf);
                    if (localReadAmount <= 0) {
                        // not was read release the buffer
                        byteBuf.release();
                        close = localReadAmount < 0;
                        break;
                    }
                    if (!readPendingReset) {
                        readPendingReset = true;
                        setReadPending(false);
                    }
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;

看见没,内核是需要ByteBuf.release的,继续通过byteBuf的一个实现PooledByteBuf分析源码,原来是实现了一个基于简单计数应用计数的循环使用的缓冲区,一旦计数变为1,该缓冲区被归还到netty4内核,被后面的数据读取线程重新使用

而我们InternalDecoder的代码为

      message = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.wrappedBuffer(
                    input.toByteBuffer());

直接引用了ByteBuf.toByteBuffer,继续查看源码UnpooledHeapByteBuf, 其toByteBuffer实际是对内部数据的

一个nio封装而已,因此,使用上述函数时,导致dubbo的decode保存了一个某一个ByteBuffer的内部数据,但是虽有该

buffer被归还到netty4缓冲区中被循环引用,下一次可能被其他读写线程重新改写数据,因此,高并发下当缓冲区被重复使用时,bytebuf将由于计数问题不断被使用,而解码器中缺傻傻等待。

解决方案

1.通过byteBuf的retain和release函数保证计数的有效性,通过程序例外或者缓冲区被使用完成时候归还ByteBuf到netty4内核

2.拷贝数据到dubbo的缓冲区中

思考:

netty3 是否也有该问题呢???










© 著作权归作者所有

阿阮
粉丝 36
博文 11
码字总数 3987
作品 0
海淀
架构师
私信 提问
加载中

评论(10)

迪伦少校
迪伦少校
即便是ByteBuf的循环使用,有volatile计数也不会有问题吧,还有这个“当数据超过1024时,会调用多次Decoder.messageReceived函数”确定是这样?debug?
小王爷威武
查看了一下代码,版本是netty-all.4.0.18
跟楼主说的有点出入:
UnpooledHeapByteBuf方法是没有toByteBuffer方法的。
只有nioBuffer方法,而该方法是将array数组转换为ByteBuffer对象。
照理说应该没有问题的,因为array数组在使用中并不可变的。

如果是PooledUnsafeDirectByteBuf实现,那么就是有问题的,调用nioBuffer方法的时候,实际上还持有对原有ByteBuffer的引用。
Quinton
Quinton
还有, php客户端构建一个数组,调用后端java的服务,有没有出现乱码的问题呢,怎么解决的呢
Quinton
Quinton
php+dubbo中, 我们是将后端java提供http服务,php客户端通过url即 http://nginx-host/order直连到服务端, nginx-host是前置负载均衡机, 通过它代理到后端具体某台服务。 请问你们的做法是什么呢?
scott-zhai
scott-zhai
netty4相对于netty3线程模型改了 不能简单的改包名吧
ccvcd
ccvcd
能否给出具体的代码呢?不了解netty谢谢。
阿阮
阿阮 博主

引用来自“daishuli”的评论

请问,php做客户端时,是通过url直连的么?还需要消费者么?
定制的客户端访问接口
daishuli
daishuli
请问,php做客户端时,是通过url直连的么?还需要消费者么?
阿阮
阿阮 博主
有点大,包名变了,一些类名也改了
softdawn
softdawn
兄弟 升级  dubbo中 netty到4.x版本,用于生产环境了么,改动大不?
dubbo通信消息解析过程分析(1)

由于rpc底层涉及网络编程接口,线程模型,网络数据结构,服务协议,细到字节的处理。牵涉内容较多,今天就先从一个点说起。 说说,dubbo通过netty框架做传输层,从接到数据字节流到把字节转换...

wannshan
2018/01/19
293
0
小白科普:Netty有什么用?

随着移动互联网的爆发性增长,小明公司的电子商务系统访问量越来越大,由于现有系统是个单体的巨型应用,已经无法满足海量的并发请求,拆分势在必行。 在微服务的大潮之中, 架构师小明把系统...

bjweimengshu
2017/12/12
0
0
dubbo中的那些“坑”(1) - 关于MINA传输协议的bug定位及修复

同事刘阳使用dubbo服务器中配置mina作为网络传输层,发现大并发情况下,解码发生如下异常 014-12-01 18:00:44,652 [DubboServerHandler-10.1.19.13:20880-thread-164] WARN alibaba.dubbo.r...

阿阮
2014/12/02
13.6K
0
Motan 1.1.1 发布,微博开源的高性能分布式 RPC 框架

Motan 1.1.1 已发布,更新内容如下: 功能改进 support multi serialize in simpleSerialization #635 support more data type in simpleSerialization #683 add rpc common client #682 #7......

王练
2018/05/19
1K
8
高并发架构系列:如何从0到1设计一个类Dubbo的RPC框架

在过去持续分享的几十期阿里Java面试题中,几乎每次都会问到Dubbo相关问题,比如:“如何从0到1设计一个Dubbo的RPC框架”,这个问题主要考察以下几个方面: 你对RPC框架的底层原理掌握程度。...

mikechen优知
01/22
54
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Boot + Mybatis + Ehcache 二级缓存实例

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕...

xiaolyuh
6分钟前
1
0
Spring源码学习(二)哎呦,按菜谱做菜与AbstractAutowireCapableBeanFactory.createBean流程差不多

记得跟老婆谈恋爱时,有一天心血来潮给老婆做饭,按照菜谱一步一步的做,结果差点把厨房烧了!!! 这事至今老婆还记得。 入口 上一篇说了,AbstractBeanFactory.getBean的主流程 ,今天来说下...

温安适
8分钟前
15
0
前端UI攻城狮 你们该抛弃jQuery了

你不再需要jQuery! Web工程师太依赖jQuery了,某种意义上说jQuery已经成了JavaScript的同义词。但是我们真的需要他么?或许我们应该反思一下什么时候才真的需要jQuery。 对我个人而言开始使...

前端老手
10分钟前
1
0
六、Java设计模式之工厂方法

工厂方法定义: 定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行 类型:创建型 工厂方法-使用场景: 创建对象需要大量重复的代码 ...

东风破2019
52分钟前
5
0
win服务器管理遇到的一系列问题记录

有些小伙伴在使用iis7远程桌面管理工具的时候总是会遇到一系列的问题,下面就是为大家介绍一下服务器日常管理过程中出现的问题及我的解决办法和心得。希望能帮到大家。   拒绝服务器重新启...

1717197346
59分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部