Nginx服务之代理和负载


纸上得来终觉浅,绝知此事要躬行。

Nginx服务之代理和负载


0. 性能分析

用于反向代理,HAProxy是支持最全面,成本最低的方案。

0.1 缺点分析

Nginx 社区版的缺点(反向代理和负载均衡功能上)

  • Session 会话持久保持
    • Nginx官方给出的解决方案是使用ip_hash调度算法,根据访问客户端的源IP地址进行请求分发调度到后端服务器的。表面上看起来没有什么问题,但是实际应用起来就会发现几乎不可行。
    • 比如为学校做一个选课系统,服务器放在服务网段,学生在普通网段,两个网段互通全靠NATNginx收到的IP全都是同一个源IP,那么这个负载均衡没有就没有生效。
  • 后端服务器的健康监测
    • Nginx官方给出的解决方案是被动监测的方式。
    • 如下例所示,意思就是如果 9191 这个端口某次连接超时或者失败次数超过了 max_fails 次,就会被判定为服务不可用,然后等待 fail_timeout 这么长时间之后自动认为其可用,不管到底真的修复了没有。然后再次重试 max_fails 次。
    • 这还不算最坑爹的,更麻烦的是在 Nginx 上你无法查看各个后端节点的状态,不知道哪台宕机了或者卡了不好用了。如果你后端有一百台被负载均衡的服务器,光检查可用性这一点 Nginx 社区版就完败。
# 被动监测
server localhost:9191 max_fails=1 fail_timeout=40s;

连接和流量的统计功能

  • 连接和流量的统计功能,也适用于检查后端健康情况和当前的并发连接数的。这个功能社区版默认还是没有的,需要在编译时添加一个官方支持的模块(--with-http_stub_status_module模块)

Nginx服务之代理和负载

  • 同样是反向代理负载均衡的HAProxyNGINX+就做的非常好,下面和上面可以对比一下看看。

Nginx服务之代理和负载 - Nginx社区版

Nginx服务之代理和负载 - HAProxy和NGINX+


0.2 替代方案

如何选择负载均衡器呢(一般来说)

  • 1、基于Cookie的会话保持
  • 2、监控后台服务器的负载状态和健康状况,主动进行健康检查
  • 3、支持以最小连接数为基准的负载均衡分发策略

对于 HAProxy 来说

  • 上述功能全部都可以实现,而且全部是官方支持;
  • 不需打补丁,直接在yum源上安装就可以全部实现;
  • 据网上的资料,HAProxy的并发数还要高于NGINX
  • HAProxy还可以支持TCP连接的负载均衡,这一点是NGINX社区版和plus1.9版本之后才实现的功能,而Tengine由于基础版本才1.8.1所以并不支持;

对于 Tengine 来说

  • 1、2、3 也可以支持,但是 1、3 不能同时支持;
  • 后台监控页面比较简陋,看不到具体每台节点的负载情况;
  • 也就是说如果使用Cookie作为会话保持策略,那就不能再以最少连接数作为分发策略了,只能使用轮训(round_robin),这样的话很容易出现某些服务器占用超级高,然后其他服务器几乎没有负载的糟糕情况;

补充说明

  • 对于Nginx puls来说,1、2、3 都可以支持,但是由于价格太过昂贵,所以我也没试过其真实的效果;
  • 对于硬件负载均衡器F5A10来说,1、2、3 都可以支持,而且基本没什么大问题;

1. 反向代理

ngx_http_proxy_module模块提供

1.1 使用格式

  • 格式分类
# 【基本使用方式】
# /uri --> /newuri
# 使用newuri替换原有的uri请求地址
location /uri {
    proxy_pass http://back_server[:port]/newuri;
}

# 【模式匹配特例】
# /uri --> /uri
# 但使用模式匹配的话(~或~*),请求的uri会添加在newuri后面
# proxy_pass设置的代理服务地址不能添加任何路径,否则会报错
location ~* /uri {
    proxy_pass http://back_server[:port];
}

# 【URL重定向特例】
# /uri --> /reurl
# 但使用URL重定向将uri替换成reuri,则proxy_pass的newuri就无效了
location /uri {
    rewrite reuri;
    proxy_pass http://back_server[:port]/newuri;
}
  • 模式示例
# 【基本使用方式】
location /bbs {
    proxy_pass http://192.168.100.2:80/newbbs;
}

# 【模式匹配特例】
location ~* \.(jpg|png|gif)$ {
    proxy_pass http://192.168.100.2;
}

# 【URL重定向特例】
location /bbs {
    rewrite /rebbs/;
    proxy_pass http://192.168.100.2:80/newbbs/;
}
  • 实例说明

Nginx服务之代理和负载

# 通过proxy_set_header设置自定义变量给后端服务器使用
# $host表示客户端请求的主机名,用于虚拟主机中使用
# $remote_addr表示客户端IP地址,X-Real-IP用于后端服务器记录日志
location / {
    proxy_set_header  Host      $host;
    proxy_set_header  X-Real-IP $remote_addr;
    proxy_pass        http://localhost:8000;
}
# 设置真实scheme协议
map $http_x_scheme  $real_http_x_scheme {
    default $http_x_scheme;
    "" $scheme;
}

1.2 常用指令

【1】proxy_pass

  • 语法格式
    • proxy_pass URL;
  • 含义解释
    • 该指令可以设置代理服务器的协议、地址和端口
    • 该指令可以定义本地路径和后端服务器的映射关系
  • 作用范围
    • locationif in locationlimit_except
# 使用http或https定义
proxy_pass http://localhost:8000/uri/;

# 使用UNIX域套接字路径
proxy_pass http://unix:/tmp/backend.socket:/uri/;

【2】proxy_set_header

  • 语法格式
    • proxy_set_header field value;
  • 含义解释
    • 允许重新定义或者添加发往后端服务器的请求头
    • value可以包含文本、变量或者它们的组合
    • 当且仅当当前配置级别中没有定义proxy_set_header指令时,会从上面的级别继承配置
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_set_header Host $proxy_host;
    • proxy_set_header Connection close;
# 定义传递给后端服务器的Host变量
proxy_set_header Host       $host;

# 此外,服务器名可以和后端服务器的端口一起传送
proxy_set_header Host       $host:$proxy_port;

# 如果某个请求头的值为空,那么这个请求头将不会传送给后端服务器
proxy_set_header Accept-Encoding "";

【3】proxy_http_version

代理服务器本身存在缓存,用户请求之后立即返回,所有让第二阶段(代理服务器和后端服务器之间的连接方式)一直保持长连接其实没有必要,可以根据业务情况让其使用短连接方式通信,提高效率。

  • 语法格式
    • proxy_http_version 1.0|1.1;
  • 含义解释
    • 设置代理使用的HTTP协议版本
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_http_version 1.0
# HTTP的1.1版本推荐在使用keepalive连接时一起使用
proxy_http_version 1.1;

【4】proxy_limit_rate

  • 语法格式
    • proxy_limit_rate rate;
  • 含义解释
    • 设置代理服务器的响应速率,0表示不限速,单位为字节/秒
    • 限制的是一个请求的响应速率,如果同时启动两个,则限制速率翻倍
    • 只工作在响应缓存(buffering)开启的情况下
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_limit_rate 0;
proxy_limit_rate 100k;

【5】proxy_method

  • 语法格式
    • proxy_method method;
  • 含义解释
    • 指定要使用的HTTP方法请求转发到代理服务器,而不是从客户端请求的方法,参数值可以包含变量
  • 作用范围
    • httpserverlocation
proxy_method get;

【6】proxy_hide_header

  • 语法格式
    • proxy_hide_header field;
  • 含义解释
    • nginx默认不会将“Date”“Server”“X-Accel-...”响应头发送给客户端。proxy_hide_header指令则可以设置额外的响应头,这些响应头也不会发送给客户端。相反的,如果希望允许传递某些响应头给客户端,可以使用proxy_pass_header指令。
  • 作用范围
    • httpserverlocation
proxy_hide_header Date;
proxy_hide_header Server;

【7】proxy_connect_timeout

  • 语法格式
    • proxy_connect_timeout time;
  • 含义解释
    • 设置与后端服务器建立连接的超时时间
    • 应该注意这个超时一般不可能大于75
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_connect_timeout 60s;
proxy_connect_timeout 5s;

【8】proxy_buffers

  • 语法格式
    • proxy_buffers number size;
  • 含义解释
    • 为每个连接设置缓冲区的数量number和每块缓冲区的大小size
    • 这些缓冲区用于保存从被代理的服务器读取的响应,成块一起发送
    • 每块缓冲区默认等于一个内存页的大小,值是 4K 还是 8K 取决于平台
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_buffers 8 4k|8k;
proxy_buffers 8 4k

1.3 缓存功能

Nginx服务之代理和负载

【1】proxy_cache_path

  • 语法格式
    • proxy_cache_path path [levels=levels] keys_zone=name:size [inactive=time] [max_size=size] [loader_files=number] [loader_sleep=time] [loader_threshold=time];
  • 含义解释
    • 用于设置缓存的路径和配置,需要先设置才能使用
    • 参数十分多,很多的参数都可以使用默认值就好了
    • 该指令只能使用在http中,可以设置多个不同名的缓存样式
  • 作用范围
    • http
  • 缓存格式
    • 缓存数据是保存在文件中的,缓存的键和文件名都是在代理URL上执行MD5的结果。
  • 工作方式
    • 被缓存的响应首先写入一个临时文件,然后进行重命名。从0.8.9版本开始,临时文件和缓存可以放在不同的文件系统。但请注意,这将导致文件在这两个文件系统中进行拷贝,而不是廉价的重命名操作。因此,针对任何路径,都建议将缓存和proxy_temp_path指令设置的临时文件目录放在同一文件系统。
  • 参数理解
    • 所有有效的键和缓存数据相关的信息都被存放在共享内存中。共享内存通过keys_zone参数的namesize来定义。被缓存的数据如果在inactive参数指定的时间内未被访问,就会被从缓存中移除,不论它是否是刚产生的。inactive的默认值是10分钟。
    • 特殊进程cache manager监控缓存的条目数量,如果超过max_size参数设置的最大值,使用LRU算法移除缓存数据。
    • nginx启动之后,特殊进程cache loader就被启动。该进程将文件系统上保存的过去缓存的数据的相关信息重新加载到共享内存。加载过程分多次迭代完成,每次迭代,进程只加载不多于loader_files参数指定的文件数量(默认值为100)。此外,每次迭代过程的持续时间不能超过loader_threshold参数的值(默认200毫秒)。每次迭代之间,nginx的暂停时间由loader_sleep参数指定(默认50毫秒)。
# path设置缓存存储的物理路径位置
# levels设置缓存的路径级别和命名大小
# keys_zone设置缓存的名称和大小
# levels=1:2表示有两层,第一层目录使用一位数,第二层目录使用两位数
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;

# 缓存中文件名看起来是这样的
/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c

【2】proxy_cache

  • 语法格式
    • proxy_cache zone|off;
  • 含义解释
    • 指定用于页面缓存的共享内存,同一块共享内存可在多个地方使用
    • 先定义后使用,选择http段中定义的缓存样式中的一个即可
    • 其中off参数可以屏蔽从上层配置继承的缓存功能
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_cache off;
proxy_cache one;
http {
    include       /etc/nginx/mine.types;
    default_type  application/octet-stream;
    sendfile      on;
    proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:32m;
    ...

    server {
        listen       80;
        server_name  localhost;
        ...

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }

        location /bbs/ {
            proxy_cache            mycache;
            proxy_cache_valid      200 1d;
            proxy_cache_valid      301 302 10m;
            proxy_cache_valid      any 1m;
            proxy_cache_use_stale  error timeout invalid_header http_500;
            proxy_set_header       Host      $host;
            proxy_set_header       X-Real-IP $remote_addr;
            proxy_pass             http://192.168.100.2/;
        }

        location ~* \.(jpg|png|gif)$ {
            proxy_cache       mycache;
            proxy_set_header  X-Real-IP $remote_addr;
            proxy_pass        http://192.168.100.2/;
        }
    }
}

【3】proxy_cache_methods

  • 语法格式
    • proxy_cache_methods GET|HEAD|POST ...;
  • 含义解释
    • 如果客户端请求方法是列在这个指令的响应才被缓存
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_cache_methods GET HEAD;
proxy_cache_methods GET HEAD;

【4】proxy_cache_min_uses

  • 语法格式
    • proxy_cache_min_uses number;
  • 含义解释
    • 设置请求对于的响应的数量达到指定的数量之后,响应才被缓存
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_cache_min_uses 1;
# 请求对于的响应的数量达到10次之后,对于的响应内容才会被缓存起来
proxy_cache_min_uses 10;

【5】proxy_cache_purge

  • 语法格式
    • proxy_cache_purge string ...;
  • 含义解释
    • 缓存修剪:将特定请求视为一个缓存清除请求
    • 如果缓存没有过期但后端已经变动了,用户请求将返回缓存中的响应信息,所有这个时候就需要缓存修剪操作。只要用户请求就删除对于请求的响应缓存数据,就会返回后端变动的响应信息。
  • 作用范围
    • httpserverlocation
proxy_cache_path /data/nginx/cache keys_zone=cache_zone:10m;

map $request_method $purge_method {
    PURGE   1;
    default 0;
}

server {
    ...
    location / {
        proxy_pass http://backend;
        proxy_cache cache_zone;
        proxy_cache_key $uri;
        proxy_cache_purge $purge_method;
    }
}
# 需要ngx_cache_purge模块来在过期时间未到之前,手动清理缓存
# 清除缓存的防范:(1)直接删除缓存目录;(2)GET方式请求URL
http {
    ... // $upstream_cache_status记录缓存命中率
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"'
                      '"$upstream_cache_status"';
    proxy_temp_path   /usr/local/nginx-1.6/proxy_temp;
    proxy_cache_path /usr/local/nginx-1.6/proxy_cache levels=1:2 keys_zone=cache_one:100m inactive=2d max_size=2g;

    server {
        listen       80;
        server_name  ittest.example.com;
        root   html;
        index  index.html index.htm index.jsp;

        location ~ .*\.(gif|jpg|png|html|css|js|ico|swf|pdf)(.*) {
            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_cache cache_one;
            # 定义cache_key格式
            proxy_cache_valid  200 304 301 302 8h;
            proxy_cache_valid  404 1m;
            proxy_cache_valid  any 2d;
            proxy_cache_key    $host$uri$is_args$args;
            add_header Nginx-Cache $upstream_cache_status;
            expires 30d;
        }

        location ~ /purge(/.*) {
            # 设置只允许指定的IP或IP段才可以清除URL缓存
            allow   127.0.0.1;
            allow   172.29.73.0/24;
            deny    all;
            proxy_cache_purge cache_one $host$1$is_args$args;
            error_page 405 =200 /purge$1;
        }
    }
}

【6】proxy_cache_revalidate

  • 语法格式
    • proxy_cache_revalidate on|off;
  • 含义解释
    • 过期后重新校验:当缓存过期后向后端服务器询问,如果没有改变将修改有效期继续使用,如果已经改变了,就使用改变后的响应信息发送给客户端
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_cache_revalidate off;
proxy_cache_revalidate on;

【7】proxy_cache_use_stale

  • 语法格式
    • proxy_cache_use_stale error|timeout|invalid_header|updating|http_404|http_502 ...
  • 含义解释
    • 如果缓存已经过期了且和后端服务器无法连接的情况下,如何响应用户请求
    • 使用过期缓存的情况;遇到error错误页、响应为http_404timeout为后端服务器连接超时等情况下
  • 作用范围
    • httpserverlocation
  • 默认值
    • proxy_cache_use_stale off;
proxy_cache_use_stale http_404;

【8】proxy_cache_valid

  • 语法格式
    • proxy_cache_valid [code ...] time;
  • 含义解释
    • 根据响应状态码来设置缓存时长
  • 作用范围
    • httpserverlocation
proxy_cache_valid 200 302 10m;
proxy_cache_valid 301      1h;
proxy_cache_valid any      1m;

【9】proxy_cache_bypass

  • 语法格式
    • proxy_cache_bypass string ...;
  • 含义解释
    • 设置在何种情况下nginx将不从cache中取数据
    • 可以和proxy_no_cache指令一起使用
  • 作用范围
    • httpserverlocation
proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;
proxy_cache_bypass $http_pragma    $http_authorization;

1.4 加密通信

ngx_http_ssl_module模块提供HTTPS的支持,该模块默认没有配置,需要在编译的使用--with-http_ssl_module参数开启(需要OpenSSL库的支持)。

# 减少处理器负载建议
# 1. 设置工作进程的数量等于处理器的数量
# 2. 开启keep-alive连接支持
# 3. 启用共享会话缓存,即所有工作进程之间共享一个缓存
# 4. 禁用内置的会话缓存,即禁止缓存只使用一个工作进程
# 5. 提高session会话的重用时长(ssl_session_timeout参数)

worker_processes auto;
http {
    ...
    server {
        listen              443 ssl;
        keepalive_timeout   70;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
        ssl_certificate     /usr/local/nginx/conf/cert.pem;
        ssl_certificate_key /usr/local/nginx/conf/cert.key;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 10m;
        ...
    }
}

2. 负载均衡

upstream 的官方版本翻译文档

# nginx的商用软件支持健康监测功能
upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;
    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

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

2.1 常用指令

【1】upstream

  • 语法格式
    • upstream name {...}
  • 含义解释
    • 定义一组后端服务器,这些服务器可以监听不同的端口
    • 默认情况下,nginx按加权轮转的方式将请求分发到各服务器
    • 如果所有服务器都返回失败,客户端将会得到失败响应结果
  • 作用范围
    • http
# 使用默认带权重的RR算法,在上面的例子中,每7个请求将被如下分配
# 5个请求去backend1.example.com
# 1个请求去127.0.0.1:8080
# 1个请求去unix:/tmp/backend3
upstream backend {
    server backend1.example.com weight=5;
    server 127.0.0.1:8080       max_fails=3 fail_timeout=30s;
    server unix:/tmp/backend3;
    server backup1.example.com  backup;
}

【2】server

  • 语法格式
    • server address [parameters];
  • 含义解释
    • 定义服务器的地址address和其他参数parameters
  • 作用范围
    • upstream
参数格式 含义解释和说明
weight=number 设定服务器的权重,默认值为1
max_fails=number 定义最大错误次数;设定Nginx与服务器通信的尝试失败的次数;失败的尝试次数默认值为1,设为0就会停止统计尝试次数,认为服务器是一直可用的;默认配置时,http_404状态不被认为是失败的尝试,可以通过修改参数进行修改
fail_timeout=time 定义超时是时长;在这段时间中,服务器失败次数达到指定的尝试次数,服务器就被认为不可用;默认情况下,该超时时间是10
backup 标记为备用服务器;当主服务器不可用以后,请求会被传给这些服务器
down 标记服务器永久不可用;可以跟ip_hash指令一起使用
max_conns=number 商用版本才有;设置代理服务器的最大的活动连接数;默认值为0表示不受限制
upstream backend {
    server unix:/tmp/backend3;
    server backend1.example.com  weight=5;
    server 127.0.0.1:8080        max_fails=3 fail_timeout=30s;
    server backup1.example.com:8080 backup;
}

2.2 会话保持

**sticky**是nginx的一个模块,它是基于cookie的一种nginx的负载均衡解决方案,通过分发和识别cookie,来使同一个客户端的请求落在同一台服务器上。

【1】ip_hash

  • 语法格式
    • ip_hash;
  • 含义解释
    • 类似于LVSsh算法,持久连接
    • 请求基于客户端的IP地址在服务器间进行分发
    • 这种方法可以确保从同一个客户端过来的请求,会被传给同一台服务器
  • 作用范围
    • upstream
  • 坏处说明
    • 现在大量用户都是使用SNAT进行上网的,而使用ip_hash的话,代理服务器会将多个客户端识别为一个用户并转发给同一个后端服务器,可能会损害负载均衡调度。
    • 虽然代理服务器将多个用户识别为同一个,但是后端服务器却能够识别为多个不同的用户,虽然使用的是同一个IP地址,但不同的cookie。因为用户使用的浏览器新发请求时,后端服务器都视为新请求,随之后端服务器给其发送一个特定cookie(登录),之后请求时都带上这个特定cookie来标识不同用户。
    • 如果调度的时候根据cookie进行调度,颗粒度就比较好了。如果客户端请求到达Nginx时,Nginx不做IP地址绑定而是做cookie绑定,即保证了会话绑定和力度更大(sticky)。
# 如果其中一个服务器想暂时移除,应该加上down参数,这样可以保留当前客户端IP地址散列分布
upstream backend {
    ip_hash;
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
    server backend4.example.com;
}

【2】sticky

  • 语法格式
    • sticky cookie name [expires=time] [domain=domain] [httponly] [secure] [path=path];
    • sticky route $variable ...;
    • sticky learn create=$variable lookup=$variable zone=name:size [timeout=time] [header];
  • 含义解释
    • 使用cookie绑定,使来自相同客户机的请求传递给同一台后端服务器
    • sticky_cookie_insert1.5.7之前版本使用的指令
  • 作用范围
    • upstream
# 【可用方法一】
# 当使用cookie的方法时,由nginx传入cookie信息给后端服务器。
# 新请求进来时将被分派到配置的均衡算法选择的服务器,后续带有cookie的请求将被分派给指派的服务器。

# name: 可以为任何的字符,默认是route,这里是srv_id
# expires: 设置浏览器的cookie过期时间,默认浏览器关闭就过期
# domain: 哪些域名下可以使用这个cookie
# httponly: 添加HttpOnly属性到cookie
# secure: 添加Secure属性到cookie
# path: 对哪些路径启用sticky模式,这里的/代表整个网站
upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    sticky cookie srv_id expires=1h domain=.example.com path=/;
}
# 【可用方法二】
# 当使用route的方式时,用户的第一次请求根据调度发送给后端服务器
# 用户之后的请求URI中将携带route标记的信息,用于负载均衡调度。

# 将$cookie_jsessionid标记为$route_cookie
map $cookie_jsessionid $route_cookie {
    ~.+\.(?P<route>\w+)$ $route;
}

# 将$request_uri标记为$route_uri
map $request_uri $route_uri {
    ~jsessionid=.+\.(?P<route>\w+)$ $route;
}

upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;
    sticky route $route_cookie $route_uri;
}
# 【可用方法三】
# 当使用learn的方式时
# nginx分析upstream服务器的响应并学习通常在cookie中传递的server-initiated会话

# 在这个例子中,upstream服务器通过在应答中设置cookie的"EXAMPLECOOKIE"创建会话。
# 带有这个cookie的后续请求将被分派到同一个服务器。
# 如果服务器不能处理请求,新的服务器将被选择,就如同客户端没有被绑定一样。

# 参数create和lookup分别指定变量来指示如何创建新会话和搜索已经存在的会话。
# 两个参数都可以指定多个,这样第一个非空的变量将被使用。

# 会话存储在zone中,在zone属性中配置名字和大小。
# 在64位平台上一个megabyte zone可以存储大概8000个会话。
# 在timeout参数指定的期间内没有被访问的会话将被从zone上移除。默认,超时时间设置为10分钟。
upstream backend {
   server backend1.example.com:8080;
   server backend2.example.com:8081;
   sticky learn
          create=$upstream_cookie_examplecookie
          lookup=$cookie_examplecookie
          zone=client_sessions:1m;
}

2.3 调度算法

调度算法的官方文档

调度算法 解释说明
rr 默认为轮询算法; 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响;权重越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下
ip_hash 每个请求按访问IPhash结果分配;这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题;当然如果这个节点不可用了就会发到下个节点,而此时没有session同步的话就注销掉了
least_conn 分派请求到活动连接数量最少的服务器,兼顾服务器权重
least_time 分派请求到平均响应时间最快和活动连接数量最少的服务器, 兼顾服务器权重
hash 根据客户端/服务器映射基于散列key值;key可以包含文本、变量和他们的组合
url_hash 按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率;nginx本身是不支持url_hash的,如果需要使用这种调度算法,必须安装nginxhash软件包  nginx_upstream_hash模块
fair 更加智能的负载均衡算法;该算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配;nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载nginxupstream_fair模块

2.4 持久连接

keepalive

  • 语法格式
    • keepalive connections;
  • 含义解释
    • 激活代理服务器到upstream服务器的连接缓存,即长连接
    • connections参数设置每个worker进程在缓冲中保持的空闲keepalive连接的最大数量,当超过这个数量时,最近使用最少的连接将被关闭
    • 通常upstream服务器为memcached等缓存服务器,而非http服务器,是因为http有可能会损害性能
  • 作用范围
    • upstream
# 使用keepalive连接后端memcached服务器配置的例子
upstream memcached_backend {
    server 127.0.0.1:11211;
    server 10.0.0.2:11211;
    keepalive 32;
}

server {
    ...
    location /memcached/ {
        set $memcached_key $uri;
        memcached_pass memcached_backend;
    }
}
# 对于HTTP请求,proxy_http_version应该设置为1.1,而Connection的header应该被清理
# 或者HTTP/1.0持久连接可以通过传递"Connection: Keep-Alive"的header到upstream服务器,但是不推荐使用这种方法。
upstream http_backend {
    server 127.0.0.1:8080;
    keepalive 16;
}

server {
    ...
    location /http/ {
        proxy_pass http://http_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        ...
    }
}
# 对于FastCGI服务器,要求设置fastcgi_keep_conn来让长连接工作
# 当使用默认的RR之外的负载均衡算法时,必须在keepalive指令之前激活他们
# 对于scgi和uwsgi协议没有keepalive连接的概念
upstream fastcgi_backend {
    server 127.0.0.1:9000;
    keepalive 8;
}

server {
    ...
    location /fastcgi/ {
        fastcgi_pass fastcgi_backend;
        fastcgi_keep_conn on;
        ...
    }
}

2.5 健康监测

社区版本中主动的健康监测功能是不可用,而HAProxyNGINX+才支持而且做得很好,可以参考这个链接了解更多内容

【1】health_check

  • 语法格式
    • health_check [parameters];
  • 含义解释
    • 开启对所在location引用的集群中的服务器的定期健康检查
    • 建议关闭对于location的访问日志功能,无用功
    • 如果同一个集群的服务器定义有多个健康检查,任何一个检查失败都会导致对应的服务器被认为是不健康
  • 作用范围
    • location
可选参数 说明解释
interval=time 设置两次连续健康检查之间的间隔时间,默认5秒钟
fails=number 设置连续失败的健康检查次数,之后这台服务器会被认为是不健康,默认为1
passes=number 设置连续通过的健康检查次数,之后这台服务器会被认为是健康的,默认为1
uri=uri 定义健康检查请求的URL,默认是/
match=name 指定匹配块来配置测试可以通过健康检查的响应;默认,响应的状态码应该是2xx或者3xx
port=number 定义到执行健康检查的服务器的连接端口;默认和服务器端口相同
# 将每5秒钟发送"/"请求给后端集群中的每台服务器。
# 如果发生任何通讯错误、超时、代理服务器返回状态码不是2xx或者3xx,则健康检查失败。
# 这台服务器将被认为是不健康的,后续的客户端请求将不会发送给这台不健康的服务器。
location / {
    proxy_pass http://backend;
    health_check;
}
# 健康检查可以配置为检验响应的状态码,是否存在特定header和他们的值,还有http的body内容。
# 检验可以使用match指令单独配置并在match参数中引用。
http {
    server {
        ...
        location / {
            proxy_pass http://backend;
            health_check match=welcome;
        }
    }

    match welcome {
        status 200;
        header Content-Type = text/html;
        body ~ "Welcome to nginx!";
    }
}
http {
    proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m;
    ...

    upstream backend {
        sticky;
        server 172.29.88.226:8080 weight=2;
        server 172.29.88.227:8081 weight=1;
    }

    server {
        listen       80;
        server_name  localhost;
        ...

        # 自定义响应首部,用于后端服务器使用
        # $upstream_cache_status需要启用缓存共功能,才能查看
        # $server_addr和$upstream_cache_status都是upstream提供的内部变量
        add_header X-Via $server_addr;
        add_header X-Cache $upstream_cache_status;
        location / {
            proxy_pass http://backend;
            proxy_chache mycache;
            health_check;
        }
    }
}
# status is 200, content type is "text/html",
# and body contains "Welcome to nginx!"
match welcome {
    status 200;
    header Content-Type = text/html;
    body ~ "Welcome to nginx!";
}

# status is not one of 301, 302, 303, or 307, and header does not have "Refresh:"
match not_redirect {
    status ! 301-303 307;
    header ! Refresh;
}

# status ok and not in maintenance mode
match server_ok {
    status 200-399;
    body !~ "maintenance mode";
}

【2】nginx_upstream_check_module 模块

  • 由淘宝的姚伟斌开发,专门提供负载均衡器内节点的健康检查的外部模块,在淘宝自己的tengine上是自带了该模块,项目地址官方文档

Nginx服务之代理和负载

  • ngx_http_upstream_conf_module模块允许动态配置后端服务器组,通过一个简单的HTTP接口而不需要重新启动nginx服务。
# 下面的是一个带后端监控检查的nginx.conf配置
# 对name为backend这个负载均衡条目中的所有节点,每个5秒检测一次,超时时间为1秒
# 请求2次正常则标记后端服务器的状态为up,如果检测3次都失败,则标记后端服务器的状态为down
upstream backend {
    sticky;     # or simple round-robin
    server 172.29.88.226:8080 weight=2;
    server 172.29.88.226:8081 weight=1 max_fails=2 fail_timeout=30s ;
    server 172.29.88.227:8080 weight=1 max_fails=2 fail_timeout=30s ;
    server 172.29.88.227:8081;

    check interval=5000 rise=2 fall=3 timeout=1000 type=http;
    check_http_send "HEAD / HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}

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

    location /status {
        check_status;
        access_log  off;
        allow       172.29.73.23;
        deny        all;
    }
}

3. FastCGI 模块

在现在LNMP架构里面,PHP一般是以php-cgi的形式在运行,它就是一种FastCGI,我们在进程中看到的php-fpmphp-cgi的管理调度器。

Nginx服务之代理和负载

  • 我们之前的都是使用的是http协议,如果Web服务器,而对于FastCGI协议,我们就需要使用ngx_http_fastcgi_module模块的。
  • 如客户端请求以php结尾的网页,就需要我们将请求发送给处理php的应用程序,其中传输的协议为FastCGI

3.1 安装 LNMP

动静分离:通过对请求的URI后缀名进行分类,不管是动态还是静态内容的服务器都可以定义为组进行管理、设置缓存等。动态内容可以使用FastCGIUWSGI等协议进行代理(proxy_pass),静态内容可以使用HTTPHTTPS协议进行代理(fastcgi_pass)。

# 安装服务,需要适当配置
yim install -y nginx
yum install -y php-fpm
yum install -y mysql-server
yum install -y php-mysql

# 启动服务
service nginx start
service php-fpm start
service mysqld start
# nginx配置文件
server {
    ...
    location / {
        root   /usr/share/nginx/html;
        index  index.php index.html index.htm;
    }
    location ~ \.php$ {
        root           html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME /scripts$fastcgi_script_name;
        include        fastcgi_params;
    }
}

# 因为原有的文件内容有可能无法适配
# 编辑/etc/nginx/fastcgi_params内容更改为如下内容
fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# 添加测试页面/usr/share/nginx/html/index.php验证
<?php
    phpinfo();
?>

3.2 常用指令

【1】fastcgi_pass

  • 语法格式
    • fastcgi_pass address;
  • 含义解释
    • 设置fastcgi的服务器地址
  • 作用范围
    • locationif in location
# 使用域名、IP地址和unix
fastcgi_pass localhost:9000;
fastcgi_pass unix:/tmp/fastcgi.socket;

【2】fastcgi_limit_rate

  • 语法格式
    • fastcgi_limit_rate rate;
  • 含义解释
    • 设置FastCGI服务器的响应速度,0表示不做限制
  • 作用范围
    • httpserverlocation
  • 默认值
    • fastcgi_limit_rate 0;
fastcgi_limit_rate 0;

【3】fastcgi_index

  • 语法格式
    • fastcgi_index name;
  • 含义解释
    • 设置首页的名称,值为$fastcgi_script_name的值
  • 作用范围
    • httpserverlocation
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/scripts/php$fastcgi_script_name;

【4】fastcgi_param

  • 语法格式
    • fastcgi_param parameter value [if_not_empty];
  • 含义解释
    • 设置自定义参数传递给FastCGI服务器使用
  • 作用范围
    • httpserverlocation
fastcgi_param QUERY_STRING    $query_string;
fastcgi_param REQUEST_METHOD  $request_method;
fastcgi_param CONTENT_TYPE    $content_type;
fastcgi_param CONTENT_LENGTH  $content_length;
fastcgi_param HTTPS           $https if_not_empty;

3.3 缓存功能

fastCGIproxy的缓存功能使用基本类似,对动态内容的缓存需要设置fastcgi_cache_valid指令,否则不会进行缓存的。官方文档地址

【1】fastcgi_cache_path

  • 语法格式
    • fastcgi_cache_path path [levels=levels] keys_zone=name:size [inactive=time]...
  • 含义解释
    • 设置缓存的路径和参数,先定义后使用
  • 作用范围
    • http
fastcgi_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m;

【2】fastcgi_cache

  • 语法格式
    • fastcgi_cache zone|off;
  • 含义解释
    • 使用fastcgi_cache_path定义缓存样式
  • 作用范围
    • httpserverlocation
  • 默认值
    • fastcgi_cache off;
location ~ \.php$ {
    fastcgi_cache mycache;
    fastcgi_cache_valid 200 302 10m;
    fastcgi_cache_valid 301      1h;
    fastcgi_cache_valid any      1m;
}

【3】fastcgi_cache_methods

  • 语法格式
    • fastcgi_cache_methods GET|HEAD|POST ...;
  • 含义解释
    • 对特定的请求方法进行缓存
  • 作用范围
    • httpserverlocation
  • 默认值
    • fastcgi_cache_methods GET HEAD;
fastcgi_cache_methods GET;

【4】fastcgi_cache_valid

  • 语法格式
    • fastcgi_cache_valid [code ...] time;
  • 含义解释
    • 设置缓存时间不同的响应代码
  • 作用范围
    • httpserverlocation
fastcgi_cache_valid 200 302 10m;
fastcgi_cache_valid 301      1h;
fastcgi_cache_valid any      1m;

【5】fastcgi_cache_use_stale

  • 语法格式
    • fastcgi_cache_use_stale error|timeout|http_404
  • 含义解释
    • 设置腐败缓存
  • 作用范围
    • httpserverlocation
  • 默认值
    • fastcgi_cache_use_stale off;
fastcgi_cache_use_stale http_404;

3.4 场景说明

LNMPLNAMP的动静分离场景

Nginx服务之代理和负载

Nginx服务之代理和负载

  • (1) root 为同一路径
location / {
    root /web/nginx/www;
}

location \.php$ {
    root /web/nginx/www;
}
  • (2) root 为不同的路径
location / {
    root /web/nginx/app;
}

location \.php$ {
    root /web/nginx/www;
}
  • (3) fpm server 为另一主机
location / {
    root /web/nginx/www;
}

location \.php$ {
    fastcgi_pass fastcgi://172.16.100.9:9000;
}

4. 练习作业

4.1 场景说明

  • 使用nginx反向代理(使用rr调度)用户请求至两个以上的后端LAMP服务器,服务器同时部署WordPressphpMyAdmin服务。
    • (1) 手动更新所有节点上的phpMyAdmin至新版本
    • (2) 用脚本实现手动更新phpMyAdmin的整个过程
  • 在线升级的情况下,观察:
    • (1) 后端服务器的错误日志,分析出错问题原因
    • (2) 这就是生成环境中的软件发布操作,熟悉灰度发布等

4.2 解决方式

手动更新phpMyAdmin服务

  • LAMP中部署phpMyAdmin服务的时候,用链接的方式指定安装目录,这样方便配置和更新操作。如配置/webapps/pma目录链接至/webapps/pma-3.4,之后升级新版本只需要将链接变更为/webapps/pma-3.6即可。
  • 我们手动更新时,在代理服务器上将其中一台标记为downbackup,之后升级该服务器,完成之后再以同样方式升级另一台服务器。

写脚本更新phpMyAdmin服务

  • 我们才可以采用上述所说的,停止一台更新一台的方式。但是更好的做法就是,在代理服务器上运行脚本,修改配置、传递脚本过去、更新服务器等一系列的操作。
  • 如果我们的脚本充分的进行了测试,之后再工作中就可以使用它了,方便我们之后软件升级时使用了。

文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录