深入理解Nginx 读书笔记

2.1 运行中Nginx 进程间的关系

在正式服务的产品环境下,部署Nginx 时都是使用一个master 进行来管理多个worker 进程,一般情况下,worker进程的数量与服务器上的CPU 核心数相等。每一个worker 进程是繁忙的 它们真正的提供互相网服务,master 进程则很"清闲",只负责监控和管理worker进程。worker进程之间通过共享内存、原子操作等一些进行间通信机制来实现负载均衡等功能

Nginx 是支持单进程(master进程)提供服务的,那为什么产品环境下要按照master-worker 方式配置同时启动多个进程那?这样做的好处主要有以下两点:

  • 由于master 进程不会对用户请求提供服务,只用于管理真正提供服务的 worker进程,所以master进程可以是唯一的,它仅专注于自己的管理工作,为管理员提供命令行服务,包括诸如启动服务、停止服务、重载配置文件、平滑升级程序等。master进程需要拥有较大的权限,例如,通常会利用root用户才启动master进程。work进程的权限要小于或等于master进程,这样master进程才可以完全地管理worker进程。当任意一个worker进程出现错误从而导致coredump时,master进程会立刻启动新的worker进程继续服务

  • 多个worker进程处理互联网请求不但可以提高服务的健壮性,最重要的是,这样可以充分利用现在常见的SMP多核架构,从而实现微观上真正的多核并发处理。因此,用一个进程(master进程)来处理互联网请求肯定是不合适的。另外,为什么要把worker进程数量设置得与CPU核心数量一致那?这正是Nginx 与Apache 服务器得不同之处。在Apache上每个进程在一个时刻只处理一个请求。因此,如果希望Web 服务器拥有并发处理得请求数更多,就把Apache得进程或线程数设置得更多,通常会达到一台服务器拥有几百个工作进程,这样大量得进程间切换将带来无谓得系统资源消耗。而Nginx则不然,一个worker进程可以同时处理得请求数只受限于内存大小,而且在架构设计上,不同得worker进程之间处理并发请求时几乎没有同步锁得限制,worker进程通常不会进入睡眠状态,因此当Nginx 上得进程数与CPU 核心数相等时,进程间切换的代价是最下的

2.2 Nginx 配置的通用语法


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    use epoll
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       90;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }

        location ^~ /api/ {
			rewrite ^/api/(.*)$ /$1 break;
			proxy_pass http://localhost:8080;
        }
        
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

}

2.5 用HTTP proxy module 配置一个反向代理服务器

由于 Nginx 具有 "强悍" 的高并发负载能力,因此一般会作为前端的服务器直接向客户端提供静态文件服务。但也有一些复杂、多变的业务不合适方到Nginx 服务器上,这是会用Apache、Tomcat 等服务器来处理。于是,Nginx 通常会被配置为既是静态Web服务器也是反向代理服务器,不适合Nginx 处理的请求就会直接转发到上游服务器中处理

与Squid 等其他反向代理服务器相比,Nginx 的反向代理功能有自己的特点。如下图所示,当客户端 发来HTTP请求时,Nginx 并不会立刻转发到上游服务器,而是先把用户的请求(包括HTTP包体)完整地接收到Nginx 所在服务器的硬盘或者内存中,然后再向上游服务器发起连接,把缓存的客户端请求转发到上有服务器。而Squid 等代理服务器则采用一边接收客户端请求,一边转发发到上上游服务器的方式。

Nginx 的这种工作方式有什么优缺点那?
缺点是延长了一个请求的处理时间,并增加了用于缓存请求内容的内容和磁盘空间。而优点是降低了上游服务器的负载,尽量把压力放在Nginx服务器上。

为什么会降低上游服务器的负载?
通常客户端与代理服务器之间的网络环境会比较复杂,多半是走公网,网速平均下来可能会比较慢。因此,一个请求可能要持续很久才会完成。而代理服务器与上游服务器是走内网,或者有专线连接,传输速度比较快。Squid等反向代理服务器在与客户端建立连接且还没有开始接收HTTP包体时,就已经与上游服务器建立了连接。例如某个请求要上传一个1G的文件,那么每次Squid 在收到一个TCP分包(如2K)时,就会即时向上游服务器转发。在接收完整HTTP包体的漫长过程中,上游服务器始终要维持这个连接,这直接对上游服务器的并发处理能力提出了挑战。

Nginx 则不然,它在接收到完成的客户端请求(如1GB的文件)后,才会与上游服务器建立连接转发请求,由于是内网,这个转发请求处理的很快。这样一个客户端请求占用上游服务器的连接时间就会非常短,也就是说,Nginx这种反向代理的方案主要是为了降低上游服务器的并发压力

9.10 TCP 协议与Nginx

作为 Web 服务器的nginx,主要任务当时处理好基于TCP 的HTTP协议,本节将深入TCP协议的实现细节(linux下)以更好地理解Nginx的时间处理机制

建立TCP连接是我们耳熟能详的三次握手:
1) 客户端向服务器发起连接(SYN)
2) 服务器确认收到并向客户端也发起链接(ACK + SYN)
3) 客户端确认收到服务器发起的连接(ACK)

这个建立连接的过程是在操作系统内核中完成的,而如Nginx这样的应用程序只是从内核中取出建立好的连接TCP连接。大多数时候,Nginx 是作为连接的服务器存在的, 我们看一下 Linux
内核是怎么样处理TCP连接的,如下图所示

上图表达了一个观点:内核在我们调用listen 方法时,就已经为这个监听端口建立了SYN队列和ACCEPT队列,当客户端使用connect 方法向服务器发起TCP连接,随后图中1.1步骤客户端SYN包到达了服务器后,内核会把这一信息放到SYN队列(即未完成握手队列)中,同时回一个SYN + ACK 包给客户端。2.1步骤中客户端再次发来了针对服务器SYN包的ACK网络分组时,内核会把连接从SYC 队列中取出,再把这个连接方到ACCEPT队列(即已完成握手队列)中。而服务器在第3步调用accept方法建立连接时,其实就是直接从ACCEPT 队列中取出已经建立好的连接而已

这样,如果大量连接同时到来,而应用程序不能即使地调用accept方法,就会导致以上两个队列满(ACCEPT队列满,进而也会导致SYN队列满),从而导致连接无法建立。这其实很常见,比如Nginx的每个worker 进程都负责调用accept 方法,如果一个Nginx 模块在处理请求时长时间陷入了某个方法的执行中(如执行计算或者等待IO),就有可能导致新连接无法建立

posted @ 2023-11-26 22:26  chuangzhou  阅读(16)  评论(0编辑  收藏  举报