Vanson's Eternal Blog

Architecture基础

Architecture basic.png
Published on
/37 mins read/---

Architecture

网络基础

Get 和 Post

GET

  • 参数位置:URL(?key=value)
  • 大小限制:
    • 浏览器:约 2KB~8KB(不同浏览器不同)
    • 服务器:默认 8KB 左右,超出返回 414 URI Too Long
  • 特点:明文可见,可缓存,适合小数据(如搜索)

POST

  • 参数位置:HTTP 请求体(Request Body)
  • 大小限制:
    • 服务器可配置(默认几 MB 到几 GB)
    • 超出返回 413 Request Entity Too Large
  • 特点:不可见,不缓存,适合大数据(如表单、文件上传)

Nginx

正向代理和反向代理

正向代理

正向代理服务器一般位于用户和服务器之间,用户通过正向代理服务器访问应用服务器获取资源。

正向代理服务器,代理的是客户端,去和服务端交互。

最常见的例子就是,我们访问一个外国网站,该网站无法在国内直接访问,但是可以通过代理服务器访问. 也就是说,用户向正向代理服务器发送一个请求并指定目标,然后正向代理服务器向目标服务器(外国网站)转交请求并将获得的内容返回给用户。

反向代理

反向代理服务器一般位于用户和服务器之间,用户访问反向代理服务器获取应用服务器资源,

用户不知道应用服务器的地址,是由代理服务器转发的,有降低网络和服务器的负载,提高访问效率的作用。反向代理服务器,代理的是服务端,去和客户端交互。

Nginx怎么处理请求的?

server {         # 第一个Server区块开始,表示一个独立的虚拟主机站点
   listen       80; # 提供服务的端口,默认80
   server_name  localhost; # 提供服务的域名主机名
   location / { # 第一个location区块开始
     root   html; # 站点的根目录,相当于Nginx的安装目录
     index  index.html index.html;  # 默认的首页文件,多个用空格分开
} # 第一个location区块结果
  1. Nginx 在启动时,会解析配置文件,得到需要监听的端口与 IP 地址。在 Nginx 的 Master 进程里面先初始化好这个监控的Socket(创建 Socket,设置 addr、reuse 等选项,绑定到指定的 ip 地址端口,再 listen 监听)。
  2. fork(一个现有进程可以调用 fork 函数创建一个新进程。由 fork 创建的新进程被称为子进程 )出多个子进程出来。
  3. 子进程会竞争 accept 新的连接。此时,客户端就可以向 nginx 发起连接了。当客户端与nginx进行三次握手,与 nginx 建立好一个连接后。此时,某一个子进程会 accept 成功,得到这个建立好的连接的 Socket ,然后创建 nginx 对连接的封装,即 ngx_connection_t 结构体。
  4. 设置读写事件处理函数,并添加读写事件来与客户端进行数据的交换。
  5. Nginx 或客户端来主动关掉连接,到此,一个连接就寿终正寝了。

Nginx目录结构有哪些?

[root@localhost ~]# tree /usr/local/nginx
/usr/local/nginx
├── client_body_temp
├── conf                             # Nginx所有配置文件的目录
│   ├── fastcgi.conf                 # fastcgi相关参数的配置文件
│   ├── fastcgi.conf.default         # fastcgi.conf的原始备份文件
│   ├── fastcgi_params               # fastcgi的参数文件
│   ├── fastcgi_params.default       
│   ├── koi-utf
│   ├── koi-win
│   ├── mime.types                   # 媒体类型
│   ├── mime.types.default
│   ├── nginx.conf                   # Nginx主配置文件
│   ├── nginx.conf.default
│   ├── scgi_params                  # scgi相关参数文件
│   ├── scgi_params.default  
│   ├── uwsgi_params                 # uwsgi相关参数文件
│   ├── uwsgi_params.default
│   └── win-utf
├── fastcgi_temp                     # fastcgi临时数据目录
├── html                             # Nginx默认站点目录
│   ├── 50x.html                     # 错误页面优雅替代显示文件,例如当出现502错误时会调用此页面
│   └── index.html                   # 默认的首页文件
├── logs                             # Nginx日志目录
│   ├── access.log                   # 访问日志文件
│   ├── error.log                    # 错误日志文件
│   └── nginx.pid                    # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件
├── proxy_temp                       # 临时目录
├── sbin                             # Nginx命令目录
│   └── nginx                        # Nginx的启动命令
├── scgi_temp                        # 临时目录
└── uwsgi_temp                       # 临时目录
 

nginx.conf有哪些属性模块?

worker_processes  1;       # worker进程的数量
events {                     # 事件区块开始
    worker_connections  1024;    # 每个worker进程支持的最大连接数
}                                 # 事件区块结束
http {                            # HTTP区块开始
    include       mime.types;    # Nginx支持的媒体类型库文件
    default_type  application/octet-stream;  # 默认的媒体类型
    sendfile        on;       # 开启高效传输模式
    keepalive_timeout  65;  # 连接超时
    server {               # 第一个Server区块开始,表示一个独立的虚拟主机站点
        listen       80;  # 提供服务的端口,默认80
        server_name  localhost;   # 提供服务的域名主机名
        location / {      # 第一个location区块开始
            root   html;     # 站点的根目录,相当于Nginx的安装目录
            index  index.html index.htm;# 默认的首页文件,多个用空格分开
        }       # 第一个location区块结束
        error_page   500502503504  /50x.html;  # 出现对应的http状态码时,使用50x.html回应客户
        location = /50x.html {  # location区块开始,访问50x.html
            root   html;   # 指定对应的站点目录为html
        }
    }  
    ......
 

负载均衡的算法

轮询(默认)

每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某个服务器宕机,能自动剔除故障系统。

upstream backserver { 
   server 192.168.0.12; 
   server 192.168.0.13; 

权重 weight

weight的值越大,分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。其次是为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。

# 权重越高,在被访问的概率越大,如上例,分别是20%,80%。
upstream backserver { 
   server 192.168.0.12 weight=2; 
   server 192.168.0.13 weight=8; 

ip_hash( IP绑定)

每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题

upstream backserver { 
   ip_hash; 
   server 192.168.0.12:88; 
   server 192.168.0.13:80; 

fair(第三方插件)

必须安装upstream_fair模块。对比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,响应时间短的优先分配。

# 哪个服务器的响应速度快,就将请求分配到那个服务器上。
upstream backserver { 
   server server1; 
   server server2; 
   fair; 

url_hash(第三方插件)

必须安装Nginx的hash软件包按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。

upstream backserver { 
   server squid1:3128; 
   server squid2:3128; 
   hash $request_uri; 
   hash_method crc32; 

location的作用是什么?

location指令的作用是根据用户请求的URI来执行不同的应用,也就是根据用户请求的网站URL进行匹配,匹配成功即进行相关的操作。

   # 优先级1,精确匹配,根路径
    location =/ {
        return 400;
    }
 
    # 优先级2,以某个字符串开头,以av开头的,优先匹配这里,区分大小写
    location ^~ /av {
       root /data/av/;
    }
 
    # 优先级3,区分大小写的正则匹配,匹配/media*****路径
    location ~ /media {
          alias /data/static/;
    }
 
    # 优先级4 ,不区分大小写的正则匹配,所有的****.jpg|gif|png 都走这里
    location ~* .*\.(jpg|gif|png|js|css)$ {
       root  /data/av/;
    }
 
    # 优先7,通用匹配
    location / {
        return 403;
    }
 

限流怎么做的?

正常限制访问频率(正常流量):

限制一个用户发送的请求,我Nginx多久接收一个请求。Nginx中使用ngx_http_limit_req_module模块来限制的访问频率,限制的原理实质是基于漏桶算法原理来实现的。在nginx.conf配置文件中可以使用limit_req_zone命令及limit_req命令限制单个IP的请求处理频率。

 # 定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉
 limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;
 
# 绑定限流维度
server{
        
        location/seckill.html{
            limit_req zone=zone;    
            proxy_pass http://lj_seckill;
        }
 
}

突发限制访问频率(突发流量):

Nginx提供burst参数结合nodelay参数可以解决流量突发的问题,可以设置能处理的超过设置的请求数外能额外处理的请求数。我们可以将之前的例子添加burst参数以及nodelay参数:

# 定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;
 
    # 绑定限流维度
    server{
        
        location/seckill.html{
            limit_req zone=zone burst=5 nodelay;
            proxy_pass http://lj_seckill;
        }
 
    }

限制并发连接数

Nginx中的ngx_http_limit_conn_module模块提供了限制并发连接数的功能,可以使用limit_conn_zone指令以及limit_conn执行进行配置。

漏桶算法和令牌桶算法

漏桶

漏桶算法思路很简单,我们把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,

漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,

后续进入的水直接溢出(拒绝请求),以此实现限流。

令牌桶

令牌桶算法的原理也比较简单,我们可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病。

系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,

则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。

令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。

实现后端服务的健康检查?

利用 nginx_upstream_check_module 模块对后端节点做健康检查。

upstream backend {
    server 192.168.0.21:80;
    server 192.168.0.22:80;
    check interval=3000 rise=2 fall=5 timeout=1000 type=http;
}

其中,interval=3000 表示每 3 秒检查一次;rise=2 表示连续两次检查成功则认为节点正常;fall=5 表示连续五次检查失败则认为节点异常;timeout=1000 表示检查超时时间为 1 秒;type=http 表示采用 HTTP 方式进行检查。

Nginx 如何开启压缩?

开启nginx gzip压缩后,网页、css、js等静态资源的大小会大大的减少,从而可以节约大量的带宽,提高传输效率,给用户快的体验。虽然会消耗cpu资源,但是为了给用户更好的体验是值得的。

http {
  # 开启gzip
  gzip on;
 
  # 启用gzip压缩的最小文件;小于设置值的文件将不会被压缩
  gzip_min_length 1k;
 
  # gzip 压缩级别 1-10 
  gzip_comp_level 2;
 
  # 进行压缩的文件类型。
 
  gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
 
  # 是否在http header中添加Vary: Accept-Encoding,建议开启
  gzip_vary on;
}
 

Nginx事件驱动模型

select 模型

select 是一种传统的多路复用 I/O 模型,它通过一个系统调用来监控多个文件描述符(FD),判断它们是否准备好进行读、写或异常操作。select 的工作原理如下:

  • 调用 select 函数时,需要传入一个文件描述符集合(FD_SET),并指定要监控的最大文件描述符值。
  • select 函数会阻塞,直到集合中的某个文件描述符准备好或超时。
  • 当某个文件描述符准备好时,select 函数返回,并更新文件描述符集合,标记哪些文件描述符已经准备好。
 
fd_set readfds;
struct timeval timeout;
int maxfd;
 
// 初始化文件描述符集合
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
maxfd = server_socket + 1;
 
// 设置超时时间
timeout.tv_sec = 5;
timeout.tv_usec = 0;
 
// 调用 select
int activity = select(maxfd, &readfds, NULL, NULL, &timeout);
 
if ((activity < 0) && (errno != EINTR)) {
    printf("Select error");
} else {
    if (FD_ISSET(server_socket, &readfds)) {
        // 处理新连接
    }
}
 

优点

  • 简单易用:select 的 API 简单直观,容易理解和使用。
  • 跨平台:select 是 POSIX 标准的一部分,几乎在所有操作系统上都可用。

缺点

  • 性能问题:select 的最大文件描述符数量通常受限于系统参数(如 FD_SETSIZE,默认为 1024),这限制了其在高并发场景下的适用性。
  • 效率低下:select 需要检查整个文件描述符集合,当文件描述符数量较多时,效率较低。
  • 资源浪费:select 会复制整个文件描述符集合,增加了系统调用的开销。

epoll 模型(默认)

epoll 是 Linux 提供的一种高效的 I/O 多路复用机制,它通过维护一个事件表来监控文件描述符的状态。epoll 的工作原理如下:

  • 创建一个 epoll 文件描述符,用于管理事件表。
  • 将需要监控的文件描述符注册到 epoll 文件描述符中。
  • 调用 epoll_wait 函数,阻塞等待事件发生。
  • 当有事件发生时,epoll_wait 返回,并返回一个事件列表,包含所有已经准备好的文件描述符。
int epoll_fd = epoll_create(10); // 创建 epoll 文件描述符
struct epoll_event ev, events[10];
int nfds, n;
 
// 注册文件描述符
ev.events = EPOLLIN;
ev.data.fd = server_socket;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &ev);
 
// 等待事件
while (1) {
    nfds = epoll_wait(epoll_fd, events, 10, -1);
    for (n = 0; n < nfds; ++n) {
        if (events[n].data.fd == server_socket) {
            // 处理新连接
        }
    }
}

优点

  • 高性能:epoll 不需要像 select 那样复制整个文件描述符集合,减少了系统调用的开销。
  • 无限制:epoll 没有文件描述符数量的限制,可以处理大量的并发连接。
  • 高效:epoll 只返回已经准备好的文件描述符,减少了不必要的检查。

缺点

  • 仅限于 Linux:epoll 是 Linux 特有的机制,不具有跨平台性。
  • 复杂性:epoll 的使用相对复杂,需要管理事件表和文件描述符的注册、修改和删除。

如何优化 Nginx 的并发处理能力?

  • worker_processes: 设置 worker_processes 为与系统 CPU 核心数相同的数量,以充分利用多核 CPU。worker_processes 4; # 根据服务器的 CPU 核心数进行调整• 
  • worker_connections: 每个 worker 进程可以处理的最大连接数,增加此值可以提升并发能力。worker_connections 1024; # 每个工作进程最多处理 1024 个连接
  • events: 启用 epoll(Linux)或 kqueue(Mac)等高效的事件驱动模型,以提升 I/O 处理效率。
events {
    use epoll;  # 适用于 Linux 系统
    worker_connections 1024;
}

如何通过缓存优化 Nginx 性能?

HTTP 缓存

可以通过配置 proxy_cache 来缓存响应,避免每次请求都转发到后端服务。

http {
    proxy_cache_path /tmp/cache levels=1:2 keys_zone=my_cache:10m inactive=60m max_size=1g;
    server {
        listen 80;
        location / {
            proxy_cache my_cache;
            proxy_pass http://backend;
            proxy_cache_valid 200 1h;  # 对于 200 状态码的响应,缓存 1 小时
            proxy_cache_use_stale error timeout updating;  # 错误或超时情况下使用陈旧缓存
        }
    }
}

静态文件缓存

静态资源(如图片、CSS、JavaScript 等)适合缓存,可以通过设置缓存头来减少带宽消耗。

server {
    location /images/ {
        expires 30d;  # 设置缓存过期时间为 30 天
        add_header Cache-Control "public";
    }
}
 

优化 Nginx 的响应时间和带宽利用率?

开启 Gzip 压缩

Nginx 支持 Gzip 压缩,能够显著减小传输内容的大小,提高带宽利用率,并减少响应时间。

http {
    gzip on;
    gzip_min_length 1024;  # 启用 Gzip 压缩,且只对大于 1KB 的内容生效
    gzip_types text/plain text/css application/javascript application/json;
}

TCP_NOPUSH 和 TCP_NODELAY

在高延迟环境下,可以通过启用 TCP 优化选项来提高性能,减少等待数据包的时间。

server {
    listen 80;
    tcp_nopush on;  # 优化网络传输
    tcp_nodelay on;  # 降低延迟
}

优化传输协议

启用 HTTP/2 协议,它具有多路复用、头部压缩和请求优先级等特性,能显著提升网页加载速度。

server {
    listen 443 ssl http2;
    server_name example.com;
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/certificate.key;
}

如何配置 Nginx 的日志以减少 I/O 开销?

禁用访问日志

在高流量情况下,如果不需要访问日志,可以通过设置 access_log off; 来禁用访问日志。

server {
    listen 80;
    access_log off;
    location / {
        proxy_pass http://backend;
    }
}
 

日志缓冲

使用 log_format 指令定义自定义日志格式,并且利用 access_log 的缓冲机制来减少磁盘 I/O。可以设置 buffer 和 flush 参数来控制日志的刷新频率。

http {
    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 buffer=32k flush=5m;
}

如何优化 Nginx 配置以防止 DDoS 攻击?

限制请求速率

使用 limit_req 模块限制每个 IP 地址的请求速率。

http {
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    server {
        listen 80;
        location / {
            limit_req zone=req_limit burst=20 nodelay;
            proxy_pass http://backend;
        }
    }
}
 

限制每个客户端的最大连接数

通过 limit_conn 模块限制每个客户端的最大并发连接数。

http {
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    server {
        listen 80;
        location / {
            limit_conn conn_limit 1;  # 每个客户端最多 1 个并发连接
            proxy_pass http://backend;
        }
    }
}
 

增加连接超时

设置适当的连接超时和读取超时,防止长时间未处理的连接占用过多资源。

http {
    client_body_timeout 10s;
    client_header_timeout 10s;
    send_timeout 10s;
}
 

如何优化 Nginx 的 SSL/TLS 性能?

Nginx 在处理 HTTPS 时需要高效地配置 SSL/TLS,以减少加密解密的性能开销:

启用 SSL/TLS 会话缓存

通过缓存 SSL/TLS 会话来减少握手时间。

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1h;
 

使用现代加密算法

配置安全的加密套件,并禁用过时的协议。

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'HIGH:!aNULL:!MD5';
 

启用 OCSP Stapling

在线证书状态协议,是一种用于实时验证数字证书状态的协议。它允许客户端(如浏览器)查询证书颁发机构(CA)的OCSP响应器,以确定某个证书是否已被吊销。OCSP相比传统的证书吊销列表(CRL)具有更高的时效性和隐私保护性。

通过启用 OCSP Stapling 来提高 SSL/TLS 握手的速度。

ssl_stapling on;
ssl_stapling_verify on;
 

如何排查 Nginx 性能瓶颈?

监控 Nginx 状态

通过启用 Nginx 状态页面,实时监控 Nginx 的性能。

server {
    listen 8080;
    location /status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}

日志分析

检查错误日志、访问日志,尤其关注高延迟或 5xx 错误,找出可能的性能瓶颈。•

系统资源监控

通过监控 CPU、内存、网络带宽和磁盘 I/O 等系统资源,定位 Nginx 服务器的瓶颈所在。

Nginx如何实现高并发?

  • Master进程: 负责读取配置、绑定端口、管理Worker进程(平滑重启、热加载)。
  • Worker进程: 实际处理请求,采用非阻塞+epoll多路复用机制。
  • Epoll模型: 基于事件驱动,仅遍历活跃连接,复杂度O(1),支持百万级并发。
worker_processes auto;  # 匹配CPU核心数
worker_connections 10240; # 单个Worker最大连接数
use epoll;  # 明确指定事件模型
 

如何通过Slab分配器优化Nginx内存碎片?

总内存需求 = worker_processes × (worker_connections × 请求缓冲区 + 响应缓冲区)

优化配置

分级内存池管理
slab_size1m;  
slab_page_size4k;
 
限制单个请求内存
client_body_buffer_size16k;
client_header_buffer_size4k;
large_client_header_buffers832k;
 
连接级内存限制
connection_pool_size4096;
request_pool_size4k;

监控指标

查看内存碎片率
nginx -V 2>&1 | grep -o 'with-debug' && kill -USR1 $(cat /run/nginx.pid)
tail -f /var/log/nginx/error.log | grep slab

如何设计Nginx缓存策略防止高并发下的缓存穿透?

proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:1024m inactive=7d use_temp_path=off;
 
location / {
    proxy_cache mycache;
    proxy_cache_lockon;  # 防击穿:同一请求仅一个回源
    proxy_cache_key"$scheme$request_method$host$request_uri";
    proxy_cache_valid20030210m;
    proxy_cache_use_staleerror timeout updating;
    proxy_cache_background_updateon; # 后台更新缓存
}

如何利用Nginx+Lua实现动态限频

lua_shared_dict limit_counter 10m;
server {
    location / {
        access_by_lua_block {
            local limit_counter = ngx.shared.limit_counter
            local key = ngx.var.binary_remote_addr
            local req,_ = limit_counter:get(key)
 
            if req then
                if req > 100 then  # 每秒100次阈值
                    ngx.exit(503)
                else
                    limit_counter:incr(key,1)
                end
            else
                limit_counter:set(key,1,1)  # 过期时间1秒
            end
        }
    }
}
 

如何通过 Nginx 拦截 SQL 注入和 XSS 攻击?

set $block0;
if ($request_method !~ ^(GET|POST)$ ) { set$block1; }
 
if ($query_string~* "union.*select.*from") { set$block1; } # SQL注入检测
if ($args~* "<script.*>") { set$block1; } # XSS检测
 
location / {
    if ($block = 1) {
        return444;  # 静默丢弃攻击请求
    }
    # 其他业务逻辑
}

如何构建Nginx全维度监控指标?

nginx.conf 开启 Stub Status

location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}

Prometheus 配置

job_name: 'nginx'
static_configs:
 

 Grafana看板: 包含QPS、连接数、4xx/5xx错误率、Upstream响应时间分布

如何实现Nginx的零停机热升级?

  1. 备份旧版本二进制文件

cp /usr/sbin/nginx /usr/sbin/nginx.old

  1. 编译新版本(需保留原 configure 参数)
./configure --with-http_v2_module --with-stream=dynamic...
make && make install
  1. 向主进程发送 USR2 信号启动新进程 kill -USR2 $(cat /run/nginx.pid)

  2. 逐步关闭旧 Worker 进程

kill -WINCH $(cat /run/nginx.pid.oldbin)

  1. 强制回滚(若新版本异常)
mv /usr/sbin/nginx.old /usr/sbin/nginx
kill -HUP $(cat /run/nginx.pid.oldbin)

如何基于Nginx+GeoIP实现跨国流量调度?

GeoIP数据库配置

geoip_country /usr/share/GeoIP/GeoIP.dat;
map$geoip_country_code$backend {
    default  us.web.service;  # 默认美国集群
    CN       cn.web.service;  # 中国用户
    JP       jp.web.service;  # 日本用户
}
 
server {
    location / {
        resolver8.8.8.8 valid=30s;  # 动态DNS解析
        proxy_pass http://$backend;
        proxy_next_upstreamerror timeout http_500;
    }
}

Nginx Ingress如何实现自动弹性伸缩?

在Kubernetes+Serverless架构中,Nginx Ingress如何实现自动弹性伸缩?

水平扩展:基于 HPA 监控 CPU/内存 自动扩缩 Pod 数量

智能路由:通过 Nginx Ingress Annotation 实现金丝雀发布

nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"

冷启动优化:预加载 Nginx 配置到内存池,减少首次请求延迟

将 HTTP/1.1 升级为 HTTP/2 并优化性能?

http {
    # 开启HTTP/2
    listen443 ssl http2;  
 
    # 优化连接复用
    keepalive_timeout300s;
    keepalive_requests10000;
 
    # 头部压缩优化
    gzipon;
    gzip_min_length1k;
    gzip_comp_level3;
    gzip_types text/plain application/json;
 
    # 调整缓冲区应对大 Header
    http2_max_field_size16k;
    http2_max_header_size64k;
 
    # 动态调整窗口大小
    http2_body_preread_size128k;
    http2_streams_index_size1024;
}
 
  • 连接复用率提升 40%+
  • 首字节时间(TTFB)降低 30%
  • 头部传输体积减少 50%

设计防御大规模 CC 攻击的分布式限流方案

分布式限流(Redis集群)

limit_req_zone $binary_remote_addr zone=global_limit:10m rate=100r/s;
limit_req zone=global_limit burst=200 delay=10;

动态规则API

location /api/limit_rules {
    # 接受JSON格式规则更新
    proxy_pass http://rule_engine;
 
    # 实时加载新规则
    nginx -s reload && lua_shared_dict limit_rules 10m;
}

Lua动态过滤

access_by_lua_block {
    local rules = ngx.shared.limit_rules
    local ip = ngx.var.remote_addr
    if rules:get(ip) == "block" then
        ngx.exit(444)
    end
}

防御指标

  • 单IP QPS限制精度 ±5%
  • 规则更新延迟 < 500ms
  • 10Gbps攻击流量过滤率 99.9%

如何优化Nginx Ingress Controller性能?

在Kubernetes中如何优化Nginx Ingress Controller性能?

水平自动扩缩

autoscaling:
  enabled:true
minReplicas:3
maxReplicas:100
metrics:
    -type:Pods
      pods:
        metric:
          name:nginx_connections_active
        target:
          type:AverageValue
          averageValue: 1000
 

内核参数调优(DaemonSet)

sysctls:
name: net.core.somaxconn
value: "65535"
name: net.ipv4.tcp_tw_reuse
value: "1"
 

零拷贝优化

env:
name: NGINX_ENABLE_TCP_NOPUSH
value: "true"

优化效果

  • Pod启动时间缩短至 2s
  • 长连接复用率提升至 95%
  • 单Pod支撑并发连接数突破 50k

如何通过火焰图定位 Nginx CPU 热点问题

安装 SystemTap 工具链 yum install systemtap kernel-devel-$(uname -r)

采集CPU样本(持续30秒)

stap -v -DMAXSKIPPED=99999 -DSTP_NO_OVERLOAD \
  -DMAXTRYLOCK=1000 -DMAXMAPENTRIES=100000 \
  -d nginx --ldd -c 'global s; probe process("/usr/sbin/nginx").function("*") { s[probefunc()] <<< 1; }' \
  -o nginx_cpu.flame

生成火焰图

git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/stackcollapse-stap.pl nginx_cpu.flame | ./FlameGraph/flamegraph.pl > cpu.svg
 

典型问题定位

  • 正则表达式回溯(优化rewrite规则)
  • 阻塞式文件IO(启用aio threads)
  • 第三方模块死锁(检查OpenSSL版本兼容性)

location优先级

匹配类型优先级顺序说明
location = /uri最高精确匹配
location ^~ /prefix次高前缀匹配(阻止后续正则匹配)
location ~ \.regex$正则匹配(区分大小写,按顺序执行)
location ~* \.regex$正则匹配(不区分大小写,按顺序执行)
location /prefix普通前缀匹配
location /最低默认匹配

数据库

MySQL

这部分详见 mysql-basic

Redis

这部分详见 redis-basic

MongoDB

这部分详见 mongodb-basic

业务设计

PHP 短信验证码防刷机制

1)、时间限制:60 秒后才能再次发送从发送验证码开始,前端(客户端)会进行一个 60 秒的倒数,在这一分钟之内,用户是无法提交多次发送信息的请求的。这种方法虽然使用得比较普遍,但是却不是非常有用,技术稍微好点的人完全可以绕过这个限制,直接发送短信验证码。

2)、手机号限制:同一个手机号,24 小时之内不能够超过 5 条对使用同一个手机号码进行注册或者其他发送短信验证码的操作的时候,系统可以对这个手机号码进行限制,例如,24 小时只能发送 5 条短信验证码,超出限制则进行报错(如:系统繁忙,请稍后再试)。然而,这也只能够避免人工手动刷短信而已,对于批量使用不同手机号码来刷短信的机器,这种方法也是无可奈何的。

3)、短信验证码限制:30 分钟之内发送同一个验证码网上还有一种方法说:30 分钟之内,所有的请求,所发送的短信验证码都是同一个验证码。第一次请求短信接口,然后缓存短信验证码结果,30 分钟之内再次请求,则直接返回缓存的内容。对于这种方式,不是很清楚短信接口商会不会对发送缓存信息收取费用,如果有兴趣可以了解了解。

4)、前后端校验:提交 Token 参数校验这种方式比较少人说到,个人觉得可以这种方法值得一试。前端(客户端)在请求发送短信的时候,同时向服务端提交一个 Token 参数,服务端对这个 Token 参数进行校验,校验通过之后,再向请求发送短信的接口向用户手机发送短信。

5)、唯一性限制:微信产品,限制同一个微信 ID 用户的请求数量如果是微信的产品的话,可以通过微信 ID 来进行识别,然后对同一个微信 ID 的用户限制,24 小时之内最多只能够发送一定量的短信。

6)、产品流程限制:分步骤进行例如注册的短信验证码使用场景,我们将注册的步骤分成 2 步,用户在输入手机号码并设置了密码之后,下一步才进入验证码的验证步骤。

7)、图形验证码限制:图形验证通过后再请求接口用户输入图形验证码并通过之后,再请求短信接口获取验证码。为了有更好的用户体验,也可以设计成:一开始不需要输入图形验证码,在操作达到一定量之后,才需要输入图形验证码。具体情况请根据具体场景来进行设计。

8)、IP 及 Cookie 限制:限制相同的 IP/Cookie 信息最大数量使用 Cookie 或者 IP,能够简单识别同一个用户,然后对相同的用户进行限制(如:24 小时内最多只能够发送 20 条短信)。然而,Cookie 能够清理、IP 能够模拟,而且 IP 还会出现局域网相同 IP 的情况,因此,在使用此方法的时候,应该根据具体情况来思考。

9)、短信预警机制,做好出问题之后的防护以上的方法并不一定能够完全杜绝短信被刷,因此,我们也应该做好短信的预警机制,即当短信的使用量达到一定量之后,向管理员发送预警信息,管理员可以立刻对短信的接口情况进行监控和防护。

如何设计一个高并发的系统

  1. 数据库的优化,包括合理的事务隔离级别、SQL 语句优化、索引的优化
  2. 使用缓存,尽量减少数据库 IO
  3. 分布式数据库、分布式缓存
  4. 服务器的负载均衡

参考文档: https://mp.weixin.qq.com/s/Wp6ErsgUKOYOjry7eRczEQ

https://mp.weixin.qq.com/s/BgR7x_JNoMc2iHlkg8jwDw

https://mp.weixin.qq.com/s/ZALPGbsvYpKv66POZ4V6dw

← Previous postMySQL夯实基础