首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

netty在大厂内部的优化实践

在查看源码的时候有个抽象的概念,抽象理解源码这块就类似鸡生蛋,蛋生鸡的问题,不看源码永远不知道这种写法,不知道这个原理也好像看不懂这个写法,就形成一个死循环,不懂reactor就很难看得懂netty,不懂netty就不知道reactor这种模式。需要在知识这块了解原始的积累。今天一起说说在实际开发过程中需要考虑的部分,那些影响性能,进而影响功能。这次说说腾讯邮箱的技术分享。邮箱不像咱们的http请求,属于自己的协议,内部使用netty的功能比较多,一起说说内部如何实践的。

(一)内部实践

整体服务架构

其中业务服务,推送服务等均是由netty实现,承担着各种长连接,高并发的业务服务。

推送服务架构

netty对象优化

这个优化和实际的功能就是无关,做什么功能都不要紧。

handler对象的复用。initChannel 每次链接建立后,new XDecoder() 和 new XHandller()多个对象,每个连接都对应一个pipeline,也就是pipeline 里面保留多个handler,复用的含义就是我不创建这个么对象行不行,其实不行的,因为handler不能做成功的handler,只能被一个连接所使用,

如果想共享的话,需要在handler上边增加一个标记 @ChannelHandler.Sharable

一个netty EventLoop 每次都创建一个Handler,共享后连接1 和 连接2都指向同一个handler,就会产生一个问题,就是多线程的问题,多个请求多个线程,同时调用handler的时候,共享变量的情况就会导致线程安全的问题,handler要共享数据,如果优化了一定要注意共享变量不要产生线程安全问题。目前的XHandller 满足共享,不存在线程安全。

XDecoder 里面就存在线程不安全的因素, ByteBuf tempMsg = Unpooled.buffer(); 共享变量的存在。一般来说共享是没有问题的,但是编解码器一般不会共享,在编解码器里面ByteToMessageDecoder 父类里面有个ensureNotSharable() 告诉就不能进行共享。

处理过程中,线程的调度问题

在整个程序运行过程中,存在好几个块。

Accept 接收, reactor进行读取work线程。

handler耗时操作是在数据库,导致IO线程堵塞所有都被积压。、

为耗时操作交给指定的线程池。

增加了线程池,addLast增加了线程池

响应内容,必须经过netty的IO线程

客户端拉取邮箱服务器,邮箱服务器通过线程异步进行调用,线程异步处理一下拉取1000条邮件,达到了更大的数据量了,转交给客户端的时候,IO线程(CPU的两倍),最好的状态是少量的线程处理大量的请求,但是会写给客户端的时候量比较大的时候,反馈响应也存在数据量太大的问题。响应如果通过netty 就会存在IO阻塞问题。

5000个分成每个500个,桥窄大车过去就堵塞了,把货都放在一辆辆小车上过去,就可以轻松从桥上通过了。

netty 源码中,会判断如果涉及到iO,自动切换成WriteTask,任务类型来进行处理。

调大操作系统的tcp网络缓存

业务处理,将大数据的write,多次小数据write

一般为了稳定,基本会保留40~50的资源空闲。QPS3000-5000 机器适度就可以。

ByteBuf 复用机制

如果注释了XHandller中,((ByteBuf) msg).release(); // 引用计数减一。

NIOEventLoop 收到 selector 通知,然后进入read环境,申请一个Bytebuf对象,作为数据的载体,最后转变handler进行业务处理。连接如果比较多,每次都申请一个Bytebuf对象,有一种情况1万个连接,就创建500个对象。500个对象可能到501的时候,突然变成1了,这就是JVM的GC机制,内存的损耗交给了GC,引发各种的GC,GC线程的压力,GC最大的问题STW(Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)),并不是优化没有效果,GC会导致程序的不稳定,并发量在大点,1万个对象都有可能,这样会频繁的GC。为什么每次都要重复生成一个对象呢。((ByteBuf) msg).release(); // 引用计数减一。同一个地址多次调用使用同一个ByteBuf。

里面用到了计数器,直接这样理解吧,同一个地址的ByteBuf直接放入到一个堆里面,同一个地址直接取同一个ByteBuf,需要在((ByteBuf) msg).release();

(二)提高请求、推送的吞吐量

业务操作提交到单独的线程执行(防止IO线程的阻塞)。

调整TCP缓冲区大小,提高网络吞吐量。

基于Netty框架开发时,业务代码逻辑的调优。

结合Netty框架特点,复用对象,实现性能提升。

(三)总结

并发连接主要靠操作系统参数调优。

吞吐量的提示,主要靠代码处理能力来提升。

有时候网络和磁盘会成为瓶颈(10M和100M差距很大)。

水平扩展,集群的方式是最终方案。

Netty的运作机制很重要(多路复用至关重要reactor)。

PS:底层原理的优化,体现在对源码的理解,系统参数决定了你执行的情况,操作系统是对外的平台,平台慢了话,netty程序,java程序在快是没用的。系统的参数调整后,netty也需要进行优化,下一步就是JAVA程序调优。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20210106A0DY2M00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券