什么是nginx?

  • 是一个高性能的负载均衡服务器,处理并发能力强、效率高
  • 内存消耗小:开启10nginx服务才占150M内存。
  • 运行时间长:即使运行数月也不需要重新启动,还能在不间断服务的情况下进行软件版本的升级

nginx模块讲解

nginx主要分为三个模块:全局块events块http块
其中http块又由server块location块upstream块组成

# 全局块:定义影响全局的配置,如用户、worker进程数量、错误日志等
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 768;  # 每个worker进程的最大连接数
}

http {
    # http块:定义HTTP服务器的配置,如mime类型、日志、压缩等
    include /etc/nginx/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 /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

    # server块:定义虚拟主机的配置,每个server块代表一个虚拟主机
    server {
        listen 80;  # 监听端口
        server_name example.com;  # 服务器域名

        # location块:定义URL匹配的处理规则
        location / {
            root /var/www/html;  # 根目录
            index index.html index.htm;  # 默认文件
        }

        location /images/ {
            root /var/www/data;  # 指定目录
        }

        # 配置反向代理
        location /proxy/ {
            proxy_pass http://backend_servers;  # 转发到后端服务器池
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

    # 定义后端服务器池
    upstream backend_servers {
        server backend1.example.com;
        server backend2.example.com;
        server backend3.example.com;
    }
}

全局块

主要定义一些全局属性。如worker进程数、日志等全局属性

user www-data;  # 定义运行NGINX进程的用户
worker_processes auto;  # 自动检测并设置worker进程数
pid /run/nginx.pid;  # 定义存储进程ID文件的位置
include /etc/nginx/modules-enabled/*.conf;  # 包含额外的模块配置



events块

主要定义nginx的worker进程的最大连接数

events {
    worker_connections 768;  # 每个worker进程的最大连接数
}

http块

定义所有与http服务(服务/应用)的相关配置;如连接超时时间。可以包含多个server块,每个server代表一个服务

http {
    include /etc/nginx/mime.types;  # 包含mime类型定义
    default_type application/octet-stream;  # 默认的mime类型

    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 /var/log/nginx/access.log main;  # 定义访问日志的位置和格式

    sendfile on;  # 启用高效文件传输模式
    tcp_nopush on;  # 提高TCP传输效率
    tcp_nodelay on;  # 减少TCP延迟
    keepalive_timeout 65;  # 连接保持的超时时间
    types_hash_max_size 2048;  # 提高mime类型的哈希表大小
    include /etc/nginx/conf.d/*.conf;  # 包含额外的配置文件
    include /etc/nginx/sites-enabled/*;  # 包含虚拟主机配置文件

server块

定于一个服务的相关配置。一个server块包含多个location块。每个location定义特定URL的处理规则。

    server {
        listen 80;  # 监听80端口
        server_name example.com;  # 定义虚拟主机的域名

        location / {
            root /var/www/html;  # 定义根目录
            index index.html index.htm;  # 默认文件
        }

        location /images/ {
            root /var/www/data;  # 定义/images/路径的目录
        }

        location /proxy/ {
            proxy_pass http://backend_servers;  # 配置反向代理
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

upstream块

定义和管理后端服务器,用于负载均衡和反向代理

    upstream backend_servers {
        server backend1.example.com;  # 定义后端服务器1
        server backend2.example.com;  # 定义后端服务器2
        server backend3.example.com;  # 定义后端服务器3
    }
}

worker进程

nginx采用的是多进程架构;主要由一个主进程(master process)和多个worker进程组成,每个worker进程互相隔离,单独处理请求。

主进程

  • 负责管理(启动、停止)所有worker进程,同时将请求路由分发给worker进程
  • 读取和验证配置文件
  • 处理系统相关级别工作(如重载配置,重启服务)

worker进程

  • 各个worker进程相互隔离,独立处理客户端请求。
  • 每个woker进程只有一个线程来进行工作。采用异步、非阻塞的事件驱动模型来处理连接和请求

流程:

  1. 启动:nginx启动时,主进程加载全局配置信息,并启动对应数量的worker进程
  2. 请求处理:客户端请求到达时,主进程将请求分发worker进程,worker根据连接请求转发到目标服务中。

示例配置解释

# 全局块
user www-data;  # 定义运行NGINX进程的用户
worker_processes auto;  # 自动设置worker进程数量,通常设置为CPU核心数
pid /run/nginx.pid;  # 定义存储主进程ID文件的位置

events {
    worker_connections 768;  # 每个worker进程的最大连接数 
}

连接:表示TCP或者websocket连接(nginx支持websocket)。连接的超时时间在http块中定义


http {
    # 全局配置
    keepalive_timeout 65;  # 保持连接的超时时间为65秒
    keepalive_requests 100;  # 每个连接最多处理100个请求
 }   

异步非阻塞事件处理机制

为什么都说nginx的性能高,其实nginx内部是采用多进程架构+异步非阻塞事件处理机制。
线程一直持续工作,减少了大量的CPU上下文切换。

异步非阻塞事件处理机制:

线程是进程的执行单元,线程获取CPU使用权就会执行任务(代码)。但是线程在执行过程因为某些情况会放弃CPU使用权,进入阻塞状态。

  1. 示例1 线程执行I/O(网络I/O)任务时,线程会阻塞放弃CPU使用权,并等待I/O任务完成,重新等待CPU调用。
  2. 示例2 线程获取锁资源时,会进行阻塞放弃CPU使用权,直到当前线程获取锁后,再次等待CPU调度。

这两个实例中,线程在执行过程中都会阻塞,会偷懒,不再往后执行相关代码,直到阻塞消失(I/O完成,获取到锁)。在这种情况下 线程执行任务会非常慢。 那有没有一种办法 让线程一直工作不让线程阻塞(偷懒)。这种办法就叫做 异步非阻塞事件处理机制

假设当前有多个请求进入到同一个worker进程中。
异步非阻塞:当第一个请求被处理时,线程不会一直等待该请求结果,而是直接处理其它请求,当第一个请求完成接收到结果后,线程再来处理该请求。这样可以更好地利用系统资源,提高处理效率。

详细步骤:

  1. 当有多个请求同时发送时,nginx的主进程会将请求分发不同worker进程或者是同一个worker进程进行处理
  2. worker进程都有一个循环监听事件,一个单独的线程,线程请求发送给目标服务,就会立马处理其它请求
  3. 当目标服务处理完成请求后,会响应给nginx(具体worker进程),循环监听事件监听到了响应,就会告知线程处理响应。这样就会提高线程的执行效率.
  4. 根据相关配置,关闭连接。

总结:请求会分发给多个worker进程,实际上同一时间还是有多个线程处理请求(并发处理请求)。只不过这些线程来自不同的进程,又因为线程不会休息,新的请求进来时很快就被处理,执行效率快,所以并发能力也会提高。

参考链接:太详细了!Java语言异步非阻塞模式(原理篇)

nginx监听规则

在server模块经常能看到这个属性:listenserver_name

server {
    listen 80;  # NGINX监听80端口
    server_name example.com;

	location /image/{
	 root /home/image/;
        expires 30d;
} 

   # 定义所有请求的处理规则
    location / {
        # 将请求转发到目标网站
        proxy_pass http://testServer.com:8848;

        # 设置代理请求的头部信息
        # 将请求的Host头部信息设置为客户端请求中的Host
        proxy_set_header Host $host;
        
        # 将客户端的IP地址传递给目标服务器
        proxy_set_header X-Real-IP $remote_addr;
        
        # 将客户端的真实IP地址包含在请求的X-Forwarded-For头部中
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

}

listen:表示监听的端口,通常为nginx所在服务器的端口
server_name:表示监听的IP/域名,通常为nginx所在服务器

在我们前端应用中,通常是指定具体的后端服务器IP进行交互,如testServer.com:8848/list。
但是部署到nginx后,项目是不需要指定后端服务器,nginx根据url会自动将请求路由到后端服务器。如下:

发送请求 example.com:80/image/xx.jpg 。就会从nginx所在服务器的/home/image/ 目录下找
发送请求 example.com:80/list;就会转发给testServer.com服务器。url就变成了testServer.com:8848/list

正向代理

代理对象是 客户端 ;当客户端想要访问某一网站(服务器)时,nginx就会代理客户端去发送请求,同时隐藏客户端IP地址。
流程:

  1. 发送请求要目标网站(服务器)时,nginx会拦截请求并生成代理服务器,由它去与目标网站进行交互
  2. 代理服务器将请求发送给目标网站
  3. 目标网站返回响应给代理服务器
  4. 代理服务器返回给客户端
    在服务器角度,与我交互的是代理服务器,所以我并不知道客户端的IP地址

示例:客户端通过监听端口发送请求到 example.com 网站,就会生成代理服务器与服务器进行交互,同时可以选择是否隐藏真实IP地址。

# 定义代理服务器监听的端口
server {
    listen 8080;

    # 定义所有请求的处理规则
    location / {
        # 将请求转发到目标网站
        proxy_pass http://example.com;

        # 设置代理请求的头部信息
        # 将请求的Host头部信息设置为客户端请求中的Host
        proxy_set_header Host $host;
        
        # 将客户端的IP地址传递给目标服务器
        proxy_set_header X-Real-IP $remote_addr;
        
        # 将客户端的真实IP地址包含在请求的X-Forwarded-For头部中
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

反向代理

代理对象是 服务器;一个大的网站,通常会有多个服务器来处理请求,保证服务的高可用性。nginx就代理这些服务器,当检测到有请求要进入到网站时,就会为网站生成一个代理服务器,并根据负载均衡策略决定将请求路由到哪个服务器中。

流程:

  1. 客户端发送请求给目标网站,nginx会生成代理服务器
  2. nginx根据负载均衡策略,决定将请求路由到具体服务器中。同时隐藏真实服务器IP
  3. 服务器处理请求并将响应结果返回给代理服务器。
  4. 代理服务器将响应结果放回给客户端。
    在客户端角度,与我交互的是代理服务器,我并不知道请求路由到哪个具体的服务器(IP)中

示例:
一个大型网站有很多后端服务器处理用户请求,通过反向代理服务器分发请求以提高性能和安全性

# 定义代理服务器监听的端口
server {
    listen 80;

    # 定义所有请求的处理规则
    location / {
        # 将请求转发到后端服务器池
        proxy_pass http://backend_servers;

        # 设置代理请求的头部信息
        # 将请求的Host头部信息设置为客户端请求中的Host
        proxy_set_header Host $host;
        
        # 将客户端的IP地址传递给后端服务器
        proxy_set_header X-Real-IP $remote_addr;
        
        # 将客户端的真实IP地址包含在请求的X-Forwarded-For头部中
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

# 定义后端服务器池
upstream backend_servers {
    # 定义多个后端服务器
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

反向代理通常会使用 upstream 指令来定义和管理后端服务器。
其实在上方反向代理示例中,也有使用正向代理来代理客户端。

正向、反向代理区别:从nginx配置文件中来看就是是否使用了 upstream 指令

nginx解决跨域

nginx采用CORS策略来跨域。

server {
    listen 80;
    server_name example.com;

    location / {
        # 允许所有来源的跨域请求
        add_header Access-Control-Allow-Origin *;
        
        # 允许指定的请求方法
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, HEAD";
        
        # 允许携带认证信息
        add_header Access-Control-Allow-Credentials true;
        
        # 允许指定的请求头字段
        add_header Access-Control-Allow-Headers "Authorization, Origin, X-Requested-With, Content-Type, Accept";
        
        # 设置预检请求的有效期
        add_header Access-Control-Max-Age 3600;

        # 配置处理请求的指令
    }
}

负载均衡

轮询

每个客户端的请求有序的转发到不同的服务器中,默认是轮询
在这里插入图片描述

权重论询

就如某一个服务器比其他服务器更好,我就让客户端的请求多转发到这个服务器中,weight代表权重

在这里插入图片描述

在nginx.conf中的配置:
第一步:在http全局块添加负载均衡的服务器列表
第二步:在server块中的location中设置它的映射规则
在这里插入图片描述
想要加权重,可在服务器列表中加weight
server 192.168.17.129:8080 weight=5;
server 192.168.17.129:8081 weight=10;
这样8080的服务器处理的量就多一倍

最小连接

下一个连接会被路由到连接数最小的服务器中

IP哈希

每个请求按访问IP的hash结果进行路由,这样每个客户端固定一个后端服务器。
实际中,不考虑使用该方法

动静分离

简单来说就是把动态资源(请求)跟静态资源(图片)分开,分别部署到不同服务器中。

好处:静态资源由nginx处理,减少了后端服务器的负载,提高了响应速度。同时后端服务器只需要处理动态请求即可。

示例:静态资源部署到nginx所在服务器中
url为/image/xx.jpg时就会从/var/www/image/目录下获取

server {
    listen 80;
    server_name example.com;

    # 处理静态文件
    location /image/ {
        root /var/www/image/;
        expires 30d;
    }

    # 处理动态请求
    location / {
        proxy_pass http://backend_server;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

upstream backend_server {
    server 127.0.0.1:8080;  # 后端服务器地址
}

为什么说nginx处理静态资源就快,而处理动态快呢

这是项目的静态资源都部署在nginx所在的服务器上,所以nginx读取静态资源就非常快。
而处理动态资源,需要解析请求,将请求转发到目标服务器。同时涉及到负载均衡策略等问题。目标服务器返回响应给nginx时还要将响应数据返回给客户端。这一过程非常的繁琐,所消耗的资源也比处理静态资源时多。

限流规则

  1. 基于连接数限流
  2. 基于每秒的请求数限流

注:nginx的限流是作用在目标服务器,而不是nginx服务本身。

基于连接数限流

说明:某个客户端IP与nginx发起的连接数超过了阈值,那么超出的连接将会被nginx拒绝。防止某个客户端ip地址与nginx的连接数过载。

http {
    # 定义连接限制的共享内存区域,10m是内存大小
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    server {
        listen 80;
        server_name example.com;

        # 限制每个 IP 地址最多允许 10 个连接
        limit_conn addr 10;

        location / {
            # 配置处理请求的指令
        }
    }
}


使用 limit_conn_zone 指令定义了一个共享内存区域,用于存储连接限制的状态信息。$binary_remote_addr 是一个 NGINX 变量,表示客户端的 IP 地址,zone=addr:10m 指定了共享内存区域的名称为 addr,大小为 10MB。

每个客户端与nginx进行交互,都会进行一个TCP连接,后续的请求都会复用这个连接,连接过了超时时间后就会销毁。

连接超时时间设置:

http {
    keepalive_timeout 30s;  # 设置连接超时时间为 30 秒

    server {
        listen 80;
        server_name example.com;

        location / {
            # 配置处理请求的指令
        }
    }
}

基于每秒的请求数限流

说明:每秒最多处理多少个请求

http {
    # 定义请求速率限制的共享内存区域,10m是内存大小,rate=10r/s表示每秒允许的请求速率
    limit_req_zone $binary_remote_addr zone=req_zone:10m rate=10r/s;

    server {
        listen 80;
        server_name example.com;

        # 每秒最多允许 10 个请求,突发量不超过 20 个,10-20之间的请求会延迟处理,超过20的请求会直接拒绝。
        limit_req zone=req_zone burst=20 nodelay;

        location / {
            # 配置处理请求的指令
        }
    }
}

nginx的基于请求数量限流是根据令牌桶算法实现的。
参考文章:限流:计数器、漏桶、令牌桶 三大算法的原理与实战(史上最全)

高可用主备模式

我们是无法确定一个软件是一直运行下去的,所有当nginx坏了的时候,我们需要设置另外一个nginx来完善工作,这里需要下载keepalived来管理nginx,当keppalived检测到主nginx坏了,就会启用备用nginx

参考链接:Nginx配置参数详解

全文参考连接:
Nginx面试题(总结最全面的面试题!!!)

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部