Twitter:使用Netty 4来减少GC开销

2024-01-26 06:08

本文主要是介绍Twitter:使用Netty 4来减少GC开销,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在twitter,需要网络功能的核心模块使用的都是Netty。
比方说:

[list]
[*]- [url=http://twitter.github.io/finagle/]Finagle[/url]是我们的协议无关的RPC系统,它的传输层是在Netty之上构建的,许多内部的服务都是通过它来实现的,比如说搜索服务。
[*]- TFE(Twitter Front End,Twitter前端)是我们专门的填鸭式反向代理,它使用Netty支撑了大部分面向公众的HTTP及SPDY的流量。
[*]- [url=https://github.com/twitter/cloudhopper-smpp]Cloudhopper[/url]每个月都通过Netty向世界各地的数百个移动运营商发送数十亿的短消息。
[/list]

可能有的人还没听说过Netty,它是一款开源的Java NIO框架,能让你更容易编写出高性能的网络服务器。前一个版本的Netty 3使用Java对象来表示IO事件。这样做比较简单,但会产生大量的垃圾,尤其是在我们这种规模下。最新的版本Netty 4中做了一些改进,短生命周期的事件对象已经不复存在了,而是通过生命周期较长的管道对象来处理IO事件。 同时还有一个专门的缓冲区分配器,它使用缓冲区池来进行实现。

我们非常关注Netty项目的性能,可用性以及可持续性,也和Netty社区紧密合作以便全方位的对它进行完善。这里我们特别会提到的是,我们是如何使用Netty 3的,以及项目移植到Netty 4后带来的性能提升。

[b]减少GC压力以及内存带宽的开销[/b]

Netty 3存在一个问题是,它依赖于JVM的内存管理来进行缓冲区的分配。只要有接收到新的消息或者是用户要发送远程消息,Netty 3就会在堆里分配一个缓冲区。也就是说每分配一个缓冲区就有一次new byte[capacity]操作。这些缓冲区给GC带来了压力,并且吞噬着内存的带宽:分配一个新的缓冲区,为了安全性需要给数组填0,而这会消耗内存带宽。然而填0的字节数组一般都会被真实的数据所填充,这同样也会消耗内存带宽。如果JVM可以提供一种方式来创建无需填充0的字节数组的话,就可以省掉50%的带宽了,不过现在还没有这样的办法。

为了解决这个问题,我们在Netty 4中做了如下的改进。

[b]去除事件对象[/b]

Netty 4使用不同的方法来处理不同类型的事件,而不是直接创建事件对象。在Netty 3中,[url=http://docs.jboss.org/netty/3.2/api/org/jboss/netty/channel/ChannelHandler.html]ChannelHandler[/url]使用一个单独的方法来处理所有的事件对象。


class Before implements ChannelUpstreamHandler { void handleUpstream(ctx, ChannelEvent e) {
if (e instanceof MessageEvent) { ... }
else if (e instanceof ChannelStateEvent) { ... }
...
}
}


而Netty 4会为每种事件类型分配一个处理方法:


class After implements ChannelInboundHandler { void channelActive(ctx) { ... }
void channelInactive(ctx) { ... }
void channelRead(ctx, msg) { ... }
void userEventTriggered(ctx, evt) { ... }
...
}


注意,这个handler还有一个‘userEventTriggered’方法,也就是说还可以通过它来创建一个自定义的事件对象。

[b]缓冲区池[/b]

Netty 4引入了一个新的接口,’ByteBufAllocator。它通过这个接口提供了一个缓冲池的实现,这是jemalloc的一个纯Java的实现版本,它提供了伙伴分配器以及SLAB分配器的实现。

既然Netty已经有了自己的缓冲区内存分配器,就不会再因为给缓冲区填0而造成内存带宽的浪费了。然而,这种方式又带来了另一件麻烦事——引用计数。因为我们不再依赖GC来将不用的缓冲区放回池里,我们得自己去注意内存泄露的问题。哪怕只有一个handler忘了释放缓冲区都会导致整个服务器的内存使用率无限制的增长。


[b]这么大的改动值得吗?[/b]

由于以上的这些改动,因此Netty 4不再向下兼容Netty 3。这也意味着我们用Netty 3构建的项目,必须花上相当一段时间来进行迁移。这样做值得吗?

我们比较了两个echo协议的服务器,分别用Netty 3和Netty 4进行开发(echo协议非 简单,因此如果产生了任何垃圾,那肯定是Netty造成的,而和协议无关)。我用分布式的echo协议客户端对它们进行访问,有16384个并发的连接,同时在不停地发送256字节的随机负荷包,几乎能让千兆以太网满负载地工作。

从我们的测试结果来看,Netty 4:

1. GC暂停的频率低了5倍:45.5 vs. 9.2 times/min
2. 产生垃圾的速度也降低了5倍:207.11 vs 41.81 MiB/s

我还想确认下缓冲区池的性能是不是也足够快。下图的X轴和Y轴分别代表的是每次分配的缓冲区的大小以及分配缓冲所需要的时间。

[img]http://t2.qpic.cn/mblogpic/3581876d57aa14cf2744/460[/img]

可以看到,随着缓冲区大小的增加,缓冲池的性能要比JVM更好。DirectBuffer的效果则更明显。然而,对于小的缓冲区而言,性能和JVM相比没有太大优势。这块我们还有不少工作要做。

尽管我们有些服务已经成功地迁移到Netty 4上了,但迁移是逐步来进行的,在这个过程中我们发现了一些问题,这阻碍了我们的迁移进度,希望在不久的将来这些问题能够得到解决:

1. 缓冲区泄露: Netty有一个简单的缓冲泄露报告机制,不过它提供的信息不够详细,难以迅速定位问题。

2. 核心功能足够简单:Netty是一个社区驱动的开源项目,因此核心功能越简单对使用者来说更好。而那些非核心的特性往往导致核心功能发生改动,这导致了Netty的核心功能的不稳定性。我们希望只有真正核心的特性才能留在核心库中,别的东西都通通闪开。

同时我们还希望能够加入以下的新特性:

- HTTP/2的实现
- 客户端HTTP和socks代理的支持
- 异步DNS解析
- Linux的本地扩展,这样可以直接通过JNI使用epoll
- 对连接划分优先级,确保严格的响应时间

[b]加入我们[/b]

Netty有意思的一点就是来自全世界的不同的人,不同的公司都在使用它,而不仅仅是twiiter。它是一个独立地健康发展的开源项目,有许多的贡献者。如果你也对构建“未来的网络编程”兴趣的话,为什么不访问下项目的主页,关注@netty_project,打开GitHub上的源代码,甚至考虑下参与到改进Netty的工作中来呢?

[b]鸣谢[/b]

Netty项目的创始人Trustin Lee,也在2011年加入我们,一起来进行Netty 4的完善工作。我们还要感谢来自TFE团队的Jeff Pinner,文中很多不错的想法都是他提出来的,并且他毫不犹豫的充当了我们的小白鼠。同样要感谢的还有,Norman Maurer,他是Netty代码的核心提交者之一,为了帮助我们把这些想法付诸实践,他付出了很多的努力 。还有许多人,他们积极体验了各种不稳定的版本,帮助我们试验了每一个改动的功能,这里没办法一一列举,里面特别要感谢的有:Berk Demir (@bd), Charles Yang (@cmyang), Evan Meagher (@evanm), Larry Hosken (@lahosken), Sonja Keserovic (@thesonjake), 以及Stu Hood (@stuhood)。


原创文章转载请注明出处:[url=http://it.deepinmind.com/gc/2014/04/23/netty-4-at-twitter-reduced-gc-overhead.html]http://it.deepinmind.com[/url]

[url=https://blog.twitter.com/2013/netty-4-at-twitter-reduced-gc-overhead]英文原文链接[/url]

这篇关于Twitter:使用Netty 4来减少GC开销的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/645863

相关文章

使用Python进行文件读写操作的基本方法

《使用Python进行文件读写操作的基本方法》今天的内容来介绍Python中进行文件读写操作的方法,这在学习Python时是必不可少的技术点,希望可以帮助到正在学习python的小伙伴,以下是Pyth... 目录一、文件读取:二、文件写入:三、文件追加:四、文件读写的二进制模式:五、使用 json 模块读写

Python使用qrcode库实现生成二维码的操作指南

《Python使用qrcode库实现生成二维码的操作指南》二维码是一种广泛使用的二维条码,因其高效的数据存储能力和易于扫描的特点,广泛应用于支付、身份验证、营销推广等领域,Pythonqrcode库是... 目录一、安装 python qrcode 库二、基本使用方法1. 生成简单二维码2. 生成带 Log

Python如何使用seleniumwire接管Chrome查看控制台中参数

《Python如何使用seleniumwire接管Chrome查看控制台中参数》文章介绍了如何使用Python的seleniumwire库来接管Chrome浏览器,并通过控制台查看接口参数,本文给大家... 1、cmd打开控制台,启动谷歌并制定端口号,找不到文件的加环境变量chrome.exe --rem

Oracle数据库使用 listagg去重删除重复数据的方法汇总

《Oracle数据库使用listagg去重删除重复数据的方法汇总》文章介绍了在Oracle数据库中使用LISTAGG和XMLAGG函数进行字符串聚合并去重的方法,包括去重聚合、使用XML解析和CLO... 目录案例表第一种:使用wm_concat() + distinct去重聚合第二种:使用listagg,

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

redis-cli命令行工具的使用小结

《redis-cli命令行工具的使用小结》redis-cli是Redis的命令行客户端,支持多种参数用于连接、操作和管理Redis数据库,本文给大家介绍redis-cli命令行工具的使用小结,感兴趣的... 目录基本连接参数基本连接方式连接远程服务器带密码连接操作与格式参数-r参数重复执行命令-i参数指定命

PyTorch使用教程之Tensor包详解

《PyTorch使用教程之Tensor包详解》这篇文章介绍了PyTorch中的张量(Tensor)数据结构,包括张量的数据类型、初始化、常用操作、属性等,张量是PyTorch框架中的核心数据结构,支持... 目录1、张量Tensor2、数据类型3、初始化(构造张量)4、常用操作5、常用属性5.1 存储(st

使用zabbix进行监控网络设备流量

《使用zabbix进行监控网络设备流量》这篇文章主要为大家详细介绍了如何使用zabbix进行监控网络设备流量,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录安装zabbix配置ENSP环境配置zabbix实行监控交换机测试一台liunx服务器,这里使用的为Ubuntu22.04(

使用Python将长图片分割为若干张小图片

《使用Python将长图片分割为若干张小图片》这篇文章主要为大家详细介绍了如何使用Python将长图片分割为若干张小图片,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果1. Python需求