第一章节:(分三部分翻译)
本章节包括
java的网络编程
netty的介绍netty的核心组件
假如你正在为一个大型公司开发一个全新的具有历史意义的使命级的应用,在第一个会议上你了解到这个系统必须无损且高效地支持150000用户并发使用,所有人的眼光都注视着你,你打算怎么说?
如果你自信地说:“可以,没有问题”,所有人将会对你脱帽致敬,但是大多数的我们都会谨慎地去思考。比如去思考可执行性,接着你会拿着一台电脑,然后去搜索“高性能的java网络编程”
如果你今天搜索这样的关键词,大概下面的第一个结果就是你将看见的:
也许我们大多数人和你一样都是通过这种方式去第一次发现Netty的,你接下来可能去浏览网页,下载与Netty相关的代码,自信阅读Netty的java文档和一些相关的博客,然后一篇篇学习理解。如果你有扎实的网络编程经验,通过这样的方式你可能会有很好的学习体验,否则,情况也许就不是这么的乐观
为什么呢?高性能的系统(例如我们刚才举例的系统)需要我们掌握不止一种的一流一线技术,我们需要有以下几个复杂领域的经验技能:网络编程,多线程编程,和并发编程。这些编程不是很好彻底地掌握,但是Netty却将这几个领域的编程技巧很好的融合在一起方便大家的学习,即便你是一个网络编程的新手。但是直到现在,由于缺少综合的指导使得学习Netty的过程变得相对困难,直到这本书的出现。
我们写这本书的主要目的是:使Netty尽可能的传播给更多的开发者去使用,这些开发者包括一些有创新的的idea并且想将这些idea做成服务提供出来给大家使用,但是又没有时间或者说没有意向成为网络专家的开发者,如果这正好适合你,那么我们相信你现在已经非常期待开发你的第一个Netty应用了,另外,我们的初衷也有要支持一些有网络开发经验的专业人员,这些人员正在寻求一些工具去开发他们自定义的网络协议,Netty也提供了类似的服务支持
Netty的确提供了极其丰富的网络工具包,我们花费了我们绝大部分的时间去探索扩展它的性能,但是,Netty从本质上是一个框架,所以它的基本架构方法和设计原则与它的技术内容是一样重要的,因此,我们也会从以下几个方面去讲解:
分离概念(如果去从业务逻辑和网络逻辑层双重解耦)
模块化和可重用性
可测性作为第一需求
在接下来的第一个章节中,我们将以高性能的网络编程为背景,尤其关注以JDK实现的,在这个大背景下,我们将介绍netty,介绍netty的核心概念,并且构建模块,在这个章节的结束,你将有能力搭建你的第一个基于Netty的客户端和服务器端
1.1 Networking in Java
早些时候的开发者已经花费大量的时间去学些错综复杂的C语言的socket的库并且去处理这些在不同系统运行时的一些怪的现象,在最早的java版本上,介绍了基于面向对象的“虚假”的外观模式来隐藏一些更加棘手的内部网络编程的实现,但是却用更多类似陈词滥调的模板代码来创造了更加复杂的服务器/客户端协议
第一版本的java网络模块的APIs只支持通过原生态的系统套接字库来支持所谓的阻塞通信功能,以下代码清单展示了最为原始的服务器端的代码
上面的代码清单展示了一种最为基础的基于socketAPI的实现,我们分析这段代码最为重要的几点:
1) accpet()方法会一直阻塞直到有一个链接与ServerSocket建立连接,建立好连接后将返回一个socket来使客户端和服务器端进行通信,与此同时,serverSocket继续监听是否有新的连接请求
2)BufferedReader和PrintWriter来自于socket的输入输出流,BufferedReader从字符输入流中读取文本,PrintWriter将文本输出流打印成格式化后的对象展示格式
3)readLine()也会阻塞,直到读取到字符串被换行或者回车切断之前的部分(读取到一行数据才会返回)
4)对客户端的请求进行处理
这个代码清单一次只能对一次请求进行处理,如果想要管理多线程并发的客户端,你需要为每一个客户端的socket连接分配一个线程,如下图1.1所示
让我们思考一下通过上述方法实现的效果,首先,在任意的某一个时刻,很多的线程都会在休眠来等待即将出现的read/write事件处理,这很可能会浪费大量的资源,第二,每一个线程都需要分配一些栈内存空间,根据不同的操作系统划分的内存空间从64k到1M不等,即使JAVA虚拟机可以物理支持非常大的数量的线程数,如果你的连接数达到10000或者更高的时候,线程之间的上下文切换的开销会很大程度的限制性能,
也许这个方案可以支持小到中型的客户端并发连接,但是这个方案想要同时支持100000或者更多的连接还是有点不可能的的,幸运的是,这只是方案之一,还有一些很好的方案
1.1.1JAVA NIO
除了代码清单1.1显示的阻塞方式的系统通信调用,JAVA的原生库中很早也开始支持非阻塞的通信调用了,这种类型的调用被公认为可以更加容易的管控对网络资源的利用:
使用setsockopt()你可以配置sockets以致于在如果没有数据的时候read/write操作可以立即返回,如果阻塞模块就会在这里阻塞不会返回了
你可以使用系统的事件通知机制来注册一些非阻塞的sockets来确定这些注册的是否已经准备好读或者写了
JAVA 从2002年开始支持非阻塞的IO操作,包位于JDK1.4下的java.nio下
1.1.2Selectors
图1.2向我们展示如果真实的消除我们上个章节描述的阻塞IO所带来的缺点
java.nio.channels包下的Selector是JAVA NIO实现的关键,它利用事件通知的API来通知哪个或者哪些个非阻塞的socket已经准备好了读写,因为任何读写操作的完成状态可以在任何时刻可以被检查,只需要一个线程如上图1.2所示,可以很好的处理多线程的并发连接
总之,这个模型可以比阻塞IO模型提供更好的资源管控:
1)很多连接可以被少部分的线程处理管控,大大减少了由于内存管控或者线程之间上下文切换带来的性能损失
2)如果当前没有I/O要处理,线程可以重新定向到其他的任务
尽管很多应用直接构建与Java NIO之上,可以做一些正确的安全的应用,但是如果直接使用java NIO在高负载的情况下,想要应用达到可信赖高效的I/O分配,这将会是一个麻烦且易错的事情,所以最好交给高性能的网络专家Netty处理。