Netty in Action:异步和事件驱动
本文翻译自《Netty in Action》第一章
本章包含
Java的网络编程
Netty初探
Netty的核心组件
假设你即将要为一个重要的大公司开发一个新的关键任务的应用程序。在第一次会议上,你了解到这个系统必须能无性能损耗地扩展到支持15万个并发用户。这时所有的人都看着你,你会说什么?
如果你自信地说“没问题”,那么真心佩服你!但是我们中的大部分人可能还是说得更谨慎些,比如“听起来是可行的”。然后,一旦回到电脑前,我们就会开始搜索“高性能Java网络编程(high performance Java networking)”。
如今,如果你搜索这个关键字,在第一页的结果中,你将会看到这个:
Netty: Home
netty.io/
Netty是一个用于快速开发可维护的高性能协议服务器和客户端的异步的事件驱动网络应用框架
如果你像很多人一样,通过这种方式发现了Netty,那么接下来你也许就会浏览官网,下载代码,研读Javadocs和一些博客,同时开始写代码。如果你已经有了扎实的网络编程经验,你也许会取得很大的进步;否则的话,可能不会。
为什么这么说呢?实现像上述例子中的高性能系统需要超一流的编程技术,需要诸多复杂领域的经验,包括网络,多线程和并发。Netty让甚至是网络编程的新手都可以运用到这些专业知识。但到目前为止,缺少一份全面的指导使得这个习得过程变得很困难,所以才有了本书。
我们写这本书的主要目的是使Netty被最大可能范围的开发人员所接受。这包括那些有创新内容和服务,但是既没有时间也无意成为网络专家的人。如果你也是其中一员,我们相信你会惊喜地发现你会很快地准备好创建你的第一个Netty应用。另一方面,我们致力于支持那些寻找开发私有网络协议工具的高级开发者。
事实上,Netty确实提供了一个非常丰富的网络开发工具包,我们会花大部分的时间来探索它的性能。但是Netty终究是一个框架,它的架构方法和设计原则同它的技术内容一样重要。因此,我们还将会提到这些:
- 关注点分离(解耦业务和网络逻辑)
- 模块化和可重用性
- 做为第一需求的易测性
在第一章,我们先介绍高性能网络的背景,特别是高性能网络在Java开发套件(JDK)中的实现。在这个背景下,我们会引入Netty,以及它的核心概念和组件。到本章的结尾,你将会准备好创建你的第一个基于Netty的客户端和服务器。
1.1 Java网络编程
早年就开始网络编程的开发者会花很多时间学习C语言socket库的繁复细节,还要对付在不同操作系统上可能突然出现的异常。早期的Java版本(1995-2002)引入了一个面向对象的外观,来掩盖这些棘手的细节,但同时也创建了一个复杂的需要很多样本文件代码(boilerplate code)的客户端/服务器协议(和为了让这个协议工作顺畅大量查阅底层细节的工作)。
那些第一代的Java APIs(java.net) 只支持本地系统的socket库提供的所谓阻塞函数。下面的代码清单是服务器端代码调用这些函数的例子:
代码清单 1.1 阻塞I/O举例

这个代码清单实现了一个基本的Socket API 模式。最重要的几个要点:
- accept() 一直阻塞直到ServerSocket上的连接建立完成(1), 然后返回一个新的socket,用于客户端和服务器直接的通讯。然后ServerSocket继续监听新的连接
- BufferedReader和PrintWriter是从这个Socket的输入输出流中导出的(2)。前者可以从一个字符输入流中读入文本,后者用于打印格式化的对象到一个文本输出流
- readLine()阻塞直到读到以换行或者回车结尾的字符串(3)
- 处理客户端请求(4)
这份代码一次只能处理一个连接。为了处理多个并发的客户端,你需要为每个新的客户端Socket分配一个新的线程,如图1.1所示
图1.1 使用阻塞I/O的多个连接

让我们考虑下这个方案可能带来的结果。首先,在任何时候,很多线程是休眠的状态,在等待输入输出数据。这可能是个资源的浪费。第二,每个线程,根据操作系统的不同,需要分配默认大小从64KB到1M不等的栈内存。第三, 即使一个Java虚拟机(JVM)物理上可以支持很大数量的线程,早在这个数量到达上限前,比如达到1万个连接的时候,上线文切换的开销就已经成了大问题。
虽然这个针对并发的方案对中小规模数量的客户端也许能接受,但支持1万或者更多并发连接所需的资源让这个方案远不够理想。幸运的是,我们还有其他选择。
1.1.1 Java NIO
除了列在代码清单1.1里的阻塞系统调用底层代码, 本地的socket库早就包含了非阻塞调用,这实现了对网络资源使用的更多控制。
- 用setsockopt()你可以配置sockets,如果没有数据,读/写调用会立即返回;原本,一个阻塞调用会一直阻塞;
- 你可以通过用系统的事件通知API注册一组非阻塞sockets,来判断是否这些sockets中的任何一个有待读取的数据
2002年,Java在JDK1.4的java.nio包中开始支持非阻塞I/O。
是“新(new)”还是“非(non)”阻塞?
NIO一开始是新输入/输出(New Input/Output)的缩略词,但是这个Java API已经存在了够长的事件,所以不再那么新了。目前大部分使用者把NIO看做非阻塞的简称,而阻塞I/O是OIO或者说老输入/输出(old Input/Output)。你也许还能碰到普通(plain) I/O的说法。
1.1.2 Selectors
This chapter requires login to view full content. You are viewing a preview.
Login to View Full Content