Netty 4 减少了GC开销

Netty是一个开源Java NIO框架,它使创建高性能协议服务器变得更加容易。Netty v3的较旧版本使用Java对象表示I / O事件。这很简单,但是会产生很多垃圾,尤其是在规模比较大的情况下。在新的Netty 4发行版中,进行了更改,以便使用寿命长的通道对象上的方法代替短暂的事件对象来处理I / O事件。还有一个使用缓冲池的专用缓冲区分配器。

我们非常重视Netty项目的性能,可用性和可持续性,并且我们一直与Netty社区紧密合作以在各个方面进行改进。特别是,我们将讨论Netty 3的用法,并旨在说明为什么迁移到Netty 4可以提高我们的效率。

减少GC压力和内存带宽消耗

一个问题是Netty 3依赖JVM的内存管理来进行缓冲区分配。每当收到新消息或用户向远程对等方发送消息时,Netty 3都会创建一个新的堆缓冲区。这意味着每个新缓冲区都有一个“新字节[容量]”。这些缓冲区导致GC压力并消耗了内存带宽:为安全起见,分配新的字节数组会消耗内存带宽,以零填充该数组。但是,零填充字节数组很可能会用实际数据填充,从而消耗相同数量的内存带宽。如果Java虚拟机(JVM)提供了一种创建不一定要填充零的新字节数组的方法,那么我们可以将内存带宽的消耗减少到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) { ... }
	...
}

请注意,处理程序现在具有一个名为“ userEventTriggered ” 的方法,因此它不会失去定义自定义事件对象的能力。

缓冲池

Netty 4还引入了一个新接口“ ByteBufAllocator ”。现在,它通过该接口提供缓冲池实现,并且是jemalloc的纯Java变体,它实现了伙伴内存分配和平板分配。

现在,Netty具有自己的缓冲区内存分配器,它不会通过用零填充缓冲区来浪费内存带宽。但是,这种方法打开了另一种蠕虫病毒—引用计数。因为我们不能依靠GC将未使用的缓冲区放入池中,所以我们必须非常小心泄漏。即使是一个忘记释放缓冲区的处理程序,也可以使我们服务器的内存使用量无限增加。

进行如此大的改变值得吗?

由于上述更改,Netty 4与Netty 3没有向后兼容性。这意味着我们在Netty 3之上构建的项目以及其他社区项目必须花费大量的时间进行迁移。值得这样做吗?

我们比较了分别建立在Netty 3和Netty 4之上的两个回显协议服务器。(Echo很简单,因此创建的任何垃圾都是Netty的错,而不是协议的错)。我让它们通过16384个并发连接为相同的分布式回显协议客户端提供服务,这些连接重复发送256字节的随机有效负载,几乎使千兆以太网饱和。

根据我们的测试结果,Netty 4具有:

  • GC暂停频率降低5倍:45.5 vs 9.2次/分钟
  • 垃圾产生减少5倍:207.11 vs 41.81 MiB / s

我还想确保我们的缓冲池足够快。这是一个图形,其中X和Y轴分别表示每次分配的大小和分配单个缓冲区所花费的时间:

在这里插入图片描述

如您所见,随着缓冲区大小的增加,缓冲池比JVM快得多。对于直接缓冲区,它甚至更加引人注意。但是,对于小型堆缓冲区而言,它无法击败JVM,因此我们在这里需要进行一些工作。

展望未来

尽管我们的服务的某些部分已经成功地从Netty 3迁移到了Netty 4,但是我们正在逐步执行迁移。我们发现了一些阻碍我们采用该技术的障碍,希望在不久的将来解决:

  1. 缓冲区泄漏:Netty具有简单的泄漏报告功能,但没有提供足够详细的信息来轻松修复泄漏。
  2. 更简单的核心:Netty是一个社区驱动的项目,有许多利益相关者可以从更简单的核心代码集中受益。这增加了Netty核心的不稳定性,因为那些非核心功能往往会导致核心的附带变更。我们要确保只有真正的核心功能保留在核心中,而其他功能不在其中。
    我们还考虑添加更多更酷的功能,例如:
    • HTTP / 2实施
    • 客户端的HTTP和SOCKS代理支持
    • 异步DNS解析(请参阅请求请求)
    • 可通过JNI 直接与epoll一起使用的Linux本机扩展
    • 优先考虑具有严格响应时间限制的连接
发布了56 篇原创文章 · 获赞 14 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/feng_xiaoshi/article/details/103620838