深入理解Nginx ——模块开发与架构解析
当我试图在产品的关键位置设计一个高性能Web服务器时,我选择使用成熟的Nginx。选择它的理由为:首先,它对服务器性能上的挖掘已经达到了很高水平,它能尽量使不同的硬件(包括网卡、硬盘、不同的CPU核心)并发运行,同时软件中又没有阻塞进程使之睡眠的代码,从性能上来说,它可以挑战任何服务器。其次,完全基于事件驱动的服务器开发效率往往很不理想,它们要处理的事件过于底层化、细节化,这使得各功能模块无法聚焦于业务,最终产品的功能都较为单一,不会有丰富的可选功能。但Nginx却不然,由于它在软件架构上具有优秀的设计,使得Nginx完全由许多简单的模块构成,各模块(特别是HTTP模块)不用介入底层细节,在尽享分阶段、无阻塞的事件驱动架构下,可以专注于业务功能的实现,这样最终为Nginx带来了大量的官方、第三方的功能模块,使得功能同样强大的Nginx在产品核心位置上足以担当重任,经受住海量请求的考验。
当Nginx已有模块提供的功能不能完全实现我的所有业务需求时,我可以在Nginx的后端再搭建一个实现了缺失功能的非Nginx服务器,将Nginx无法实现的请求反向代理到这台服务器上处理。但这样也有一定的弊端,首先增大了处理请求的开销,其次后端服务器的设计仍然制约着总体性能(它依然需要解决Nginx解决过的无阻塞问题,那样才能像Nginx一样高效),这样做仅适用于对性能要求不高的场景。唯有开发一个实现了所需功能的自定义Nginx模块嵌入到Nginx代码中,才能让自己的业务像Nginx一样充分挖掘服务器的硬件资源,及时地响应百万级别的并发TCP连接。
当我在开发Nginx模块之前,试图在市面上找到一本关于Nginx模块开发的书籍(无论是中文还是英文)时却一无所获。我只能找到如何使用Nginx及其已有模块的书籍。为了开发Nginx模块,我只能通过阅读Nginx极度缺少注释的源代码,并分析各种官方Nginx模块来逐步还原其设计思想,反复尝试、验证着怎样的模块能够使用Nginx的基础架构,和丰富的跨平台工具方法,同时符合Nginx设计思想,使Nginx拥有媲美Linux内核的一流效率。这个过程耗费了我很多的精力,因此,我希望今后的Nginx使用者、开发者在遇到同样的问题时,不至于还要很痛苦地阅读源代码来找到模块开发方法,而是简单地按照章节查阅本书,就可以快速找到怎样简单、高效地开发Nginx模块,把精力放在业务的实现上。这是我写这本书的第一个目的。
当我们产品中运行的Nginx出现了问题时,往往是通过找到错误的配置项、使用方式来解决的,这样也的确能够修复大部分问题。但是更深层次的问题,或者是使用场景比较偏僻,抑或是Nginx自身代码考虑得不够全面时,这些问题往往只能由那些花费大量精力研究Nginx源代码的工程师来解决。我写作本书的第二个目的是希望通过透彻地解析Nginx架构,帮助读者深入理解Nginx,既能够正确地使用它,也能在它出现任何问题时找到根本原因,进而用最合适的方法修复或者回避问题。
Nginx是一个优秀的事件驱动框架,虽然它在HTTP的处理上非常出色,但它绝不仅仅用于Web服务器。Nginx非常适合开发在传输层以TCP对外提供服务的服务器程序。基于Nginx框架开发程序有5个优势:
1)Nginx将网络、磁盘及定时器等异步事件的驱动都做了非常好的封装,基于它开发将可以忽略这些事件处理的细节。
2)Nginx封装了许多平台无关的接口、容器,适用于跨平台开发。
3)优秀的模块化设计,使得开发者可以轻易地复用各种已有的模块,其中既包括基本的读取配置、记录日志等模块,也包括处理请求的诸如HTTP、mail等高级功能模块。
4)Nginx是作为服务器来设计其框架的,因此,它在服务器进程的管理上相当出色,基于它开发服务器程序可以轻松地实现程序的动态升级,子进程的监控、管理,配置项的动态修改生效等。
5)Nginx充分考虑到各操作系统所擅长的“绝活”,能够使用特殊的系统调用更高效地完成任务时,绝不会去使用低效的通用接口。尤其对于Linux操作系统,Nginx不遗余力地做了大量优化。
当我们期望编写一款能够以低负载处理高并发请求并且主要处理基于TCP的服务器程序时,推荐选择Nginx,它可能会带给我们意外的惊喜。这本书的第三部分,将通过分析Nginx的内部架构,帮助读者了解怎样基于Nginx开发高效的TCP服务器程序:通过开发一种新的模块类型,实现一种新的功能框架来提供极佳的扩展性,使得功能子模块仅关注于业务的开发,忽视底层事件的处理。这是我写作本书的第三个目的。
除了这3个主要目的外,我还希望通过这本书向大家展示Nginx在服务器开发上的许多巧妙设计,它们或在抽象设计上精妙,或通过操作系统精确、节省地使用硬件资源,这些细节之处的设计都体现了Igor Sysoev的不凡功底。即使我们完全不使用Nginx,学习这些技巧也将有助于我们服务器编程水平的提升。