在它之上构建的TCP / IP和HTTP天生就是事件驱动的。尽管提供了输入和输出管道的错觉,但在底层可以看到异步数据包的异步到达。与计算机科学中几乎所有的抽象一样,将网络堆栈作为阻塞的字节流处理变得有漏洞。特别是当我们想充分利用硬件的时候。
即使是在适度的负载下,传统的网络方法也很好。但是要在传统Java应用程序中扩展到前所未闻的限制,您必须应用响应式。虽然Netty是一个用于构建响应式、事件驱动的网络应用程序的出色框架,但它很少被直接使用。相反,它是大多数库和框架的一部分,包括RxNetty。RxNetty特别有趣,因为它将事件驱动的网络的力量与RxJava操作符的简单性结合在一起。我们仍然认为网络通信是一种消息流(信息包),但可以通过Observable< ByteBuf >进行抽象.
还记得我们在第165页的“Beating the C10k Problem”上如何定义的10,000个并发连接的问题吗?我们通过大量的Netty和RxNetty实现来解决这个问题。事实上,我们成功地实现了支持C50k的服务器,处理了50,000个并发的HTTP持久连接。通过使用更多的客户端硬件(因为服务器做的很好)和较少的频繁请求,同样的实现可以很容易地在C100k和更大的范围内生存——使用大约12行代码。
显然,实现HTTP的服务器部分(或任何其他协议;HTTP之所以被选中是因为它的普遍性),它只是故事的一个方面。同样重要的是服务器正在做什么,并且大多数情况下它会成为另一个服务器的客户机。在本章中,我们关注的是响应式的、非阻塞的HTTP服务器。这是合理的,但有这里有多个阻塞代码可能会潜入的地方。首先,我们对服务器端进行了大量的关注,但是我们完全忽略了客户端部分。但现代服务器,尤其是在大型分布式系统中,服务器也会作为客户端去调用其他服务器,请求并将数据推送到许多下游服务中。可以肯定的是,对流行搜索引擎的单个请求可以跨越数百甚至数千个下游组件,从而产生大量客户端请求。很明显,如果这些请求是阻塞和连续的,那么搜索引擎的响应时间将会非常缓慢。
无论我们如何完美地实现服务器的基础设施代码,如果它仍然需要处理阻塞的api,那么可伸缩性就会受到损害,就像我们的基准测试所显示的那样。在Java生态系统中有一些已知的阻塞的来源,我们将对此进行简要的探讨。