如何做阿里巴巴网站,手机qq浏览器网页安全防护怎么关,建设培训学校网站,企业网站制作的书一、Nginx核心原理 本节为大家介绍Nginx的核心原理,包含Reactor模型、Nginx的模块化设计、Nginx的请求处理阶段.
#xff08;本文源自微博客,且已获得授权#xff09;
1.1、Reactor模型 Nginx对高并发IO的处理使用了Reactor事件驱动模型。Reactor模型的基本组件包含时间收集…一、Nginx核心原理 本节为大家介绍Nginx的核心原理,包含Reactor模型、Nginx的模块化设计、Nginx的请求处理阶段.
本文源自微博客,且已获得授权
1.1、Reactor模型 Nginx对高并发IO的处理使用了Reactor事件驱动模型。Reactor模型的基本组件包含时间收集器、事件发送器、事件处理器3个基本单元其核心实现四将所有要处理的I/O事件注册到一个中心I/O多路复用器上同时主线程/进程阻塞在多路复用器上一旦有I/O时间到来或者准备就绪文件描述符或Socket可读、写多路复用器返回并将事先注册的响应I/O事件分发到对应的处理器中。 在Reactor模式中时间收集器、事件发送器、事件处理器则3个基本单元的职责分别如下
时间收集器负责收集Worker进程的各种I/O请求事件发送器负责将I/O时间发送到事件处理器事件处理器负责各种时间的响应工作
Nginx的Reactor模型的设计大致如下 时间收集器将各个连接通道的IO事件放入一个待处理时间列通过事件发送器发送给对应的事件处理器来处理。而时间收集器之所能够同时管理上百万连接通道的事件是基于操作系统提供的“多路IO复用”技术常见的包括select、epoll两种模型。 正是由于Nginx使用了高性能的Reactor模式因此是目前并发能力很高的Web服务器之一成为迄今为止使用广泛的工业级Web服务器。当然Nginx也解决了著名的网络读写是C10K问题。什么是C10K问题呢网络服务在处理数以万计的客户端连接时往往出现效率低下甚至完全瘫痪这类问题就被成为C10K问题。
1.2、Nginx的两类进程 一般来说Nginx在启动以后会与daemon方式在后台运行其后台进程有两种 一类称为Master进程(相当于管理进程)另一类称为Worker进程(工作进程)。Nginx的进程结构大致如下: Nginx启动方式有两种
单进程启动此时系统中仅有一个进程该进程既充当Master管理进程角色又充当Worker工作进程角色多进程启动此时系统有且仅有一个Master管理进程至少有一个Worker工作进程。 有点类似于RocketMQ的模式 一般来说单进程模式用于调试。在生产环境中一般会配置成多进程模式并且Worker工作进程的数量和机器CPU核心数配置不一样多。 了解Worker工作进程之前首先了解一下Master管理进程的主要工作主要有以下两点
Master管理进程主要负责调度Worker工作进程比如加载配置、启动工作进程、接收来自外界的信号、向各Worker进程发送信号、监控Worker进程的运行状态等。所以Nginx启动以后我们能看到至少两个Nginx进程Master负责创建监听套接口交由Worker进程进行连接监听。 接下来介绍Nginx的Worker进程。Worker进程主要用来处理网络事件。当一个Worker进程在接受一条连接通道之后就开始读取请求、解析请求、处理请求处理完成产生数据以后再返回给客户端最后断开连接通道。 各个Worker进程之间是对等且相互独立的它们同等竞争来自客户端的请求一个请求只可能在一个Worker进程中处理。则都是典型的Reactor模型中Worker进程或者线程的职能。 如果启动了多个Worker进程那么每个Worker子进程独自尝试接受已连接的Socket监听通道accept操作默认会上锁优先使用操作系统的共享内存原子锁如果操作系统不支持就是用文件上锁。 经过配置Worker进程的接受操作也可以不适用锁在多个进程同时接受时当一个连接进来的时候多个工作进程同时被唤起则会导致惊群问题。而在上锁的场景下只会有一个Worker阻塞在accept上其他的进程会因为不能获取锁而阻塞所以上锁的场景不存在惊群问题。
1.3、Nginx模块化设计 Nginx服务器被分解为多个模块模块之间严格遵循“高内聚低耦合”的原则每个模块都聚焦于一个功能。高度模块化的设计是Nginx的架构基础。 什么是Nginx模块呢在Nginx的实现中一个模块包含一系列命令cmd和这些命令相对应的处理函数cmd→handler。Nginx的Worker进程在执行过程中会通过配置文件的配置指令定位到对应的功能模块的某个命令cmd然后调用命令对应的处理函数来完成相应的处理。 Nginx的Worker进程首先会调用Nginx的Core核心模块。大家知道在Reactor模型中会维护一个运行循环Run-Loop主要包括事件收集、事件分发、事件处理这个工作在Nginx中由Core核心模块负责。Core模块负责执行网络请求处理的基础操作比如网络读写、存储读写、内容传输、外出过滤以及将请求发往上游服务器等。 Nginx的Core模块是启动时一定会加载的其他的模块只有在解析配置时遇到了这个模块的命令才会加载对应的模块。Core模块为其他模块构建了基本的运行时环境并成为其他各模块的协作基础。 除了Core模块外Nginx还有Event、Conf、HTTP、Mail等一系列模块并且可以在编译时加入第三方模块。Nginx的模块结构如图 这里对Nginx的主要模块说明如下
Core核心模块核心模块是Nginx服务器正常运行必不可少的模块提供错误日志记录、配置文件解析、Reactor事件驱动机制、进程管理等核心功能。标准HTTP模块标准HTTP模块提供HTTP协议解析相关的功能比如端口配置、网页编码设置、HTTP响应头设置等。可选HTTP模块可选HTTP模块主要用于扩展标准的HTTP功能让Nginx能处理一些特殊的服务比如Flash多媒体传输、网络传输压缩、安全协议SSL的支持等。邮件服务模块邮件服务模块主要用于支持Nginx的邮件服务包括对POP3协议、IMAP协议和SMTP协议的支持。第三方模块第三方模块是为了扩展Nginx服务器的功能定制开发者自定义功能比如JSON支持、Lua支持等。 Nginx的非核心模块可以在编译时按需加入这里不再赘述。 总之Nginx通过模块化设计使得大家可以根据需要对功能模块进行适当的选择和修改编译成具有特定功能的服务器。
1.4、Nginx配置文件上下文结构 前面介绍到一个Nginx的功能模块包含一系列的命令cmd以及与命令对应的处理函数cmd→handler。而Nginx根据配置文件中的配置指令就知道对应到哪个模块的哪个命令然后调用命令对应的处理函数来处理。 一个Nginx配置文件包含若干配置项每个配置项由配置指令和指令参数两部分组成可以简单认为配置项是一个键-值对。图中有3个简单的Nginx配置项 Nginx配置文件中的配置指令如果包含空格就需要用单引号或双引号引起来。指令参数如果是由简单的字符串构成的简单配置项就需要以分号结束指令参数如果是复杂的多行字符串配置项就需要用花括号“{}”括起来。 Nginx配置项的具体功能与其所处的作用域上下文、配置块是强相关的。Nginx指令的作用域配置块大致有5种它们之间的层次关系如图
一个标准的Nginx配置文件的上下文结构如下
nginx
#main全局配置块例如工作进程数
events { #events事件处理模式配置块例如IO读写模式、连接数等}
http #HTTP协议配置块
{#HTTP协议的全局配置块server #server虚拟服务器配置块一{#server全局块location [PATTERN] #location路由规则配置块一{}location [PATTERN] #location路由规则配置块二{}}server #server虚拟服务器配置块二{}
#其他HTTP协议的全局配置块
}
mail #mail服务配置块
{#email相关协议如SMTP/IMAP/POP3的处理配置
}对以上作用域上下文、配置块说明如下
1.4.1、main全局配置块 配置影响Nginx全局的指令一般有运行Nginx服务器的用户组、Nginx进程PID存放路径、日志存放路径、配置文件引入、允许生成的Worker进程数等。
1.4.2、events事件处理模式配置块 配置Nginx服务器的IO多路复用模型、客户端的最大连接数限制等。Nginx支持多种IO多路复用模型可以使用use指令在配置文件中设置IO读写模型。
1.4.3、HTTP协议配置块 可以配置与HTTP协议处理相关的参数比如keepalive长连接参数、GZIP压缩参数、日志输出参数、mime-type参数、连接超时参数等。
1.4.4、server虚拟服务器配置块 配置虚拟主机的相关参数如主机名称、端口等。一个HTTP协议配置块中可以有多个server虚拟服务器配置块。
1.4.5、location路由规则块 配置客户端请求的路由匹配规则以及请求过程中的处理流程。一个server虚拟服务器配置块中一般会有多个location路由规则块。
1.4.6、mail服务配置块 Nginx为email相关协议如SMTP/IMAP/POP3提供反向代理时mail服务配置块负责配置一些相关的配置项。 提示以上介绍的Nginx配置块主要针对的是Nginx基本应用程序配置文件包括基本配置文件在内Nginx的常用配置文件大致有下面这些
nginx.conf应用程序基本配置文件mime.types与MIME类型关联的扩展配置文件fastcgi.conf与FastCGI相关的配置文件proxy.conf与Proxy相关的配置文件sites.conf单独配置Nginx提供的虚拟机主机
1.5、Nginx的请求处理流程 Nginx中HTTP请求的处理流程可以分为4步:
读取解析请求行读取解析请求头多阶段处理也就是执行handler处理器列表将结果返回给客户端
Nginx中HTTP请求的处理流程如图: 多阶段处理是Nginx的HTTP处理流程中非常重要的一步。Nginx把请求处理划分成了11个阶段在完成第一步读取请求行和第二步读取请求头之后Nginx将整个请求封装到一个请求结构体ngx_http_request_t实例中相当于Java中的一个请求对象然后进入第三步多阶段处理也就是执行handler处理器列表。列表中的每个handler处理器都会对请求对象进行处理例如重写URI、权限控制、路径查找、生成内容以及记录日志等。 Nginx将HTTP请求处理流程分成了11个阶段每个阶段都涉及一些handler处理器。HTTP请求到来时这些组装在一个列表的handler处理器会按组装的先后次序执行。这一点和Netty的处理流水线pipeline在原理上是类同的。 在Nginx进行多阶段处理时handler处理器的执行次序除了和配置文件中对应指令的配置顺序相关外还和指令所处的阶段先后次序相关。 Nginx请求处理的11个阶段以及阶段与阶段之间的执行次序如图: 对HTTP请求进行多阶段处理是Nginx模块化非常关键和重要的功能第三方模块的处理器都在不同的处理阶段注册例如
用Memcache进行页面缓存的第三方模块用Redis集群进行页面缓存的第三方模块执行Lua脚本的第三方模块
1.6、HTTP请求处理的11个阶段 Nginx请求处理的11个阶段介绍如下
1.6.1、post-read阶段 在完成第一步读取请求行和第二步读取请求头之后就进入多处理阶段首当其冲的就是post-read阶段。注册在post-read阶段的处理器不多标准模块的ngx_realip处理器就注册在这个阶段。ngx_realip处理器模块的用途是改写请求的来源地址。 为何要改写请求的来源地址呢 当Nginx处理的请求经过了某个正向代理服务器Nginx、CDN的转发后请求中的IP地址$remote_addr可能就不是客户端的真实IP了变成了下游代理服务器的IP。如何获取用户请求的真实IP地址呢解决办法之一在下游的正向代理服务器把请求的原始来源地址编码成某个特殊的HTTP请求头在Nginx中把这个请求头中编码的地址恢复出来然后传给Nginx自己后头的上游服务器。ngx_realip模块正是用来处理这个需求的。 下面有一个简单的例子假定前头的正向代理服务器能将客户端IP编码成某个特殊的HTTP请求头如X-My-IPNginx就可以通过ngx_realip模块的real_ip_header指令将X-My-IP请求头的IP取出作为请求中的IP地址$remote_addr。
server {listen 8080;set_real_ip_from 192.168.0.100;real_ip_header X-My-IP;location /test {echo from: $remote_addr ;}
}这里的配置是让Nginx把来自正向代理服务器192.168.0.100的所有请求的IP来源地址都改写为请求头X-My-IP所指定的值放在$remote_addr内置标准变量中。
1.6.2、server-rewrite阶段 server-rewrite阶段简单地翻译就是server块中的请求地址重写阶段。在进行请求URI与location路由规则匹配之前可以修改请求的URI地址。 大部分直接配置在server配置块中的配置项都运行在server-rewrite阶段。
server {listen 8080;set $a hello; #server-rewrite阶段运行location /test {set $b $a, world;echo $b;}set $b hello; #server-rewrite阶段运行
}其中两个变量赋值的配置项set a h e l l o 和 s e t a hello和set ahello和setb hello直接写在server配置块中因此它们就运行在server-rewrite阶段。
1.6.3、find-config 紧接在server-rewrite阶段后面的是find-config阶段也叫配置查找阶段主要功能是根据请求URL地址去匹配location路由表达式。 find-config阶段由Nginx HTTP Corengx_http_core_module模块全部负责完成当前请求URL与location配置块之间的配对工作。这个阶段不支持Nginx模块注册处理程序。 在find-config阶段之前客户端请求并没有与任何location配置块相关联。因此对于运行在此之前的post-read和server-rewrite阶段来说只有server配置块以及更外层作用域中的配置项才会起作用location配置块中的配置项不起作用。
1.6.4、rewrite 由于Nginx已经在find-config阶段完成了当前请求与location的匹配因此从rewrite阶段开始location配置块中的指令就可以产生作用。 rewrite阶段也叫请求地址重写阶段注册在rewrite阶段的指令首先是ngx_rewrite模块的指令比如break、if、return、rewrite、set等。其次第三方ngx_lua模块中的set_by_lua指令和rewrite_by_lua指令也能在此阶段注册。
1.6.5、post-rewrite 请求地址URI重写提交Post阶段防止递归修改URI造成死循环一个请求执行10次就会被Nginx认定为死循环该阶段只能由NginxHTTP Corengx_http_core_module模块实现。
1.6.6、preaccess 访问权限检查准备阶段控制访问频率的ngx_limit_req模块和限制并发度的ngx_limit_zone模块的相关指令就注册在此阶段。
1.6.7、access 在访问权限检查阶段配置指令多是执行访问控制类型的任务比如检查用户的访问权限、检查用户的来源IP地址是否合法等。在此阶段能注册的指令有HTTP标准模块ngx_http_access_module的指令、第三方ngx_auth_request模块的指令、第三方ngx_lua模块的access_by_lua指令等。 比如deny和allow指令属于ngx_http_access_module模块它的 使用示例如下
server {#拒绝全部location /denyall {deny all;}#允许来源IP属于192.168.0.0/24网段或127.0.0.1的请求#其他来源IP全部拒绝location /allowsome {allow 192.168.0.0/24;allow 127.0.0.1;deny all;echo you are ok;}
}如果同一个location块配置了多个allow/deny配置项access阶段的配置项之间是按配置的先后顺序匹配的匹配成功一个便跳出。上面的例子中如果客户端源IP是127.0.0.1则匹配到“allow127.0.0.1”配置项后就不再匹配后面的“deny all”也就是说该请求不会被拒绝。如果这些配置项的指令来自不同的模块则每个模块会执行一个访问控制类型的指令。 特别提醒echo指令用于返回内容在location上下文中该指令注册在content生产阶段。由于echo指令不是注册在access阶段因此在access阶段不执行该指令的配置项。
1.6.8、post-access 访问权限检查提交阶段。如果请求不被允许访问Nginx服务器该阶段负责就向用户返回错误响应。在access阶段可能存在多个访问控制模块的指令注册post-access阶段的satisfy配置指令可以用于控制它们彼此之间的协作方式。下面有一个例子
#satisfy指令进行协调
location /satisfy-demo {satisfy any;access_by_lua ngx.exit(ngx.OK);deny all;echo hello;
}在上面的例子中deny指令属于HTTP标准模块的ngx_http_access_module访问控制模块而access_by_lua指令属于第三方ngx_lua模块两个模块都有自己的计算结果需要经过最终的结果统一。 不同访问控制模块的计算结果统一工作这里由satisfy指令负责有两种统一的方式
逻辑或操作具体的配置项为“satisfy any”表示访问控制模块A、B、C或更多只要其中任意一个通过验证就算通过。逻辑与操作具体的配置项为“satisfy all”表示访问控制模块A、B、C或更多全部模块都通过验证才能最终通过。
1.6.9、try-files 如果HTTP请求访问静态文件资源那么try-files配置项可以使这个请求按顺序访问多个静态文件资源直到某个静态文件资源符合选取条件。这个阶段只有一个标准配置指令try-files并不支持Nginx模块注册处理程序。 try-files指令接收两个以上任意数量的参数每个参数都指定了一个URINginx会在try-files阶段依次把前N-1个参数映射为文件系统上的对象文件或者目录然后检查这些对象是否存在。若Nginx发现某个文件系统对象存在则查找成功进而在try-files阶段把当前请求的URI改写为该对象所对应的参数URI但不会包含末尾的斜杠字符也不会发生“内部跳转”。如果前N-1个参数所对应的文件系统对象都不存在try-files阶段就会立即发起“内部跳转”跳转到最 后一个参数第N个参数所指定的URI。 下面是一个简单的实例
root /var/www/; #root指令把“查找文件的根目录”配置为 /var/www/
location /try_files-demo {try_files /foo /bar /last;
}
#对应到前面try_files的最后一个URI
location /last {echo uri: $uri ;
}这里try-files会在文件系统查找前两个参数对应的文件/var/www/foo和/var/www/bar所对应的文件是否存在。如果不存在此时Nginx就会在try-files阶段发起到最后一个参数所指定的URI/last的内部跳转如图: 1.6.10、content 大部分HTTP模块会介入内容产生阶段是所有请求处理阶段中重要的阶段。Nginx的echo指令、第三方ngx_lua模块的content_by_lua指令都注册在此阶段。 这里要注意的是每一个location只能有一个“内容处理程序”因此当在location中同时使用多个模块的content阶段指令时只有一个模块能成功注册成为“内容处理器”。例如echo和content_by_lua同时注册最终只会有一个生效但具体是哪一个生效结果是不稳定的。
1.6.11、log 日志模块处理阶段记录日志。 最后总结一下
Nginx将一个HTTP请求分为11个处理阶段这样做让每个HTTP模块可以只专注于完成一个独立、简单的功能。而一个请求的完整处理过程由多个HTTP模块共同合作完成可以极大地提高多个模块合作的协同性、可测试性和可扩展性。Nginx请求处理的11个阶段中有些阶段是必备的有些阶段是可选的各个阶段可以允许多个模块的指令同时注册。但是find-config、post-rewrite、post-access、try-files四个阶段是不允许其他模块的处理指令注册的它们仅注册了HTTP框架自身实现的几个固定的方法。同一个阶段内的指令Nginx会按照各个指令的上下文顺序执行对应的handler处理器方法。