十三周一次课(6月12日)

12.17 Nginx负载均衡

  • 代理一台机器称为代理 ,代理两台机器就可以称为负载均衡
  • 代理服务器后面可以有多个web服务器,多个web服务器去提供服务的时候,就可以实现一个负载均衡的功能
  • 正常情况下,用户访问web服务器,是一台一台去请求;要么就是指定一个IP,把这域名解析到多台服务器上
  • 案例
  • 用户1 –> web1服务器
  • 用户2 –> web2服务器
  • 假设这时web1服务器挂掉了(宕机),用户1因为解析到了web1,但web1宕机了,所以就无法正常访问
  • 这时候若是用nginx的负载均衡,在web1宕机后,代理服务器就不会把请求发送给web1,这就是代理的一个优点,负载均衡的优点
  • 配置负载均衡,负载均衡的配置借助了upstream 模块
    这里将qq.com作为演示对象
    dig命令查看解析的IP——>yum install -y bind-utils
# dig www.qq.com @202.96.134.133

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> www.qq.com @202.96.134.133
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46092
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 11

;; QUESTION SECTION:
;www.qq.com.                    IN      A

;; ANSWER SECTION:
www.qq.com.             3600    IN      A       182.254.34.74

;; AUTHORITY SECTION:
www.qq.com.             6722    IN      NS      ns-os1.qq.com.
www.qq.com.             6722    IN      NS      ns-cmn1.qq.com.
www.qq.com.             6722    IN      NS      ns-cnc1.qq.com.
www.qq.com.             6722    IN      NS      ns-tel1.qq.com.

;; ADDITIONAL SECTION:
ns-os1.qq.com.          473     IN      A       103.7.29.14
ns-os1.qq.com.          473     IN      A       184.105.206.121
ns-os1.qq.com.          473     IN      A       203.205.147.152
ns-cmn1.qq.com.         1906    IN      A       182.254.49.112
ns-cmn1.qq.com.         1906    IN      A       183.232.120.59
ns-cmn1.qq.com.         1906    IN      A       121.51.32.102
ns-cnc1.qq.com.         1826    IN      A       140.207.128.140
ns-cnc1.qq.com.         1826    IN      A       223.167.83.104
ns-cnc1.qq.com.         1826    IN      A       111.161.107.195
ns-tel1.qq.com.         298     IN      A       14.17.42.40
ns-tel1.qq.com.         298     IN      A       14.17.32.211

;; Query time: 10 msec
;; SERVER: 202.96.134.133#53(202.96.134.133)
;; WHEN: Sat Jun 09 11:58:39 CST 2018
;; MSG SIZE  rcvd: 307

会看到返回出两个IP,这个就是域名解析,也就是qq.com解析到了两个IP上
这时候就可以用这两个IP:14.17.42.40和14.17.32.211,去 做负载均衡
vim一个vhost配置文件

vim /usr/local/nginx/conf/vhost/www.qq.com_upstream.conf       
#upstream来指定多个web server
upstream qq_com
{
    ip_hash;
    server 14.17.42.40:80;
    server 14.17.32.211:80;
}
server
{
    listen 80;
    server_name www.qq.com;
    location /
    {
        proxy_pass      http://qq_com;
        proxy_set_header Host   $host;
        proxy_set_header X-Real-IP      $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    access_log /www/wwwlogs/${host}_access.log format1;
}

upstream来指定多个web server
当有多个服务器同时对一个域名提供服务的时候,长时间访问一个域名,在一定的时效内,会出现需要重新登录或者是说跳转到另外一个地址的服务器上;ip_hash,就是使通过这个代理访问的同一个域名的多个IP的服务器是,始终保持在一个IP上对这个域名进行访问
在未加载配置的时候,本机去访问qq.com,回去访问默认虚拟主机

检查配置文件语法,并重新加载


[root@aaa abc.com]# curl -xlocalhost:80 -I www.qq.com
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 13 Mar 2018 19:18:51 GMT
Content-Type: text/html
Connection: keep-alive
Vary: Accept-Encoding

[root@aaa abc.com]# curl -xlocalhost:80 -I www.qq.com
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 13 Mar 2018 19:18:52 GMT
Content-Type: text/html
Connection: keep-alive
Vary: Accept-Encoding

nginx不支持去代理https,也就是在配置文件中的server 后不能写443,是不支持的,只能代理http、tcp
若想要实现代理https,nginx监听443端口,但web服务必须是80端口

12.18 ssl原理

浏览器发送一个https的请求给服务器;
服务器要有一套数字证书,可以自己制作(后面的操作就是自己制作的证书),也可以向组织申请,区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出>提示页面,这套证书其实就是一对公钥和私钥;
服务器会把公钥传输给客户端;
客户端(浏览器)收到公钥后,会验证其是否合法有效,无效会有警告提醒,有效则会生成一串随机数,并用收到的公钥加密;
客户端把加密后的随机字符串传输给服务器;
服务器收到加密随机字符串后,先用私钥解密(公钥加密,私钥解密),获取到这一串随机数后,再用这串随机字符串加密传输的数据(该加密为对称加密,所谓对称加密,就是将数据和私钥也就是这个随机字符串>通过某种算法混合在一起,这样除非知道私钥,否则无法获取数据内容);
服务器把加密后的数据传输给客户端;
客户端收到数据后,再用自己的私钥也就是那个随机字符串解密;
十三周一次课(6月12日)

12.19 生成ssl密钥对

cd /etc/nginx/vhost
openssl genrsa -des3 -out tmp.key 2048  //key文件为私钥
openssl rsa -in tmp.key -out aaa.com.key //转换key,取消密码 
rm -f tmp.key
openssl req -new -key aaa.com.key -out aaa.com.csr  //生成证书请求文件,需要拿这个文件和私钥一起生产公钥文件
openssl x509 -req -days 365 -in aaa.com.csr -signkey aaa.com.key -out aaa.com.crt
这里的aaa.com.crt为公钥

1、创建私钥并取消其密码
准备工作:需要使用到openssl命令,故安装其rpm包!

[root@aiker ~]# rpm -qf `which openssl`  //搜索openssl命令依赖的安装包!
openssl-1.0.1e-60.el7_3.1.x86_64
[root@aiker ~]# yum install -y openssl  //安装;
[root@aiker ~]# cd /etc/nginx/vhost/
[root@aiker vhost]# openssl genrsa -des3 -out tmp.key 2048  //生成长度2048的rsa私钥.key 文件;
Enter pass phrase for tmp.key:
Verifying - Enter pass phrase for tmp.key: 
[root@aiker vhost]# openssl rsa -in tmp.key -out aaa.com.key  //取消私钥的密码,并输出为aaa.com.key
Enter pass phrase for tmp.key:
writing RSA key
[root@aiker vhost]# rm -f tmp.key   //删除原来的私钥;

2、生成证书请求文件--通过证书文件和私钥生成公钥!

[root@aiker vhost]# openssl req -new -key aaa.com.key -out aaa.com.csr  //生成.csr证书请求文件;
[root@aiker vhost]# openssl x509 -req -days 365 -in aaa.com.csr -signkey aaa.com.key -out aaa.com.crt  //生成.crt的公钥
x.key -out aaa.com.crt
Signature ok
subject=/C=XX/L=Default City/O=Default Company Ltd
Getting Private key

12.20 Nginx配置ssl

 vim /etc/nginx/vhost/aaa.com.conf//加入如下内容
server
{
    listen 443;
    server_name aaa.com;
    index index.html index.php;
    root /data/wwwroot/aaa.com;
    ssl on;
    ssl_certificate aaa.com.crt;
    ssl_certificate_key aaa.com.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}
nginx -t && nginx -s reload //若报错unknown directive “ssl” ,需要重新编译nginx,加上--with-http_ssl_module
 mkdir /data/wwwroot/aaa.com
 echo “ssl test page.”>/data/wwwroot/aaa.com/index.html
 编辑hosts,增加127.0.0.1 aaa.com
 curl https://aaa.com/

1、编辑配置文件

[root@aiker test.com]# vim /etc/nginx/vhost/aaa.com.conf  //加入如下内容
server
{
    listen 443;                     //定义监听端口443
    server_name aaa.com;          //定义域名
    index index.html index.php;
    root /data/wwwroot/aaa.com;   //网站根目录
    ssl on;                         //开启ssl
    ssl_certificate aaa.com.crt;       //公钥
    ssl_certificate_key aaa.com.key;   //私钥
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;  //一般这三个协议 
}

2、重新加载

nginx -t && nginx -s reload //若报错unknown directive “ssl” ,需要重新编译nginx,加上--with-http_ssl_module

3、创建网站主目录及页面

[root@aiker ~]# mkdir /data/wwwroot/aaa.com

4、编辑hosts,访问aaa.com
echo “ssl test page.”>/data/wwwroot/aaa.com/index.html
编辑hosts,增加127.0.0.1 aaa.com
[root@aiker ~]# curl https://aaa.com
curl: (60) Peer's certificate issuer has been marked as not trusted by the user.   //提示证书不信任;
More details here: http://curl.haxx.se/docs/sslcerts.html
……

试试网页访问:
注意:需要清除防火墙iptalbes -F

或者增加一行iptables规则:iptables -I INPUT -p tcp --dport 443 -j ACCEPT

扩展

针对请求的uri来代理

场景:1台nginx去代理4台apache
需求:根据不同的请求uri 代理到不同的apache
nginx的配置:

    upstream aa.com {         
                      server 192.168.0.121;
                      server 192.168.0.122;  
     }
    upstream bb.com {  
                       server 192.168.0.123;
                       server 192.168.0.124;
        }
    server {
        listen       80;
        server_name  www.abc.com;
        location ~ aa.php
        {
            proxy_pass http://aa.com/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
         location ~ bb.php
        {
              proxy_pass http://bb.com/;
              proxy_set_header Host   $host;
              proxy_set_header X-Real-IP      $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          }
}

根据访问的目录来区分后端的web

我的需求: 当请求的目录是 /aaa/ 则把请求发送到机器a,当请求的目录为/bbb/则把请求发送到机器b,除了目录/aaa/与目录/bbb/外,其他的请求发送到机器b

我的配置文件内容为:

upstream aaa.com
{
            server 192.168.111.6;
}
upstream bbb.com
{
            server 192.168.111.20;
}
server {
        listen 80;
        server_name li.com;
        location /aaa/
        {
            proxy_pass http://aaa.com/aaa/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location /bbb/
        {
            proxy_pass http://bbb.com/bbb/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location /
        {
            proxy_pass http://bbb.com/;
            proxy_set_header Host   $host;
            proxy_set_header X-Real-IP      $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

说明:
1 以上配置文件中的 aaa.com 以及 bbb.com 都是自定义的,随便写。
2 upstream 中的server 可以写多个,例如

upstream aaa.com 
{
            server 192.168.111.6;
            server  192.168.111.4;
            server  192.168.111.5;
}

3 proxy_pass http://aaa.com/aaa/ 这里必须要加这个目录,不然就访问到根目录了。
4 实际上,上述配置文件中, localtion /bbb/ 部分是可以省略掉的,因为后边的 location / 已经包含了/bbb/,所以即使我们不去定义 localtion /bbb/ 也是会访问到 bbb.com 的。

nginx长连接

网站使用程序 discuzx3 访问都正常,只有用户登陆存在异常,具体的情况是这样的:
用户登陆后,会马上显示未登陆,然后刷新一下或者多下又变成了登陆中。

这个问题很显然是由于session导致,后台有多个web机器,当用户登陆后,会把登陆态session保存到当前web,但是再次发送请求时则会到另一台机器,所以原来的session信息找不到了。解决这个问题有两个思路:

  1. 可以把session时时同步到另外的机器。
  2. 可以让前端的调度器保持长连接,也就是说某个用户的请求在某一时间段内始终抓发到固定的一台机器上。
    这两种方式,第二种更容易实现。

我使用的是nginx的代理,其中nginx有一种算法支持长连接,具体配置是这样的:

 upstream test {
            ip_hash;
server 192.168.109.5;
server 192.168.109.3;
}

关键代码: ip_hash
这样,nginx会把用户的请求一直转发到后端的某台机器。

nginx算法分析

随着互联网信息的爆炸性增长,负载均衡(load balance)已经不再是一个很陌生的话题,顾名思义,负载均衡即是将负载分摊到不同的服务单元,既保证服务的可用性,又保证响应足够快,给用户很好的体验。快速增长的访问量和数据流量催生了各式各样的负载均衡产品,很多专业的负载均衡硬件提供了很好的功能,但却价格不菲(如F5 BIG-IP、Citrix NetScaler、Radware等等,虽然可以解决问题,但其高昂的价格却往往令人望而却步),这使得负载均衡软件大受欢迎,nginx就是其中的一个。

nginx第一个公开版本发布于2004年,2011年发布了1.0版本。它的特点是稳定性高、功能强大、资源消耗低,从其目前的市场占有而言,nginx大有与apache抢市场的势头。其中不得不提到的一个特性就是其负载均衡功能,这也成了很多公司选择它的主要原因。本文将从源码的角度介绍nginx的内置负载均衡策略和扩展负载均衡策略,以实际的工业生产为案例,对比各负载均衡策略,为nginx使用者提供参考。

nginx的负载均衡策略可以划分为两大类:内置策略和扩展策略。内置策略包含加权轮询和ip hash,在默认情况下这两种策略会编译进nginx内核,只需在nginx配置中指明参数即可。扩展策略有很多,如fair、通用hash、consistent hash等,默认不编译进nginx内核。由于在nginx版本升级中负载均衡的代码没有本质性的变化,因此下面将以nginx1.0.15稳定版为例,从源码角度分析各个策略。

  1. 加权轮询(weighted round robin)

轮询的原理很简单,首先我们介绍一下轮询的基本流程。如下是处理一次请求的流程图:

十三周一次课(6月12日)

图中有两点需要注意,第一,如果可以把加权轮询算法分为先深搜索和先广搜索,那么nginx采用的是先深搜索算法,即将首先将请求都分给高权重的机器,直到该机器的权值降到了比其他机器低,才开始将请求分给下一个高权重的机器;第二,当所有后端机器都down掉时,nginx会立即将所有机器的标志位清成初始状态,以避免造成所有的机器都处在timeout的状态,从而导致整个前端被夯住。

接下来看下源码。nginx源码的目录结构很清晰,加权轮询所在路径为nginx-1.0.15/src/http/ngx_http_upstream_round_robin.[c|h],在源码的基础上,针对重要的、不易理解的地方我加了注释。首先看下ngx_http_upstream_round_robin.h中的重要声明:
十三周一次课(6月12日)
image从变量命名中,我们就可以大致猜出其作用。其中,current_weight和weight的区别主要是前者为权重排序的值,随着处理请求会动态的变化,后者是配置值,用于恢复初始状态。

接下来看下轮询的创建过程,代码如下图所示。
十三周一次课(6月12日)
image这里有个tried变量需要做些说明。tried中记录了服务器当前是否被尝试连接过。他是一个位图。如果服务器数量小于32,则只需在一个int中即可记录下所有服务器状态。如果服务器数量大于32,则需在内存池中申请内存来存储。对该位图数组的使用可参考如下代码:
十三周一次课(6月12日)
image最后是实际的策略代码,逻辑很简单,代码实现也只有30行,直接上代码。

十三周一次课(6月12日)

  1. ip hash

ip hash是nginx内置的另一个负载均衡的策略,流程和轮询很类似,只是其中的算法和具体的策略有些变化,如下图所示:
十三周一次课(6月12日)
imageip hash算法的核心实现如下图:
十三周一次课(6月12日)
image从代码中可以看出,hash值既与ip有关又与后端机器的数量有关。经过测试,上述算法可以连续产生1045个互异的value,这是该算法的硬限制。对此nginx使用了保护机制,当经过20次hash仍然找不到可用的机器时,算法退化成轮询。因此,从本质上说,ip hash算法是一种变相的轮询算法,如果两个ip的初始hash值恰好相同,那么来自这两个ip的请求将永远落在同一台服务器上,这为均衡性埋下了很深的隐患。

  1. fair

fair策略是扩展策略,默认不被编译进nginx内核。其原理是根据后端服务器的响应时间判断负载情况,从中选出负载最轻的机器进行分流。这种策略具有很强的自适应性,但是实际的网络环境往往不是那么简单,因此要慎用。

  1. 通用hash、一致性hash

这两种也是扩展策略,在具体的实现上有些差别,通用hash比较简单,可以以nginx内置的变量为key进行hash,一致性hash采用了nginx内置的一致性hash环,可以支持memcache。

对上面的集中负载均衡算法进行测试(测试工具polygraph),考察下面三个关键的测试指标:

均衡性:是否能够将请求均匀的发送给后端

一致性:同一个key的请求,是否能落到同一台机器

容灾性:当部分后端机器挂掉时,是否能够正常工作

通过实际的对比测试,我们对nginx各个负载均衡策略进行了验证。下面从均衡性、一致性、容灾性以及适用场景等角度对比各种策略。
十三周一次课(6月12日)
image无论哪种策略都不是万金油,在具体的场景下应该选择哪种策略一定程度上依赖于使用者对这些策略的熟悉程度。希望本文的分析和测试数据能够对读者有所帮助,更希望有越来越多、越来越好的负载均衡策略产出。

参考资料

http://wiki.nginx.org/HttpUpstreamConsistentHash

http://wiki.nginx.org/HttpUpstreamFairModule

http://wiki.nginx.org/HttpUpstreamRequestHashModule

http://www.web-polygraph.org/

http://nginx.org/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章