反向代理

负载均衡策略 round-bobin 算法, 依次轮训, 挨个进行

  • weight 权重, 默认1
  • max_conns server的最大并发连接数, 仅作用于单worker
  • max_fails 在 fail_timeout 时间段内, 最大的失败次数. 当达到最大失败时候, 会在 fail_timeout 秒内这台server 不允许再次被选中
  • fail_timeout 单位时间秒, 默认10秒. 指定一段时间内, 最大是吧次数 max_fails. 到达 max_fails后, 该server不能访问的时间

对上游(比如tomcat)服务使用 keepalive 长连接

proxy_http_version 1.1; # 1.0 不支持长链接, 所以重置下, 为1.1
proxy_set_header Connection "";
upstream rrups {
    server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
    server 127.0.0.1:8012;
    keepalive 32;
}

server {
    server_name rrups.liuhonghe.me;
    error_log myerror.log info;

    location /{
        proxy_pass http://rrups;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

负载均衡策略 ip_hash 算法

以ip为基础, 即使上游服务端挂了, 也会一直访问一台服务器

log_format  varups  '$upstream_addr $upstream_connect_time $upstream_header_time $upstream_response_time '
                        '$upstream_response_length $upstream_bytes_received '
                        '$upstream_status $upstream_http_server $upstream_cache_status';

upstream iphashups {
    #ip_hash;
    # curl -H 'X-Forwarded-for: 192.168.1.100 iphash.liuhonghe.me?username=abcdefg'
    # hash $host$uri consistent; # 一致性 hash 算法, 避免缓存失效
    hash user_$arg_username consistent; # 使用特定字符串作为hash, 除非username更改, 否则不会更改
    server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5; # 使用ip_hash 后权重不生效
    server 127.0.0.1:8012 weight=1;
}

server {
    set_real_ip_from  116.62.160.193;
    real_ip_recursive on;
    real_ip_header X-Forwarded-For;
    server_name iphash.liuhonghe.me;
    error_log myerror.log info;
    access_log logs/upstream_access.log varups;

    location /{
        proxy_pass http://iphashups;
        proxy_http_version 1.1;
            proxy_set_header Connection "";
    }
}

upstream_least_conn

优先选择连接最少的上游服务器

# 语法
least_conn

upstream 模块顺序

  1. hash
  2. ip_hash
  3. least
  4. random
  5. keepalive
  6. zone

upstream 提供的变量

  • upstream_addr 上游服务器的IP地址, 格式为刻度的字符串
  • upstream_connect_time 与上游服务建立连接消耗的时间, 单位为妙
  • upstream_header_time 接收上游服务发挥相应中http头部所消耗的时间
  • upstream_response_time 接收完整的上游服务器相应所想好的时间
  • upstream_http_名称 从上游服务返回的相应头部的值
  • upstream_bytes_received 与上游服务接收到的相应长度
  • upstream_response_length 与上游服务返回的相应包体长度
  • upstream_status 上游服务返回的http相应中的状态码.如果未连接, 502
  • upstream_cookie_名称 从上游服务返回的响应头Set-Cookie中取出cookie值
  • upstream_trailer_名称 从上游服务的相应尾部取到的值
log_format  varups  '$upstream_addr $upstream_connect_time $upstream_header_time $upstream_response_time '
                        '$upstream_response_length $upstream_bytes_received '
                        '$upstream_status $upstream_http_server $upstream_cache_status';

proxy 模块

抓包调试

tcpdump -i lo port 8012 -A -s 0

upstream proxyupstream {
    server 127.0.0.1:8012 weight=1;
}

server {
    server_name proxy.liuhonghe.me;
    error_log logs/myerror.log debug;

    location / {
        proxy_pass http://proxyupstream;
        #proxy_method POST;

        proxy_hide_header aaa;
        proxy_pass_header server;
        proxy_ignore_headers X-Accel-Limit-Rate;

        #proxy_pass_request_headers off; # 用户的请求不会向上游发放
        #proxy_pass_request_body off; # 不会发送body到上游
        #proxy_set_body 'hello world!'; # 手动构造一个body内容
        #proxy_set_header name ''; # 修改或者添加一个头部, '' 为空, 就不发送
        proxy_http_version 1.1; # http 1.1
        proxy_set_header Connection "";
    }

    listen 80; # managed by Certbot

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/proxy.taohui.tech/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/proxy.taohui.tech/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
        listen 127.0.0.1:8011;
    default_type text/plain;
    limit_rate 1;
        return 200 '8011 server response.\n';
}

server {
        listen 8013;
        default_type text/plain;
        return 500 '8013 Server Internal Error.\n';
}

server {
        listen 8012;
    default_type text/plain;
    #client_body_in_single_buffer on;
    #add_header Cache-Control 'max-age=3,stale-while-revalidate=3';
    #add_header Vary *;
    #add_header X-Accel-Expires 3;
    root html;

    location / {
        #add_header aaa 'aaa value';
        #add_header X-Accel-Limit-Rate 10;
    }

    location /test {
            return 200 '8012 server response.
uri: $uri
method: $request_method # 请求方法
request: $request 
http_name: $http_name
curtime: $time_local
\n';
    }
}

接收客户端请求的包体

proxy_request_buffering

  • on 客户端网速慢, 上游服务并发处理能力低, 适应高吞吐量场景
  • off 更及时的相应, 降低nginx读写磁盘的消耗, 一旦开始发送内容, proxy_next_upstream 功能失效

客户端包体的接收

  • 若接收头部时已经接收完全部包体, 则不分配

  • 若剩余待接收包体的长度小于client_body_buffer_size , 则仅分配所需大小

  • 分配client_body_buffer_size 大小内存接收包体.关闭包体缓存, 该内存上内容及时发给上游; 打开包体缓存, 该段大小内存用完时, 写入临时文件, 释放内存

  • client_body_buffer_size size;

  • client_body_in_single_buffer on|off;

最大包体长度限制

  • client_max_body_size size; default 1m; 仅对请求头部中含有 Content-Length有效超出最大长度后, 返回413错误. 比如上传

临时文件路径格式

  • client_body_temp_path path; # 路径
  • client_body_in_file_only on|clean|off; # 包体必须存在文件中|处理完删除|不会存到文件中

读取包体时的超时

  • client_body_timeout time; # tcp 报文接收超时返回408

与上游进行连接

  • proxy_connect_timeout time; # 60s, 控制握手的时间, 超时 返回502
  • proxy_next_upstream http_502;
  • proxy_socket_keepalive on | off; # 防止资源浪费
  • proxy_bind address [transparent] off; # 主动去绑定 ip 地址
  • proxy_ignore_client_abort on | off; # 与客户端关闭连接, 默认off
  • proxy_send_timeout # 向上游服务发送请求

接收上游的响应

  • proxy_buffer_size 4k | 8k; # 接收上游的http响应头部.
  • proxy_buffers 8 4k|8k; # 接收上游的http包体
  • proxy_buffering on; # 尽快释放上游的链接
  • proxy_max_temp_file_size 1024m; # 限制写入磁盘文件的最大值
  • proxy_temp_file_write_size 8k|16k; # 每一次向临时文件中写入的字节数
  • proxy_temp_path path [level1 [level2[level3]]]; # 存放临时文件的目录
  • proxy_busy_buffers_size 8k|16k; # 及时转发包体
  • proxy_read_timeout 60s; # 两次读取操作时间限制
  • proxy_limit_rate 0; # 限制读取上游的响应, 0 不限制
  • proxy_store_access user:rw;
  • proxy_store on|off|string;
upstream proxyups {
    server 127.0.0.1:8012 weight=1;
}

server {
    server_name store.liuhonghe.me;
    error_log logs/myerror.log debug;
    root /tmp; # 指定了一个目录

    location / {
        proxy_pass http://proxyups;
        proxy_store        on; # 存放在 /tmp
        proxy_store_access user:rw group:rw all:r;
    }

    listen 80; # managed by Certbot
}

处理上游的响应头部

  • proxy_ignore_headers field ...; # 禁用上游响应头部的功能
  • 可以禁用的头部
    • X-Accel-Redirect 由上游服务器指定再nginx内部重定向, 控制请求的执行
    • X-Accel-Limit-Rate 由上游设置发往客户端的速度限制, 等同 limit_rate指令
    • X-Accel-Buffering 由上游控制是否缓存上游的响应
    • X-Accel-Charset 由上游控制 Content-Type中的Charset
    • 缓存相关
      • X-Accel-Expires 设置响应再nginx中的缓存时间,单位秒
      • Expires 控制nginx 缓存时间, 优先级低于 X-Accel-Expires
      • Cache-Control 控制nginx缓存时间, 优先级低于X-Accel-Expires
      • set-Cookie 响应中出现set-Cookie 则不缓存, 可通过 proxy_ignore_headers 禁止生效
      • Vary 响应中出现 Vary:* 则不缓存, 同样可禁止生效
upstream proxyupstream {
    server 127.0.0.1:8012 weight=1;
}

server {
    server_name proxy.liuhonghe.me;
    error_log logs/myerror.log debug;

    location / {
        proxy_pass http://proxyupstream;
        #proxy_method POST;

        proxy_hide_header aaa; # 对于上游响应中的头部, 设置不向客户端转发
        proxy_pass_header server; # 返回上游服务的server
        proxy_ignore_headers X-Accel-Limit-Rate; # 禁用limit限速头部

        #proxy_pass_request_headers off;
        #proxy_pass_request_body off;
        #proxy_set_body 'hello world!';
        #proxy_set_header name '';
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    listen 80; # managed by Certbot

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/proxy.liuhonghe.me/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/proxy.liuhonghe.me/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

上游返回失败时的处理办法

  • proxy_next_upstream on|off|error ...;
upstream nextups {
    server 127.0.0.1:8013;
    server 127.0.0.1:8011;
}

server {
    server_name nextups.liuhonghe.me;
    error_log  logs/myerror.log  debug;
    #root html/;
    default_type text/plain;
    
    error_page 500 /test1.txt;

    location / {
        proxy_pass http://nextups;
    }

    location /test {
    }

    location /error {
        proxy_pass http://nextups;
        proxy_connect_timeout 1s;
        proxy_next_upstream error; # 后端一个不存在, 会自动转到好用的机器
    }

    location /intercept {
        proxy_intercept_errors on; # 发生错误返回上面的error_page
        proxy_pass http://127.0.0.1:8013;
    }

    location /httperr {
                proxy_next_upstream http_500;
                proxy_pass http://nextups;
        }

}

error_page 拦截上游失败响应

  • proxy_intercept_errors on|off;