Netty与Java NIO APIs

本章包括

  • Netty的体系结构
  • 为什么我们需要非阻塞IO(NIO)
  • 阻塞IO与非阻塞IO
  • 基于JDK的NIO存在的问题与Netty的解决办法

本章虽然是Netty的介绍章节,但重点却是Java的NIO API,如果你是JVM网络方面的新手,本章是个不错的开始,同时对于经验丰富的Java开发人员,也是一个不错的复习机会。如果你对NIO与NIO2(AIO)比较熟悉了,在你本机运行Netty后,请自行跳过本章,从第二章开始深入到Netty的世界了。

     Netty是一个NIO的CS(client-server)模式框架,使用它可以快速容易的开发网络应用,如协议服务器与客户端。Netty为你开发网络应用提供了一个新的易上手的可扩展方案。它的实现基于对复杂操作的抽象,并提供给一个对业务逻辑处理与网络处理代码的解耦的易用API。由于Netty是为NIO而设计,故其所有的API都是异步的。

     通常,不管是基于Netty还是基于其他NIO API的网络应用都存在扩展性的问题。Netty的关键组成部分是它的异步性,这一章节主要讨论同步(阻塞blocking)与异步(非阻塞non-blocking)IO,以此来解释我们为何以及如何通过异步代码解决可扩展性的问题。

     对于以上这些网络中新的部分,本章将给出一个关于网络应用的一般性理解以及Netty如何提升网络应用的性能的,本章将解释如何使用基本的Java networking APIs构建应用程式,以及讨论它的优缺点,另外,本章也展示了Netty是如何解决Java的这些问题(如​​Epoll模型​​的BUG或内存泄露问题)的。

     在本章结尾处,你将会理解Netty是什么以及它能帮我们干什么,同时结合本书的其他章节你也将会再次对你工作中用到的Java的NIO以及异步程式获得足够深入的理解。

为何选择Netty

用David Wheeler的话来说"​​计算机科学中的所有问题都可以在另一个级别间接求解​​(all problems in computer science can be solved by anotherlevel of indirection)",作为一个NIO CS框架,Netty提供了一个类似的另一级别间接解决方案。Netty简化了TCP或UDP服务端的网络编程,但你依然可以通过较低级别的APIs访问,因为Netty提供了较高层次的抽象。

不是所有的网络框架都相似

Netty,"快且容易"并不意味着以牺牲应用的可维护性或性能为代价。来自于协议的实现经验(如FTP、SMTP、HTTP、WebSocket,SPDY以及各种二进制与基于文本的传统协议)引导了Netty的创作者们对Netty的设计非常小心,这也为Netty成功的提供了在可用性、性能、稳定性、灵活性上的兼顾。

     比较著名的公司以及开源组织包括RedHat、Twitter、Infinispan、HornetQ、Vert.x、Finagle、Akka、Apache  Cassandra、Elasticsearch等在使用Netty并为之做贡献。也可以公平的说Netty的一些特性正是这些项目所需的。过去的几年里,Netty变得越来越广为人之,同时它也是在一些盛行的开源非开源项目中应用最广泛的JVM网络框架之一。事实上,Netty在2011年荣获了"the  Duke_s  Choice  Award"奖。

     同样在2011年,Netty的作者Trustin Lee离开了RedHat加入了Twitter。基于这一点,Netty项目变成了一个独立的、任何公司都在努力为其做贡献的项目。RedHat与Twitter都在用Netty,所以,这两家公司理所当然的成为了Netty的最大贡献者,个人使用并贡献与Netty的数量还在增长。Netty的用户群体很活跃,Netty项目本身也是充满活力的。

Netty丰富的功能集

如果你按照本书的各章节操作练习时,你将会学到和用到很多Netty的功能。在下图的高亮部分(一些Netty支持的传输协议)给出了一个Netty架构的简要概述。


Figure 1.1 Overview of the model, transports, and protocols

 

除了它宽泛的传输协议,Netty也提供了在不同领域的优势(见下表1.1)

Table 1.1 Netty gives developers a full set of tools.


Development Area

 Netty Features

Design

  • Unified API for various transport types_blocking and non-blocking socket
  • Flexible to use
  • Simple but powerful thread-model
  • True connectionless datagram socket support
  • Chaining of logics to make reuse easy

Ease of Use

  • Well-documented Javadoc and plenty of examples provided
  •  No additional dependencies except JDK 1.6 (or above). Some features are supported only in Java 1.7 and above. Other features may have other  dependencies, but these are optional

Performance

  • Better throughput; lower latency than core Java APIs
  • Less resource consumption because of pooling and reuse
  • Minimized unnecessary memory copy

Robustness

  • No more OutOfMemoryError due to fast, slow, or overloaded connection.
  • No more unfair read/write ratio often found in a NIO application in high-speed networks

Security

  • Complete SSL/TLS and StartTLS support

 

  • Runs in a restricted environment such as Applet or OSGI

Community

  • Release early, release often
  • Active


 

除了上述列举出来的功能,Netty也附带解决了在Java NIO中已知的BUG或限制,因此,你无需在为此花费精力讨论。

    在你对Netty的功能有了一个概览后,是时候仔细研究下异步处理的机制了,NIO与Netty都是严重依赖异步编码的,如果不了解与此相关的选择方案,就很难超越它。在下一部分,我们会研究下为何我们需要异步API。

异步的设计

所有的Netty API都是异步的,异步式处理实际并不是新的东西;虽然这个方案的提出已经有一段时间了,但是在这段时间里,IO始终都是一个瓶颈,异步式处理一天比一天变得更重要。但它是如何工作的呢,又有哪些不同的模式可用呢?

    异步式处理鼓励你去更加有效的利用你的资源,首先你去开始一项任务,当任务结束的时候再去唤醒它继续执行,在这项任务的执行期间,你无须一直等待,你可以去做其他的事情。

    这部分将解释在利用异步API工作的时候的两个最常用的方法,也将讨论着两种技术的不同。

回调

回调是在异步处理中常用到的一项技术。回调就是传到方法B中一个方法A,在B执行完以后,执行A方法,你可能在javascript中认出了此种模式,没错,回调函数是这门语言的核心,下面的例子向我们展示了如何利用该项技术来获取数据。

Listing 1.1 Callback example 
public interface Fetcher {
void fetchData(FetchCallback callback);
}
public interface FetchCallback {
void onData(Data data);
void onError(Throwable cause);s
}
public class Worker {
public void doWork() {
Fetcher fetcher = ...
fetcher.fetchData(new FetchCallback() {
@Override
public void onData(Data data) { #1
System.out.println("Data received: " + data);
}
@Override
public void onError(Throwable cause) { #2
System.err.println("An error accour: " + cause.getMessage());
}
});
...
}
}s
#1 Call if data is fetched without error
#2 Call if error is received during fetch

(未完)