为什么选择 Netty 作为基础通信组件?

一、什么是 Netty?

Netty 是一个高性能 事件驱动、异步非堵塞的 IO (NIO) Java 开源框架,Jboss 提供,用于建立 TCP 等底层的连接,基于 Netty 可以建立高性能的 Http 服务器,快速开发高性能、高可靠性的网络服务器和客户端程序。支持 HTTP、 WebSocket 、Protobuf、 Binary TCP | 和 UDP,Netty 已经被很多高性能项目作为其 Socket 底层基础,如 HornetQ Infinispan Vert.x Play Framework Finangle 和 Cassandra。其竞争对手是:Apache MINA 和 Grizzly。

也就是说,Netty 是一个基于 NIO 的客户,服务器端编程框架,使用 Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty 相当简化和流线化了网络应用的编程开发过程,例如,TCP 和 UDP 的 socket 服务开发。

“快速” 和 “简单” 并不意味着会让你的最终应用产生维护性或性能上的问题。Netty 是一个吸收了多种协议的实现经验,这些协议包括 FTP,SMTP,HTTP,各种二进制,文本协议,并经过相当精心设计的项目,最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。

二、不选择 Java 原生 NIO 编程的原因

首先开发出高质量的 NIO 程序并不是一件简单的事情,除去 NIO 固有的复杂性和 BUG 不谈,作为一个 NIO 服务端,还需要能够处理网络的闪断、客户端的重复接入、客户端的安全认证、消息的编解码、半包读写等情况,如果你没有足够的 NIO 编程经验积累,一个 NIO 框架的稳定往往需要半年甚至更长的时间。更为糟糕的是,一旦在生产环境中发生问题,往往会导致跨节点的服务调用中断,严重的可能会导致整个集群环境都不可用,需要重启服务器,这种非正常停机会带来巨大的损失。

从可维护性角度看,由于 NIO 采用了异步非阻塞编程模型,而且是一个 I/O 线程处理多条链路,它的调试和跟踪非常麻烦,特别是生产环境中的问题,我们无法进行有效的调试和跟踪,往往只能靠一些日志来辅助分析,定位难度很大。

现在我们总结一下为什么不建议开发者直接使用 JDK 的 NIO 类库进行开发,具体原因如下。

1)跨平台与兼容性:NIO 算是底层的 APIs 需依赖系统的 IO APIs。但 Java NIO 发现在不同系统平台会出现问题。大量测试也耗不少时间;NIO2 只支持 JDK1.7+,而且没提供 DatagramSocket,故 NIO2 不支持 UDP 协议。而 Netty 提供统一接口,同一语句无论在 JDK6.X 还是 JDK7.X 都可运行,无需关心底层架构功能!

2)JAVA NIO 的 ByteBuffer 构造函数私有,无法扩展。Netty 提供了自己的 ByteBuffer 实现,通过简单 APIs 对其进行构造、使用和操作,一此解决 NIO 的一些限制。

3)NIO 对缓冲区的聚合与分散操作可能会导致内存泄漏。直到 JDK1.7 才解决此问题。

4)NIO 的类库和 API 繁杂,使用麻烦,你需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。

5)使用 JAVA NIO 需要具备其他的额外技能做铺垫,例如熟悉 Java 多线程编程。这是因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序。

6)可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等问题。

7)JDK NIO 的 BUG,例如臭名昭著的 epoll bug,它会导致 Selector 空轮询,最终导致 CPU 100%。官方声称在 JDK 1.6 版本的 update18 修复了该问题,但是直到 JDK 1.7 版本该问题仍旧存在,只不过该 BUG 发生概率降低了一些而已,它并没有得到根本性解决。该 BUG 以及与该 BUG 相关的问题单可以参见以下链接内容。

◎ http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6403933

◎ http://bugs.java.com/bugdatabase/view_bug.do?bug_id=2147719

异常堆栈如下。

java.lang.Thread.State: RUNNABLE

        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)

        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210)

        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65)

        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)

        - locked <0x0000000750928190> (a sun.nio.ch.Util$2)

        - locked <0x00000007509281a8> (a java.util.Collections$ UnmodifiableSet)

        - locked <0x0000000750946098> (a sun.nio.ch.EPollSelectorImpl)

        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)

        at net.spy.memcached.MemcachedConnection.handleIO(Memcached Connection.java:217)

        at net.spy.memcached.MemcachedConnection.run(MemcachedConnection. java:836)

由于上述原因,在大多数场景下,不建议大家直接使用 JDK 的 NIO 类库,除非你精通 NIO 编程或者有特殊的需求。在绝大多数的业务场景中,我们可以使用 NIO 框架 Netty 来进行 NIO 编程,它既可以作为客户端也可以作为服务端,同时支持 UDP 和异步文件传输,功能非常强大。

三、选择 Netty 作为基础通信框架的原因

Netty 是业界最流行的 NIO 框架之一,它的健壮性、功能、性能、可定制性和可扩展性在同类框架中都是首屈一指的,它已经得到成百上千的商用项目验证,例如 Hadoop 的 RPC 框架 Avro 就使用了 Netty 作为底层通信框架,其他还有业界主流的 RPC 框架,也使用 Netty 来构建高性能的异步通信能力。

通过对 Netty 的分析,我们将它的优点总结如下。

◎ API 使用简单,开发门槛低;

◎ 功能强大,预置了多种编解码功能,支持多种主流协议;

◎ 定制能力强,可以通过 ChannelHandler 对通信框架进行灵活地扩展;

◎ 性能高,通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优;

◎ 成熟、稳定,Netty 修复了已经发现的所有 JDK NIO BUG,业务开发人员不需要再为 NIO 的 BUG 而烦恼;

◎ 社区活跃,版本迭代周期短,发现的 BUG 可以被及时修复,同时,更多的新功能会加入;

◎ 经历了大规模的商业应用考验,质量得到验证。Netty 在互联网、大数据、网络游戏、企业应用、电信软件等众多行业已经得到了成功商用,证明它已经完全能够满足不同行业的商业应用了。

正是因为这些优点,Netty 逐渐成为了 Java NIO 编程的首选框架。

为什么选择 Netty 作为基础通信组件?

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章