Nginx_OpenResty理论基础

Nginx是一个高性能的HTTP和反向代理服务器,生产场景中几乎都会用到,利用Nginx+Lua可以实现高并发编程。

Nginx有以下三个社区分支:

Nginx官方:更新迭代快,提供免费版和商业版。Tengine:是由淘宝网发起的Web服务器项目。在Nginx的基础上针对大访问量网站的需求添加了很多高级的特性和功能。OpenResty:将LuaJIT VM 嵌入Nginx中,实现了OpenResty这个高性能服务端解决方案。OpenResty是一个基于Nginx和Lua的高性能Web平台,其内部集成了大量精良的Lua库,第三方模块以及大多数的依赖项,用于方便地搭建能够处理超高并发、扩展性极高的动态Web应用、Web服务和动态网关。

OpenResty的目标是让Web服务直接跑在Nginx服务内部,充分利用Nginx的非阻塞I/O模型,不仅对HTTP客户端请求,甚至对远程后端(MySQL、PostgreSQL、memcached及Redis)都进行一致的高性能响应。

OpenResty通过汇聚各种设计精良的Nginx模块,从而将Nginx有效地变成了强大的通用Web应用平台,使得Web开发工程师和系统开发工程师可以使用Lua脚本语言调用Nginx支持的各种C及Lua模块,快速构造出足以胜任10KB乃至1000KB以上单机并发连接的高性能Web应用系统。

正向代理与反向代理

两者都是代理服务中,进行客户端请求的转发

正向代理

客户端非常明确要访问的服务器地址,客户端需要配置目标服务器的IP和端口信息。一般来说,正向代理服务器是一台和客户端网络连通的局域网内部的机器或者是可以打通两个隔离网络的双网卡机器。通过正向代理,客户端的HTTP请求可以转发到之前与客户端网络不通的其他不同的目标服务器。

正向代理的主要场景是客户端。由于网络不通等物理原因,需要通过正向代理服务器这种中间转发环节顺利访问目标服务器,也可以通过正向代理服务器对客户端某些信息进行一些伪装和改变。

反向代理

客户端不知道目标服务器的信息,代理服务器就像是原始的目标服务器,客户端不需要进行任何特别的设置。反向代理最大的特点是客户端不知道目标服务器地址。

反向代理的主要场景是服务端。服务提供方可以通过反向代理服务器轻松实现目标服务器的动态切换,实现多目标服务器的负载均衡等。

总结

正向代理(如Squid、Proxy)是对客户端的伪装,隐藏了客户端的IP、头部或者其他信息,服务器得到的伪装过的客户端信息;

反向代理(如Nginx、Apache)是对目标服务器的伪装,隐藏了目标服务器的IP、头部或者其他信息,客户端得到的是伪装过的目标服务器信息。

Nginx核心原理

Nginx的核心原理包括Reactor模型、Nginx的模块化设计、Nginx的请求处理阶段。

Reactor模型

Nginx对高并发IO的处理使用了Reactor事件驱动模型。该模型的基本组件有

事件收集器:负责收集worker进程的各种I/O请求。事件发送器:负责将IO事件发送到事件处理器。事件处理器:负责各种事件的响应工作。

Reactor模型的核心思想是:基于操作系统提供的多路I/O复用技术,将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上,一旦有I/O事件到来或者准备就绪(文件描述符或Socket可读、写),多路复用器返回并将事先注册的相应I/O事件分发到对应的处理器中。

Nginx的两类进程

Nginx启动后会以daemon方式在后台运行,其后台有两类进程:

Master进程:又叫管理者进程。主要负责调度Worker工作进程,比如加载配置文件、启动工作进程、接收来自外界的信号、向各Worker进程发送信号、监控Worker进程的运行状态等。同时负责创建监听套接字接口,交由Worker进程进行监听。Worker进程:又叫工作进程。主要用来处理网络事件,当一个Worker进程在接收一个连接通道后,就开始读取请求、解析请求、处理请求,处理完成产生数据后,再返回给客户端,最后断开连接通道。各Worker进程之间是对等且独立的,它们同等竞争来自客户端的请求,一个请求只能在一个Worker进程中处理。

Nginx的模块化设计

高度模块化的设计是Nginx的架构基础。各模块之间严格遵循“高内聚、低耦合”的原则。在Nginx实现中,一个模块包含一系列命令(cmd)和这些命令相对应的处理函数(cmd->handler)。Nginx的Worker进程在执行过程中会通过配置文件的配置指令定位到对应的功能模块的某个命令,然后调用命令对应的处理函数来完成相应的处理。

Nginx的模块结构图:

Nginx的主要模块说明:

Core核心模块:是Nginx服务器正常运行必不可少的模块,提供错误日志记录、配置文件解析、Reactor事件驱动机制、进程管理等核心功能。标准HTTP模块:主要提供HTTP协议解析相关的功能,比如端口配置、网页编码设置、HTTP响应头设置等。可选HTTP模块:主要用于扩展标准的HTTP功能,让Nginx能处理一些特殊的服务,如Flash多媒体传输、网络传输压缩、安全协议SSL的支持。邮件服务模块:主要用于支持Nginx的邮件服务,包括对POP3协议、IMAP协议和SMTP协议的支持。第三方模块:为了扩展Nginx服务器功能,定制开发的,如JSON支持、Lua支持。

Nginx配置文件上下文结构

Nginx配置文件包含若干配置项,每个配置项由配置指令和指令参数两部分组成。

Nginx的配置项的具体功能与其所处的作用域(上下文、配置块)是强相关的。Nginx指令的作用域配置块大致分为5种,其层次关系如图:

Nginx指令的作用域

一个标准的Nginx基本配置文件nginx.conf上下文结构如下:

... #main全局配置快,可配置工作进程数 events { #事件处理模型配置块,例如IO读写模型、连接数等 ... } http { #http协议配置块 ... #http协议的全局配置快 server { #虚拟服务配置块1 location [PATTERN] { #路由规则配置快1 ... } location [PATTERN] { #路由规则配置快2 ... } } server { #虚拟服务配置块2 ... } ... #其他http协议的全局配置 } main { #mail服务配置块 ... #email相关协议,如SMTP、IMAP、POP3等 }

除了基本配置文件外,Nginx还有如下配置文件:

mime.type:与MIME类型相关的配置文件fastcgi.conf:与FastCGI相关的配置文件。proxy.conf:与Proxy相关的配置文件。sites.conf:单独配置Nginx的提供的虚拟主机。

Nginx的请求处理流程

Nginx中HTTP请求的处理流程可以分为4步:

读取解析请求行;读取解析请求头;多阶段处理,即执行handle处理器列表;将结果返回给客户端

Nginx把HTTP请求处理划分成了11个阶段,在完成前两步处理后,Nginx将整个请求封装到一个请求结构体ngx_http_request_t实例中,该实例相当于一个Java对象,然后进入多阶段处理,执行handler处理器列表,列表中的每个handler处理器都会对请求对象进行处理,类似于Netty中的PipeLine。

Nginx在进行多阶段处理时,handler处理器的执行次序除了和配置文件中对应指令的配置顺序相关外,还和指令所处的阶段先后次序相关。如下展示了多阶段处理的顺序:

Nginx多阶段处理顺序

HTTP请求处理的11个阶段

post-read阶段:标准模块的ngx_realip处理器就注册在该阶段。改写请求的来源地址,将客户端真实IP附加上,便于后端获取。server-rewrite阶段:server块中请求地址重写阶段。find-config阶段:配置查找阶段,根据请求URL地址去匹配location路由表达式。不支持Nginx模块注册处理程序。rewrite阶段:请求地址重写阶段,此时,location配置块中的指令开始起作用,如breaker、if、return、rewrite、set等,三方库ngx_lua模块中的set_by_lua、rewrite_by_lua指令也注册在此阶段.post-rewrite阶段:请求地址URI重写后提交阶段,防止递归修改URI造成死循环。不支持Nginx模块注册处理程序。preaccess阶段:访问权限检查准备阶段。access阶段:访问权限检查阶段。注册在该阶段的指令有ngx_http_access_module模块的指令、三方ngx_auth_request模块的指令、ngx_lua模块的指令。特别提醒:echo指令用于返回内容,在location上下文中,该指令注册在content阶段,access阶段不执行该指令的配置项。post-access阶段:访问权限检查提交阶段。如果请求不被允许访问Nginx服务器,该阶段负责向用户返回错误响应。不支持Nginx模块注册处理程序。在access阶段可能存在多个访问控制模块的指令注册,该阶段的satisfy指令可以用于控制它们的协作方式:a、逻辑或操作:satisfy any。表示访问控制模块A、B、C或更多,只要其中任意一个通过就算通过。b、逻辑与操作:satisfy all。表示访问控制模块A、B、C或更多,全部通过才算通过。try-files阶段:可以使请求按顺序访问多个静态文件资源,直到某个静态文件资源符合条件。不支持Nginx模块注册处理程序。content阶段:内容产生阶段,大部分HTTP模块会介入。Nginx的echo、ngx_lua中的content_by_lua指令就注册在该阶段log阶段:日志记录模块。

Nginx将一个HTTP请求分为11个处理阶段,这样做让每个HTTP模块可以只专注于完成一个独立、简单的功能。而一个请求的完整处理过程由多个HTTP模块共同完成,可以极大地提高多个模块合作的协同性、可测试性和可扩展性。

同一个阶段内的指令,Nginx会按照各个指令的上下文顺序执行对应的handle处理器方法。