Nginx到底厉害在什么地方
1. Nginx 的产生
Nginx 和 Apache 一样,也是一种 Web 服务器。基于 REST 架构风格,以统一资源描述符(Uniform Resources Identifier)URI 或者统一资源定位符(Uniform Resources Locator)URL 作为沟通依据,通过 HTTP 协议提供各种网络服务。
各个Web服务器在设计之初由于受到用户规模,网络带宽等局限,并且各自的定位和发展也不尽相同,所以使得各个 Web 服务器有着各自鲜明的特点。其中最出名的Apache,Apache 的发展时期很长,而且毫无争议是世界第一大服务器。它有着很多优点:稳定、开源、跨平台等等。但是也存在一些缺点:
- 它兴起的年代,互联网产业远远比不上现在,所以它被设计为一个重量级的。
- 不支持高并发。在 Apache 上运行数以万计的并发访问,会导致服务器消耗大量内存。
- 操作系统对其进行进程或线程间的切换会消耗大量的 CPU 资源,导致 HTTP 请求的平均响应速度降低。
这些缺点决定了 Apache 不可能成为高性能 Web 服务器,轻量级高并发服务器 Nginx 就应运而生了。
俄罗斯的工程师 Igor Sysoev,他在为 Rambler Media 工作期间,使用 C 语言开发了 Nginx。
Nginx 是一款开源的、高性能的 HTTP 服务器和反向代理服务器;同时也是一个 IMAP、POP3、SMTP 代理服务器。
Nginx 作为 Web 服务器一直为 Rambler Media 提供出色而又稳定的服务。后来,Igor Sysoev 将 Nginx 代码开源,并且赋予*软件许可证。
Nginx 优点:
- Nginx 基于事件驱动架构,使得其可以支持数百万级别的 TCP 连接。
- 高度的模块化和*软件许可证使得第三方模块层出不穷
- Nginx 是一个跨平台服务器,可以运行在 Linux、Windows、FreeBSD、Solaris、AIX、Mac OS 等操作系统上。
- 极大的稳定性。
2. Nginx 的用武之地
Nginx 可以作为一个 HTTP 服务器进行网站的发布处理, 也可以作为反向代理进行负载均衡的实现。
代理:
代理涉及到两个角色,一个是被代理角色,一个是目标角色。如同电脑专卖店,客人通过专卖店买了一个电脑,这个专卖店就是代理,被代理角色就是 电脑 厂家,目标角色就是用户。
2.1 正向代理:
正向代理也是最常接触到的代理模式。我们通过浏览器没办法访问某些网站,可以通过代理服务器去访问,然后将访问到的数据传递给我们!
正向代理的特点:
- 客户端非常明确要访问的服务器地址;
- 服务器只清楚请求来自哪个代理服务器,而不清楚来自哪个具体的客户端;
- 代理服务器屏蔽或者隐藏了真实客户端信息。
- “代理服务器代理的是客户端”,客户端必须要进行一些特别的设置才能使用正向代理。
设置实现:
客户端必须设置正向代理服务器,前提是要知道正向代理服务器的 IP 地址,还有代理程序的端口:
正向代理用途:
- 访问原来无法访问的资源,如 Google。
- 可以做缓存,加速访问资源。
- 对客户端的访问进行授权,进行上网认证。
2.2 反向代理
例如淘宝,有时候同时访问人数很多,单个服务器远远不能满足访问需求了。这时候需要通过部署多台服务器来解决访问人数限制的问题。
如图所示,多个客户端给服务器发送请求,Nginx 服务器接收到之后,按照一定的规则转发给后端的某个服务器进行处理。
反向代理的特点:
- 反向代理对外是透明的,访问者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。
- 反向代理,“它代理的是服务端”,隐藏了服务器的信息。
反向代理的作用:
- 主要用于服务器集群分布式部署的情况下,保证内网的安全,通常将反向代理作为公网访问地址。
- 负载均衡,通过反向代理服务器来优化网站的负载。
2.3 项目场景
通常情况下,在实际项目中,正向代理和反向代理很有可能会一起存在,正向代理代理客户端的请求去访问目标服务器,目标服务器是一个反向代理服务器,反向代理服务器代理了多台真实的业务处理服务器。
正向代理和反向代理二者之间的区别:
- 在正向代理中,Proxy 和 Client 同属于一个 LAN(图中方框内),代理服务器对服务端隐藏了客户端信息。
- 在反向代理中,Proxy 和 Server 同属于一个 LAN(图中方框内),代理服务器对客户端隐藏了服务端信息
实际上,Proxy 在两种代理中做的事情都是替服务器接收请求和响应结果,不过从结构上看正好左右互换了一下。
2.4 负载均衡
负载均衡在实际项目操作过程中,有硬件负载均衡和软件负载均衡两种。
硬件负载均衡也称为硬负载,如 F5 负载均衡,相对软负载造价昂贵成本较高。但是数据的稳定性安全性等有非常好的保障,如中国移动中国联通这样的公司才会选择硬负载。更多的公司考虑到成本原因,会选择使用软负载均衡,软负载均衡是利用现有的技术结合主机硬件实现的一种消息队列分发机制:
消息分发策略:
- weight 轮询(默认):接收到的请求按照顺序逐一分配到不同的后端服务器,在使用过程中,如果某一台后端服务器宕机,Nginx 会自动将该服务器剔除出队列,请求受理情况不会受到任何影响。这种方式下,可以给不同的后端服务器设置一个权重值(weight),用于调整不同的服务器上请求的分配率。权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的。
- ip_hash:对客户端的 ip 的 hash 结果进行匹配,这样一个固定 ip 地址的客户端总会访问到同一个后端服务器,这也在一定程度上解决了集群部署环境下 Session 共享的问题。
- fair:智能调整调度算法,动态的根据后端服务器从处理请求到响应的时间进行均衡分配。响应时间短处理效率高的服务器分配到请求的概率高,响应时间长处理效率低的服务器分配到的请求少,它是结合了前两者的优点的一种调度算法。但是需要注意的是 Nginx 默认不支持 fair 算法,如果要使用这种调度算法,请安装 upstream_fair 模块。
- url_hash:按照访问的 URL 的 hash 结果分配请求,每个请求的 URL 会指向后端固定的某个服务器,可以在 Nginx 作为静态服务器的情况下提高缓存效率。同样要注意 Nginx 默认不支持这种调度算法,要使用的话需要安装 Nginx 的 hash 软件包。
2.5 Web 服务器对比
server | Apache | Nginx | Lighttpd |
---|---|---|---|
Proxy代理 | 非常好 | 非常好 | 一般 |
Rewiter | 好 | 非常好 | 一般 |
Fcgl | 不好 | 好 | 非常好 |
热部署 | 不支持 | 支持 | 不支持 |
系统压力比较 | 很大 | 很小 | 比较小 |
稳定性 | 好 | 非常好 | 不好 |
安全性 | 好 | 一般 | 一般 |
静态文件处理 | 一般 | 非常好 | 好 |
反向代理 | 一般 | 非常好 | 一般 |
3. Nginx底层原理
Nginx 服务器,正常运行过程中:
- 多进程:一个 Master 进程、多个 Worker 进程。
- Master 进程:管理 Worker 进程。对外接口:接收外部的操作(信号);对内转发:根据外部的操作的不同,通过信号管理 Worker;监控:监控 Worker 进程的运行状态,Worker 进程异常终止后,自动重启 Worker 进程。
- Worker 进程:所有 Worker 进程都是平等的。实际处理:网络请求,由 Worker 进程处理。Worker 进程数量:在 nginx.conf 中配置,一般设置为核心数,充分利用 CPU 资源,同时,避免进程数量过多,避免进程竞争 CPU 资源,增加上下文切换的损耗。
思考:
- 请求是连接到 Nginx,Master 进程负责处理和转发?
- 如何选定哪个 Worker 进程处理请求?请求的处理结果,是否还要经过 Master 进程?
3.1 HTTP 连接建立和请求处理过程
HTTP 连接建立和请求处理过程如下:
- Nginx 启动时,Master 进程加载配置文件。
- Master 进程,初始化监听的 Socket。
- Master 进程,Fork 出多个 Worker 进程。
- Worker 进程,竞争新的连接,获胜方通过三次握手,建立 Socket 连接,并处理请求。
3.2 Nginx 高性能、高并发
Nginx 为什么拥有高性能并且能够支撑高并发?
- Nginx 采用多进程+异步非阻塞方式(IO 多路复用 Epoll)。
- 请求的完整过程:建立连接→读取请求→解析请求→处理请求→响应请求。
- 请求的完整过程对应到底层就是:读写 Socket 事件。
3.3 Nginx 的事件处理模型
Request:Nginx 中 HTTP 请求。
基本的 HTTP Web Server 工作模式:
- 接收请求:逐行读取请求行和请求头,判断段有请求体后,读取请求体。
- 处理请求。
- 返回响应:根据处理结果,生成相应的 HTTP 请求(响应行、响应头、响应体)。
Nginx 也是这个套路,整体流程一致:
3.4 模块化体系结构
Nginx 的模块根据其功能基本上可以分为以下几种类型:
-
event module:搭建了独立于操作系统的事件处理机制的框架,及提供了各具体事件的处理。包括 ngx_events_module,ngx_event_core_module 和 ngx_epoll_module 等。
Nginx 具体使用何种事件处理模块,这依赖于具体的操作系统和编译选项。
-
phase handler:此类型的模块也被直接称为 handler 模块。主要负责处理客户端请求并产生待响应内容,比如 ngx_http_static_module 模块,负责客户端的静态页面请求处理并将对应的磁盘文件准备为响应内容输出。
-
output filter:也称为 filter 模块,主要是负责对输出的内容进行处理,可以对输出进行修改。
例如,可以实现对输出的所有 html 页面增加预定义的 footbar 一类的工作,或者对输出的图片的 URL 进行替换之类的工作。
-
upstream:upstream 模块实现反向代理的功能,将真正的请求转发到后端服务器上,并从后端服务器上读取响应,发回客户端。
upstream 模块是一种特殊的 handler,只不过响应内容不是真正由自己产生的,而是从后端服务器上读取的。
-
load-balancer:负载均衡模块,实现特定的算法,在众多的后端服务器中,选择一个服务器出来作为某个请求的转发服务器。
3.5 常见问题剖析
3.5.1 Nginx vs Apache
Nginx:
- IO 多路复用,Epoll(freebsd 上是 kqueue)
- 高性能
- 高并发
- 占用系统资源少
Apache:
- 阻塞+多进程/多线程
- 更稳定,Bug 少
- 模块更丰富
3.5.2 Nginx 最大连接数
基础背景:
- Nginx 是多进程模型,Worker 进程用于处理请求。
- 单个进程的连接数(文件描述符 fd),有上限(nofile):ulimit -n。
- Nginx 上配置单个 Worker 进程的最大连接数:worker_connections 上限为 nofile。
- Nginx 上配置 Worker 进程的数量:worker_processes。
因此,Nginx 的最大连接数:
- Nginx 的最大连接数:Worker 进程数量 x 单个 Worker 进程的最大连接数。
- 上面是 Nginx 作为通用服务器时,最大的连接数。
- Nginx 作为反向代理服务器时,能够服务的最大连接数:(Worker 进程数量 x 单个 Worker 进程的最大连接数)/ 2。
- Nginx 反向代理时,会建立 Client 的连接和后端 Web Server 的连接,占用 2 个连接。
3.5.3 HTTP 请求和响应
HTTP 请求:
- 请求行:method、uri、http version
- 请求头
- 请求体
HTTP 响应:
- 响应行:http version、status code
- 响应头
- 响应体
3.5.4 IO 模型
处理多个请求时,可以采用:IO 多路复用或者阻塞 IO+多线程:
- IO 多路复用:一个线程,跟踪多个 Socket 状态,哪个就绪,就读写哪个。
- 阻塞 IO+多线程:每一个请求,新建一个服务线程。
IO 多路复用和多线程的适用场景:
- IO 多路复用:单个连接的请求处理速度没有优势。
- 大并发量:只使用一个线程,处理大量的并发请求,降低上下文环境切换损耗,也不需要考虑并发问题,相对可以处理更多的请求。
- 消耗更少的系统资源(不需要线程调度开销)。
- 适用于长连接的情况(多线程模式长连接容易造成线程过多,造成频繁调度)。
- 阻塞 IO +多线程:实现简单,可以不依赖系统调用。
- 每个线程,都需要时间和空间。
- 线程数量增长时,线程调度开销指数增长。
select/poll 和 epoll 比较如下:
详细内容,参考:
https://www.cnblogs.com/wiessharling/p/4106295.html
select/poll 系统调用:
// select 系统调用
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
// poll 系统调用
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
select:
- 查询 fd_set 中,是否有就绪的 fd,可以设定一个超时时间,当有 fd (File descripter) 就绪或超时返回。
- fd_set 是一个位集合,大小是在编译内核时的常量,默认大小为 1024。
- 特点:连接数限制,fd_set 可表示的 fd 数量太小了;线性扫描:判断 fd 是否就绪,需要遍历一边 fd_set;数据复制:用户空间和内核空间,复制连接就绪状态信息。
poll:
- 解决了连接数限制:poll 中将 select 中的 fd_set 替换成了一个 pollfd 数组,解决 fd 数量过小的问题。
- 数据复制:用户空间和内核空间,复制连接就绪状态信息。
epoll,event 事件驱动:
- 事件机制:避免线性扫描,为每个 fd,注册一个监听事件,fd 变更为就绪时,将 fd 添加到就绪链表。
- fd 数量:无限制(OS 级别的限制,单个进程能打开多少个 fd)。
select,poll,epoll:
- I/O 多路复用的机制。
- I/O 多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作;监视多个文件描述符。
- 但 select,poll,epoll 本质上都是同步 I/O:用户进程负责读写(从内核空间拷贝到用户空间),读写过程中,用户进程是阻塞的;异步 IO,无需用户进程负责读写,异步 IO,会负责从内核空间拷贝到用户空间。
3.5.5 Nginx 的并发处理能力
关于 Nginx 的并发处理能力:并发连接数,一般优化后,峰值能保持在 1~3w 左右。(内存和 CPU 核心数不同,会有进一步优化空间)。