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

标签: twitter netty gc | 发表时间:2014-04-23 17:32 | 作者:
出处:http://it.deepinmind.com

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

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

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

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

减少GC压力以及内存带宽的开销

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

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

去除事件对象

Netty 4使用不同的方法来处理不同类型的事件,而不是直接创建事件对象。在Netty 3中, ChannelHandler使用一个单独的方法来处理所有的事件对象。

   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’方法,也就是说还可以通过它来创建一个自定义的事件对象。

缓冲区池

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

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

这么大的改动值得吗?

由于以上的这些改动,因此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轴分别代表的是每次分配的缓冲区的大小以及分配缓冲所需要的时间。

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

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

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

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

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

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

加入我们

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

鸣谢

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)。

原创文章转载请注明出处: Twitter:使用Netty 4来减少GC开销

英文原文链接

相关 [twitter netty gc] 推荐:

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

- - Java译站
在twitter,需要网络功能的核心模块使用的都是Netty. Finagle是我们的协议无关的RPC系统,它的传输层是在Netty之上构建的,许多内部的服务都是通过它来实现的,比如说搜索服务. TFE(Twitter Front End,Twitter前端)是我们专门的填鸭式反向代理,它使用Netty支撑了大部分面向公众的HTTP及SPDY的流量.

GC开销的降低--netty4在Twitter的使用

- - 开源软件 - ITeye博客
netty的founder Trustin Lee发布在Twitter上的一篇博客,非常好,直接转. protocol agnostic RPC system whose transport layer is built on top of Netty, and it is used to implement most services internally like.

HTTP/2 in Netty

- -
Here, we created a context for the server with a JDK SSL provider, added a couple of ciphers, and configured the Application-Layer Protocol Negotiation for HTTP/2..

Java GC 调优

- - Darktea
关于 Java GC 已经有很多好的文档了, 比如这些:. 但是这里还是想再重点整理一下 Java GC 日志的格式, 可以作为实战时的备忘录.. 同时也会再整理一下各种概念. 一, JDK 6 提供的各种垃圾收集器. 先整理一下各种垃圾收集器.. 新生代收集器: Serial, ParNew, Parallel Scavenge (MaxGCPauseMillis vs.

Netty系列之Netty高性能之道

- - CSDN博客推荐文章
最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用. 相比于传统基于Java序列化+BIO(同步阻塞IO)的通信框架,性能提升了8倍多. 事实上,我对这个数据并不感到惊讶,根据我5年多的NIO编程经验,通过选择合适的NIO框架,加上高性能的压缩二进制编解码技术,精心的设计Reactor线程模型,达到上述性能指标是完全有可能的.

Netty代码分析

- LightingMan - 淘宝JAVA中间件团队博客
Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序[官方定义],整体来看其包含了以下内容:1.提供了丰富的协议编解码支持,2.实现自有的buffer系统,减少复制所带来的消耗,3.整套channel的实现,4.基于事件的过程流转以及完整的网络事件响应与扩展,5.丰富的example.

[译]GC专家系列3-GC调优

- - SegmentFault 最新的文章
原文链接: http://www.cubrid.org/blog/dev-platform/how-to-tune-java-garbage-collection/. 本篇是”GC专家系列“的第三篇. 在第一篇 理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.

GC 日志分析

- - 码蜂笔记
不同的JVM及其选项会输出不同的日志. 生成下面日志使用的选项: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:d:/GClogs/tomcat6-gc.log. 最前面的数字 4.231 和 4.445 代表虚拟机启动以来的秒数.

初级分代GC

- - C++博客-首页原创精华区
通常情况下GC分为两种,分别是:扫描GC(Tracing GC)和引用计数GC(Reference counting GC). 其中扫描GC是比较常用的GC实现方法,其原理是:把正在使用的对象找出来,然后把未被使用的对象释放. 而引用计数GC则是对每个对象都添加一个计数器,引用增加一个计数器就加一,引用减少一个计数器就减一,当计数器减至零时,把对象回收释放.

Netty 5用户指南

- - 并发编程网 - ifeve.com
原文地址: http://netty.io/wiki/user-guide-for-5.x.html     译者:光辉勇士       校对:郭蕾. 现如今我们使用通用的应用程序或者类库来实现系统之间地互相访问,比如我们经常使用一个HTTP客户端来从web服务器上获取信息,或者通过web service来执行一个远程的调用.