Nginx配置详解:反向代理、负载均衡与动静分离的实战应用

配置文件

Nginx 本身作为一个完成度非常高的负载均衡框架,和很多成熟的开源框架一样,大多数功能都可以通过修改配置文件来完成,使用者只需要简单修改一下 Nginx 配置文件,便可以非常轻松的实现比如反向代理,负载均衡这些常用的功能,同样的,和其他开源框架比如 Tomcat 一样,Nginx 配置文件也遵循着相应的格式规范,并不能一顿乱配,在讲解如何使用 Nginx 实现反向代理,负载均衡等这些功能的配置前,我们需要先了解一下 Nginx 配置文件的结构。

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    
    server {
        listen       80;
        server_name  localhost;

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

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

Nginx 的配置文件(通常为 /etc/nginx/nginx.conf)是模块化设计,结构清晰,由一系列指令和块组成。以下是它的主要结构与功能讲解:

全局块 Global Context

配置文件顶部,不在任何块内,定义全局适用的配置,影响整个 Nginx 服务器。主要包括配置运行 Nginx 服务器的用户(组)、允许生成的 worker process 数,进程 PID 存放路径、日志存放路径和类型以及配置文件的引入等。常见指令如下:

# 定义运行 Nginx 的用户
user nginx;

# 设置工作进程数量(可以设置为 CPU 核心数)默认为 1
worker_processes auto;

# 指定错误日志的路径和级别
error_log  logs/error.log;
error_log  logs/error.log  notice;
error_log  logs/error.log  info;

# 定义 Nginx 的 PID 文件路径
pid        logs/nginx.pid;

事件块 Events Context

全局块下,用 events 指令开始,用来定义 Nginx 如何处理网络连接。

events 块涉及的指令主要影响 Nginx 服务器与用户的网络连接,常用的设置包括是否开启对多 work process 下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型来处理连接请求,每个 word process 可以同时支持的最大连接数等。

events {
    # 每个 worker 进程允许的最大连接数,默认为 512
    worker_connections 1024;
    
    # 事件驱动模型,select、poll、kqueue、epoll、resig、/dev/poll、eventport
    use epoll;
    
    # 设置网路连接序列化,防止惊群现象发生,默认为 on
    accept_mutex on;
    
    # 设置一个进程是否同时接受多个网络连接,默认为 off
    multi_accept on;
}

HTTP 块 HTTP Context

events 块之后,用 http 指令开始,主要包含 Web 服务相关的配置,是配置文件的核心部分。这是一个大块,里面也可以包括很多小块,比如 HTTP 全局块、Server 块等。

  • HTTP 全局块:适用于所有 HTTP 服务,全局块配置的指令包括文件引入、MIME-TYPE 定义、日志自定义、连接超时时间、单链接请求数上限等。
  • Server 块:定义一个虚拟主机(一个域名对应一个 server 块),一个 HTTP 块可以拥有多个 Server 块。它下面包括全局 Server 块,和 Location 块:
    • 全局 Server 块主要包括了本虚拟机主机的监听配置和本虚拟主机的名称或 IP 配置。
    • Location 块则用来对虚拟主机名称之外的字符串进行匹配,对特定的请求进行处理。地址定向、数据缓存和应答控制等功能,还有许多第三方模块的配置也在这里进行。比如,对 /usr 相关的请求交给 8080 来处理,/admin 则较给 8081 处理。
http {
    # 引入其他配置文件,文件扩展名与文件类型映射表
    include       mime.types;
    # 引入其他配置文件
    # include /etc/nginx/conf.d/*.conf;
    
    # 默认文件类型,默认为 text/plain
    default_type  application/octet-stream;
    
    # 允许 sendfile 方式传输文件,默认为 off,可以配置在 http、server、location 块
    sendfile        on;
    
    # 连接超时时间,可以配置在 http、server、location 块
    keepalive_timeout  65;
    
    # 控制是否显示 Nginx 的版本信息
    server_tokens off;
    
    # 取消服务日志
    access_log off;
    
    # 自定义日志格式
    log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for';
    
    # 访问日志格式路径配置
    access_log log/access.log myFormat;
    
    # 启用 Gzip 压缩以提高传输效率
    gzip on;
    
    # 每个进程每次调用传输数量不能大于设定的值,默认为 0,即不设上限。
    sendfile_max_chunk 100k;
    
     # 错误页面配置
    error_page 404 https://www.baidu.com;
    
    server {
        # 指定监听端口
        listen       80;
        # 指定域名或 IP
        server_name  localhost;

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

        # 错误页面配置
        error_page  500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

Nginx 反向代理

首先我们先要了解反向代理的概念,这里我们就和正向代理做比较。

正向代理

正向代理是你找人帮忙出面访问你不能直接触达的地方,代表用户去“外面”。

举个生活例子:假设你想买一件海外商品,但商家的网站禁止直接访问(比如因为你的 IP 地址来自某个地区)。于是你找到一个朋友 A,让他替你去下单,然后把商品转交给你。这里,你通过朋友 A 间接访问了商家

正向代理是用户端的代理,它帮助用户访问本无法直接访问的外部资源。用户通过代理服务器发送请求,代理服务器再转发请求到目标服务器。

正向代理常见用途如下:

  • 科学上网(比如翻墙)。
  • 缓存用户请求,加速访问(如 CDN 客户端缓存)。

反向代理

反向代理是你作为老板,安排服务员代表你处理外部来的请求,保护“后方”。

举个生活例子:这次,你是餐馆的老板,有几位厨师忙碌地做菜。顾客点餐时,并不知道哪位厨师在做菜(也不需要知道),你安排服务员 B 接单,并把菜交给顾客。这里,顾客通过服务员 B 间接与厨师交流

反向代理是服务器端的代理,它接收来自用户的请求,然后将请求转发到内部服务器,拿到结果后再返回给用户。

反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器 IP 地址。

反向代理的常见用途如下:

  • 隐藏内部服务器信息,增强安全性。
  • 负载均衡,分发请求给多台服务器。
  • SSL 卸载,集中处理加密流量。

配置反向代理

在 Location 块中,可以通过 proxy_pass 配置要代理的服务器。

# 假设当前机器的 IP 为 10.96.3.11
server {
    # 需要监听的代理服务器端口
    listen 88;
    # 代理服务器域名
    server_name localhost;
    # 监听的请求路径 /
    location / {
        # 反向代理配置,将请求转发到指定服务
        proxy_pass http://10.96.3.12:8080;
    }
}

当客户端访问代理服务器 http://10.96.3.11:88 地址时,反向代理服务器就会将客户端请求转发到地址为 http://10.96.3.12:8080 的目标服务器 ,并且反向代理服务器再将目标服务器的响应数据转发回客户端。

通过这样,客户端就只知道代理服务器的信息,从而隐藏了真正的服务器的信息,起到了保护作用。

注意这里的 location /,这个斜线其实就是一种策略,用来匹配要代理的路径。

location 基本语法
location [匹配规则类型] URI匹配规则 {
    # 为匹配的请求定义的处理方式,比如 proxy_pass、root 等
}
location 匹配规则

Nginx 的 location 支持 4 种主要的匹配规则,按优先级从高到低如下:

精确匹配 (=)

完全匹配指定的 URI,优先级最高,匹配成功后不会继续匹配其他规则。适用于静态文件等无需正则处理的路径,避免额外的正则匹配开销。

# 仅当请求的 URI 完全是 /exact-match 时,才返回消息
location = /exact-match {
    return 200 "This is an exact match!";
}
前缀匹配 (^~)

匹配以指定前缀开头的 URI,且停止继续匹配其他规则。适用于静态资源或特殊路径。

# 所有以 /static/ 开头的请求都会被匹配,并返回 /var/www/static/ 下的文件
location ^~ /static/ {
    root /var/www;
}
正则表达式匹配 (~~*)

匹配 URI 中的正则表达式,~ 表示大小写敏感,~* 表示大小写不敏感。优先级低于 =^~,高于普通前缀匹配。

# 匹配所有以图片扩展名结尾的请求(忽略大小写),并从 /var/www/images 目录提供文件
location ~* \.(jpg|jpeg|png|gif|ico)$ {
    root /var/www/images;
}
普通前缀匹配

匹配以某字符串开头的 URI。优先级最低,按定义顺序匹配。兜底规则,适用于覆盖其他规则未匹配的 URI。

location /blog {
    root /var/www/website;
}

location / {
    root /var/www/html;
    index index.html;
}

请求和结果:

  1. 访问 /blog/article1.html:匹配到 /blog 前缀规则,文件路径为 /var/www/website/blog/article1.html
  2. 访问 /contact.html:匹配到 / 规则(默认匹配),文件路径为 /var/www/html/contact.html
综合示例
server {
    listen 80;
    server_name example.com;

    # 精确匹配
    location = /about {
        return 200 "About Page";
    }

    # 前缀匹配
    location ^~ /static/ {
        root /var/www/static/;
    }

    # 正则表达式匹配(大小写不敏感)
    location ~* \.(css|js|html)$ {
        root /var/www/assets;
    }

    # 默认匹配
    location / {
        root /var/www/html;
        index index.html;
    }
}

测试效果

  1. 请求 /about:返回 "About Page"
  2. 请求 /static/images/logo.png:从 /var/www/static/images/logo.png 返回文件。
  3. 请求 /styles/main.css:从 /var/www/assets/styles/main.css 返回文件。
  4. 请求 / 或未匹配的路径:从 /var/www/html/index.html 返回默认文件。

Nginx 负载均衡

Nginx 的负载均衡功能允许你将客户端请求分配到多台后端服务器上,从而提高网站的可用性和性能。通过合理的配置,Nginx 能够在多个服务器之间分发流量,以避免单点故障和过载,提高网站的容错性和扩展性。

负载均衡基本概念

负载均衡是一种将网络流量(如 Web 请求)分配到多个服务器的技术。在 Nginx 中,负载均衡通常通过 upstream 块配置,Nginx 将客户端请求分发到 upstream 块中定义的多个后端服务器。

负载均衡的目标是:

  • 提升性能:通过分摊请求到多台服务器,提高响应速度。
  • 提高可用性:当某台服务器故障时,流量可以自动切换到其他正常的服务器。

负载均衡方法

Nginx 提供了多种负载均衡算法(调度策略),包括轮询、加权轮询、IP 哈希等。

轮询(Round Robin)

轮询是 Nginx 的默认负载均衡算法。它会按顺序将请求均匀分发给每台后端服务器,直到所有服务器都接收到一个请求后,再重新开始分发。

适用场景:服务器性能相似,流量分布均匀。

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
加权轮询(Weighted Round Robin)

加权轮询与轮询类似,但为每台服务器分配不同的权重。权重较大的服务器会接收到更多的请求。

适用场景:服务器硬件配置不同,性能差异较大。

upstream backend {
    server backend1.example.com weight=3;  # 权重较大,处理更多请求
    server backend2.example.com weight=1;  # 权重较小,处理较少请求
}

server {
    location / {
        proxy_pass http://backend;
    }
}
最少连接(Least Connections)

Nginx 会将请求分配给当前连接数最少的服务器。这种方式适用于服务器处理请求的时间不一致的场景,比如一些请求可能需要更长的处理时间。

适用场景:后端服务器负载差异较大,且请求响应时间有较大波动。

upstream backend {
    least_conn;  # 使用最少连接的算法
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
IP 哈希(IP Hash)

IP 哈希算法根据客户端的 IP 地址来选择服务器。每个 IP 地址都映射到固定的后端服务器。这样相同的客户端 IP 会始终访问同一台后端服务器(会话保持)。

适用场景:需要会话保持(sticky session)或某些请求需要始终由同一台服务器处理。

upstream backend {
    ip_hash;  # 使用客户端 IP 来选择后端服务器
    server backend1.example.com;
    server backend2.example.com;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
随机(Random)

Nginx 也可以选择将请求随机分配给后端服务器,适用于负载均衡压力较小、请求分布比较均匀的情况。

适用场景:负载均衡压力不大,可以随机分配流量。

upstream backend {
    random;  # 随机选择服务器
    server backend1.example.com;
    server backend2.example.com;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
按请求大小(Least Time)

Nginx Plus 提供的高级负载均衡策略之一,基于每台服务器的请求处理时间来选择目标。它可以根据连接的响应时间(包括请求的处理时间和传输时间)来选择最合适的后端服务器。

upstream backend {
    least_time header;  # 基于请求头处理时间进行负载均衡
    server backend1.example.com;
    server backend2.example.com;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
哈希负载均衡(Hash)

如果你希望基于某些请求头(如 HostCookie)来选择服务器,Nginx 支持通过哈希算法选择后端服务器。它可以根据自定义的哈希值来分配请求,常见的应用场景是通过 CookieRequest URI 来实现会话保持。

upstream backend {
    hash $cookie_session consistent;  # 基于 Cookie 中的 session 来做一致性哈希
    server backend1.example.com;
    server backend2.example.com;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
基于请求的负载均衡(Request Hashing)

Nginx 可以基于请求的某些特征(如 URI 或某些自定义请求头)来进行哈希路由。这种方法适用于需要将特定请求分配给特定服务器的场景。

upstream backend {
    hash $request_uri;  # 根据请求 URI 来进行负载均衡
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

健康检查(Health Check)

Nginx 默认不支持自动健康检查,但可以使用 Nginx Plus(商业版本)或第三方模块来实现。如果某台后端服务器不可用,负载均衡器会自动将流量转发到健康的服务器。

在 Nginx Plus 中,你可以配置健康检查:

upstream backend {
    # 定义共享内存区域,64KB 用于存储 upstream 的状态信息
    zone backend 64k;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
    # 启用健康检查
    health_check;
}

负载均衡的常见问题

会话保持(Sticky Sessions)

如果应用需要会话保持,即客户端的每次请求必须由同一台服务器处理(例如,用户登录后需要访问同一台服务器),可以使用 ip_hash 或其他方法来确保会话一致性。

跨多个数据中心的负载均衡

如果有多个数据中心,Nginx 可以将流量按地理位置分配给不同的服务器。可以通过 IP 地址、域名或 GeoDNS 实现跨数据中心负载均衡。

SSL 终止

Nginx 可以作为 SSL 终端代理,处理客户端与 Nginx 之间的加密连接,然后将解密后的请求转发到后端服务器。可以配置 Nginx 进行 SSL 卸载,减轻后端服务器的负担。

负载均衡的示例:综合配置

http {
    upstream backend {
        # 负载均衡算法:加权轮询
        server backend1.example.com weight=5;
        server backend2.example.com weight=3;
        server backend3.example.com weight=2;
        
        # 启用健康检查(仅在 Nginx Plus 中可用)
        health_check;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            # 反向代理请求到后端
            proxy_pass http://backend;

            # 设置一些反向代理的常见头部
            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;
        }
    }
}

Nginx 动静分离

Nginx 配置动静分离是指将动态请求(例如 PHP、Python 或其他后端应用程序的请求)和静态请求(例如图片、CSS、JavaScript 文件)的处理分开,以优化性能和提高系统的扩展性。在这种架构下,静态资源由 Nginx 直接处理,而动态请求则转发给后端的应用服务器进行处理。

动静分离的基本概念

  • 静态资源:不需要经过后台处理的资源,如 HTML、CSS、JavaScript 文件、图片、字体文件等。Nginx 可以直接从硬盘中读取并返回给客户端,速度快且不需要复杂的计算。
  • 动态请求:需要经过后台应用程序(如 PHP、Node.js、Python、Java 等)处理的请求,通常是向数据库请求数据或执行某些计算任务,然后将结果返回给客户端。

通过动静分离,我们将静态资源交给 Nginx 高效处理,而动态请求交给后端的应用服务器,这样能充分发挥 Nginx 处理静态文件的高效能力,减少后端服务器的压力,提高整体性能。

配置动静分离的步骤

我们可以通过配置 Nginx,使得静态文件直接由 Nginx 处理,而动态请求转发给后端应用服务器。以下是配置的主要步骤:

配置静态资源处理

Nginx 对静态资源的处理非常高效,通常使用 location 块来指定静态资源的路径,并配置静态文件的根目录。静态资源通常放置在 /var/www 或其他目录中。

为了提高性能,静态资源可以配置缓存,使得 Nginx 能够更长时间地缓存这些文件,从而减少后端服务器的压力。

server {
    listen 80;
    server_name example.com;

    # 配置静态文件根目录
    location /static/ {
        root /var/www;  # 设置静态文件的根目录
        expires 30d;       # 设置静态资源缓存 30 天
        add_header Cache-Control public;  # 启用缓存控制
    }
    
    # 默认访问静态文件
    location / {
        root /var/www/html;
        index index.html;
    }
}
配置动态请求处理

动态请求通常需要转发到应用服务器进行处理,如将动态请求转发到 Spring Boot 应用。通常,静态资源的路径以 /static//public/ 开头,动态请求通常是以 /api// 开头。

server {
    listen 80;
    server_name example.com;

    # 静态资源配置
    location /static/ {
        root /var/www;  # 静态资源存放的目录
        expires 30d;    # 设置缓存过期时间
        add_header Cache-Control public;
    }

    location /public/ {
        root /var/www;  # 公共静态资源目录
        expires 30d;
        add_header Cache-Control public;
    }

    # 配置将动态请求转发给 Spring Boot
    location /api/ {
        proxy_pass http://localhost:8080;  # Spring Boot 后端应用的地址
        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;
    }

    # 配置默认首页和其他动态请求
    location / {
        proxy_pass http://localhost:8080;  # Spring Boot 后端应用的地址
        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;
    }

    # 错误页面处理
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /var/www/html;
    }
}
优化缓存和日志
静态资源的缓存

为静态资源设置长时间缓存,而对于动态请求则不进行缓存。通过 expiresCache-Control 头设置缓存策略,减少文件的加载时间。

日志优化

Nginx 提供访问日志和错误日志,可以根据访问模式对日志进行优化。例如,静态文件的访问日志可以独立出来,动态请求的日志可以单独记录。

access_log 指令:用于指定访问日志的存储路径和日志格式。可以为不同的请求配置不同的日志文件。

server {
    listen 80;
    server_name example.com;
	
    # 配置指定了一个名为 static_access.log 的访问日志文件,用于记录静态资源的访问日志
    # if=$static 表示只有当变量 $static 的值为真时,才会将访问日志记录到这个文件
    # main 是日志格式的名称,它引用了在 Nginx 配置文件中定义的日志格式(通常在 log_format 指令中定义)
    access_log /var/log/nginx/static_access.log main if=$static;
    
    # 这行配置指定了一个名为 dynamic_access.log 的访问日志文件,用于记录动态请求的访问日志
    # if=$dynamic 表示只有当变量 $dynamic 的值为真时,才会将访问日志记录到这个文件
    # 同样,main 是日志格式的名称,表示使用 log_format 中定义的格式
    access_log /var/log/nginx/dynamic_access.log main if=$dynamic;
}

这段 Nginx 配置中的 access_log 指令用于配置访问日志的记录。它指定了日志文件的位置和格式,并且通过 if 条件来分别为静态资源请求和动态请求记录不同的日志文件。

if=$staticif=$dynamic

这两个 if 条件的作用是基于某个变量的值来决定是否记录日志。实际上,$static$dynamic 应该是 Nginx 配置中定义的变量,或者通过 set 指令来为它们赋值。这些变量控制静态资源和动态请求的日志记录。

例如,你可以通过 set 指令来定义这些变量,如下所示:

set $static 1;  # 假设所有静态资源请求都设置为 1
set $dynamic 0;  # 动态请求默认设置为 0

然后,你可以通过 location 块来根据请求类型(静态或动态)设置这两个变量的值。例如:

location /static/ {
    set $static 1;
    set $dynamic 0;
    # 其他静态资源处理逻辑
}

location /api/ {
    set $static 0;
    set $dynamic 1;
    # 其他动态请求处理逻辑
}

通过这种配置,你可以将静态资源请求和动态请求的访问日志分别记录到不同的日志文件。这对于日志分析和调试非常有用,因为你可以独立查看静态资源的访问情况和动态请求的访问情况,从而更容易发现性能瓶颈、问题和优化点。

动静分离的优点

  1. 性能提升:Nginx 对静态资源的处理非常高效,能够减少 Web 应用服务器的压力,将静态文件的请求交给 Nginx 处理,提升整体响应速度。
  2. 减轻应用服务器压力:通过动静分离,动态请求由应用服务器处理,静态请求由 Nginx 直接提供,可以使得应用服务器专注于处理复杂的动态请求。
  3. 易于扩展:动静分离架构使得静态资源和动态应用的扩展可以独立进行。例如,静态资源可以单独部署 CDN(内容分发网络)进行加速,而动态请求可以通过负载均衡扩展应用服务器。
  4. 缓存优化:Nginx 对静态文件的缓存机制非常强大,可以根据缓存策略有效减轻服务器的负担,提高资源访问速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值