一、Nginx 方向代理实现

正向代理

正向代理是指客户端与目标服务器之间增加一个代理服务器,客户端直接访问代理服务器,再由代理服务器访问目标服务器并返回结果给客户端,这个过程中客户端需要知道代理服务器的地址,并配置连接,如:VPN
image.png

反向代理

指客户端访问目标服务器,在服务器内部有一个统一接如网关将请求转发至后端真正处理的服务器并返回结果,这个过程中客户端不需要知道代理服务器的地址,代理对客户端而言是透明的
image.png


正向代理 反向代理
代理服务器的位置 客户端与服务器都能连接他们的位置 目标服务器内部
主要作用 屏蔽客户端IP,集中式缓存,解决客户端不能直接连接服务器的问题 屏蔽服务端内部的实现,负载均衡、缓存
应用场景 爬虫、VPN、maven的nexus服务 Nginx、Apache 负载均衡应用

Nginx 代理配置

Nginx 代理只需要在 location 中配置 proxy_pass 属性即可,其指向代理服务器的地址即可,如果地址最后添加 / 表示代理地址中忽略 location 配置的地址

  1. # 正向代理服务
  2. location /baidu.html {
  3. proxy_pass http://www.baidu.com;
  4. }
  5. # 反向代理服务
  6. location /proxy {
  7. proxy_pass http://127.0.0.1:8080;
  8. }

代理相关参数

  1. proxy_pass http://upstream; # 代理服务
  2. proxy_redirect off; # 是否允许重定向
  3. proxy_set_header Host $host; # 传 header 参数至后端服务器
  4. proxy_set_header X-Forwarded-For $remote_addr; # 设置 request header 即客户端 IP 地址
  5. proxy_connect_timeout 90; # 连接代理服务器超时时间
  6. proxy_send_timeout 90; # 请求发送最大时间
  7. proxy_read_timeout 90; # 读取最大时间
  8. proxy_buffer_size 4k;
  9. proxy_buffers 4 32k;
  10. proxy_busy_buffer_size 64k;
  11. proxy_temp_file_write_size 64k;

二、负载均衡配置与参数解析

通过 proxy_pass 可以把请求代理至后端服务器,但是为了实现更高的负载及性能,我们的后端服务通常是多个,这个时候可以通过 upstream 模块实现负载均衡

负载均衡配置

  1. http {
  2. upstream backend {
  3. server backend1.example.com weight=5;
  4. server backend2.example.com:8080;
  5. server unix:/tmp/backend3;
  6. server backup1.example.com:8080 backup;
  7. server backup2.example.com:8080 backup;
  8. }
  9. server {
  10. location / {
  11. proxy_pass http://backend;
  12. }
  13. }

upstream 相关参数

  1. server http://xxxx:port # 反向代理服务器地址
  2. # 以下配置都是配置再 server 的后面,用空格进行分割
  3. weight=number # 权重,越大代表权重越高,默认负载均衡算法(轮询)中才会生效
  4. max_fails=number # 失败多少次,认为主机已经挂掉,踢出
  5. fail_timeout=time # 踢出后重新探测时间,默认 10s
  6. backup # 备用服务,当正式服务挂掉以后,backup 才会生效
  7. max_conns=number # 允许最大连接数
  8. slow_start=time # 当节点恢复,不立即加入,而是等待 slow_start 指定的时间后,再加入到服务队列
  9. down # 标记当前服务不可用

负载均衡算法

  1. ll + weight: 轮询加权重(默认)
  2. ip_hash: 基于 ip hash 计算,用于保持 session 一致性
  3. url_hash: 静态资源缓存,节约存储,加快访问速度(第三方)
  4. least_conn: 最少连接(第三方)
  5. least_time: 最少响应时间,计算节点平均响应时间,然后取响应最快的那个,分配更高的权重(第三方)

ip_hash
  1. upstream backend {
  2. ip_hash;
  3. server backend1.example.com;
  4. server backend2.example.com;
  5. server backend3.example.com down;
  6. server backend4.example.com;
  7. }

三、Nginx 高速缓存

1、案例分析

某电商平台商品详情页需要实现 700+ QPS,如何着手去做?

解决方案:

  1. 采用 AJAX 动态加载所需的内容
  2. 采用 key/value 缓存详情页主体

image.png

问题

当 QPS 达到 500+ 时,压测就很难再提高了

分析原因

一个详情页的 HTML,主体达平均 150kb,那么在 500+ QPS 已经接近千M局域网带宽的极限了(约 100 M 左右),此时问题并不在后台的压力,而是在 I/O 达到了访问极限,所以必须减少内网通信

基于 Nginx 静态缓存的解决方案

将静态文件缓存至 nginx,可以减少两次 I/O,大大提高了性能

image.png

Nginx 静态缓存基本配置

1、在 http 元素下添加缓存区声明

  1. # proxy_cache_path 缓存路径
  2. # levels 缓存层级及目录位数
  3. # keys_zone 缓存区内存大小
  4. # inactive 有效期
  5. # max_size 硬盘大小
  6. proxy_cache_path /data/nginx/cahce_data_shop levels=1:2 keys_zone=cache_data_shop:500m inactive=20d max_size=1g;

2、为 location 指定缓存策略

  1. # 指定缓存区
  2. proxy_cache cache_data_shop;
  3. # 以全路径 MD5 值作为 key
  4. proxy_cache_key $host$uri$is_args$args;
  5. # 对不同的 http 状态码设置不同的缓存时间
  6. proxy_cache_valid 200 304 12h;

参数详解
父元素 名称 描述
http proxy_cache_path 指定缓存的根路径
levels 缓存目录层级,最高三级,每层1-2个字符标识,如:1:2 表示三层,最后一层为真实缓存数据
keys_zone 缓存模块名称及内存大小,如:cache_data:500m,表示声明一个缓存名为 cache_data,大小为 500m,超过大小后最早被缓存的数据将被清除
inactive 最长闲置时间,如:10d,如果一个数据 10 天内都没有被访问,将会被清除
max_size 缓存区硬盘最大值,超出范围,闲置数据将被清除
location proxy_cache 指定缓存区,与 keys_zone 设置的名称需要一致
proxy_cache_key 缓存的 key 值,如:$host$uri$is_args$args 则会以 URL 全路径 md5 值作为 key
proxy_cache_valid 为不同状态码设置缓存有效期

3、缓存的清除

该功能可以用第三发模块 ngx_cache_purge 实现

为 nginx 添加 ngx_cache_purge 模块
  1. # 下载 ngx_cache_purge
  2. wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
  3. # 解压
  4. tar -zxvf ngx_cache_purge-2.3.tar.gz
  5. # 查看已安装模块,复制已安装的模块
  6. ./sbin/nginx -V
  7. configure arguments: --with-pcre --with-http_stub_status_module --with-http_ssl_module
  8. # 进入 nginx 源安装目录
  9. ./configure --with-pcre --with-http_stub_status_module --with-http_ssl_module --add-module=/home/eric/package/ngx_cache_purge-2.3
  10. # make
  11. make
  12. # 停止 nginx
  13. nginx -s stop
  14. # 查看模块是否安装成功
  15. ./objs/nginx -V
  16. configure arguments: --with-pcre --with-http_stub_status_module --with-http_ssl_module --add-module=/home/eric/package/ngx_cache_purge-2.3
  17. # 进入 objs 目录,将新的 nginx 替换原来的 nginx
  18. cp objs/nginx /usr/local/nginx/
  19. # 启动
  20. ./sbin/nginx

配置清除缓存 API
  1. location ~ /purge(/.*) {
  2. # 允许访问的 IP
  3. allow 127.0.0.1;
  4. allow 192.168.0.1;
  5. # 禁止访问的 IP
  6. deny all;
  7. # 配置清除指定缓存区和路径(与 proxy_cahce_key 一致,$1 代表 uri)
  8. proxy_cache_purge cache_data_shop $host$1$is_args$args;
  9. }

这里要注意的坑:purge 是匹配完整的 URI 请求路径,如果你的访问是 http://127.0.0.1/upsteam?a=1,那么你 purge 的清除地址就是 http://127.0.0.1/purge/upsteam?a=1

四、性能参数调优

**

worker_processes 进程数量

每个 worker 进程都是单线程的进程,它们会调用各个模块以实现多种多样的功能。如果这些模块确认不会出现阻塞式的调用,那么,有多少 CPU 内核就应该配置多少个进程;反之,如果有可能出现阻塞式调用,那么需要配置稍多一些的 worker 进程。例如,如果业务方面会致使用户请求大量读取本地磁盘上的静态资源文件,而且服务器上的内存较小,以至于大部分的请求访问静态资源文件时都必须读取磁盘(磁头的寻址是缓慢的),而不是内存中的磁盘缓存,那么磁盘 I/O 调用可能会阻塞住 worker 进程少量时间,进而导致服务整体性能下降。

语法:worker_processes number;
默认:worker_processes 1

worker_connections 最大连接数

每个 worker 进程的最大连接数

语法:worker_connections number;
默认:worker_connections 1024

worker_cpu_affinity 绑定 CPU 内核


绑定 Nginx worker 进程到指定的 CPU 内核

为什么要绑定 worker 进程到指定的 CPU 内核呢?假定每一个 worker 进程都是非常繁忙的,如果多个 worker 进程都在抢同一个 CPU,那么这就会出现同步问题。反之,如果每一个 worker 进程都独享一个 CPU,就在内核的调度策略上实现了完全的并发。

例如,如果有 4 颗 CPU 内核,就可以进行如下配置:

worker_processes 4;
语法:worker_cpu_affinity 1000 0100 0010 0001;

注意 worker_cpu_affinity 配置仅对 Linux 操作系统有效。

Nginx worker 进程优先级设置

语法:worker_priority nice;
默认:worker_priority 0;

优先级由静态优先级和内核根据进程执行情况所做的动态调整(目前只有±5的调整)共同决定。nice 值是进程的静态优先级,它的取值范围是 –20~+19,–20 是最高优先级,+19 是最低优先级。因此,如果用户希望Nginx占有更多的系统资源,那么可以把nice值配置得更小一些,但不建议比内核进程的 nice 值(通常为–5)还要小

Nginx worker 进程可以打开的最大句柄描述符个数


语法: worker_rlimit_nofile limit;
默认:**空

更改 worker 进程的最大打开文件数限制。如果没设置的话,这个值为操作系统的限制。设置后你的操作系统和Nginx可以处理比“ulimit -a”更多的文件,所以把这个值设高,这样nginx就不会有“too many open files”问题了。

是否打开 accept 锁


语法:accept_mutext [on|off]
默认:**accept_mutext on;

accept_mutex 是 Nginx 的负载均衡锁,当某一个 worker 进程建立的连接数量达到 worker_connections 配置的最大连接数的 7/8 时,会大大地减小该 worker 进程试图建立新 TCP 连接的机会,accept 锁默认是打开的,如果关闭它,那么建立 TCP 连接的耗时会更短,但 worker 进程之间的负载会非常不均衡,因此不建议关闭它。

使用 accept 锁后到真正建立连接之间的延迟时间


语法:accept_mutex_delay Nms;
默认:**accept_mutex_delay 500ms;

在使用 accept 锁后,同一时间只有一个 worker 进程能够取到 accep t锁。这个 accept 锁不是堵塞锁,如果取不到会立刻返回。如果只有一个 worker 进程试图取锁而没有取到,他至少要等待 accept_mutex_delay 定义的时间才能再次试图取锁。