Nginx 的负载均衡

摘要:nginx 利用反向代理来做负载均衡,nginx 给出了多重负载分发的方式,比如轮询,ip_hash,weight 等等,翻翻官方文档用一台服务器实现了最简单的 nginx 负载均衡配置实例,但是配置好负载均衡后也会带来很多问题,我们需要一一应对和解决。

负载均衡是什么

比如:现在一匹马拉一根木头,这时候突然增加了十根木头。这时候一匹马已经拉不动了,我们有两种选择。一种是找一匹更加强壮的马,一种是多找几匹马。当然第一种也是一种解决方案,但是如果再增加 50 根木头呢?毫无疑问,这时候已经不是一匹马能够解决的了。所以我们会选择多找几匹马来拉木头。

根据上面的例子,那么换做服务器。客户端发送多个请求到服务器,如果请求量非常庞大,比如双十一并发量很大,单台服务器就无法承受住这么多请求。我们会多找几台服务器来解决并发和负载的问题,也就是纵向解决问题已经行不通,这时候需要横向扩展来了解决问题。

主要实现就是利用 nginx 做反向代理,然后将多个请求理想化的平均分发到多个服务器上,来实现负载均衡。


负载均衡主要配置项

proxy_pass:配置反向代理。

upstream:配置负载均衡的服务器列表。

least_conn:配置最少分发负载方式。

ip_hash:配置 ip_hash 负载方式。

weight:配置服务器负载权重。


负载均衡的负载分配方式

1、轮询:以轮询的方式分发到每个服务器,一台服务器一次。

2、weight:根据权重分配,默认权重是 1,权重越大,代表服务器被分配的客户端越多。

3、最少连接:下一个请求被分配给活动连接数最少的服务器。

4、ip_hash:哈希函数根据客户端 IP 地址来确定请求分发到那个服务器。

5、fair:按照后端服务器的响应时间来分配,响应时间越短的服务器优先分配。

如果没有配置负载方式,则默认为轮询方式。在 nginx 1.3 之后可以同时使用 ip_hash 和 weight 两种方式。


循环方式分发

http {
    upstream myapp1 {
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

上面这段配置中,在 srv1 - srv3 上运行了3个相同应用程序的实例。如果没有配置负载方式,则默认为轮询方式。所有请求都代理到服务器组 myapp1,nginx 会按照顺序,每次将客户端请求以轮询的方式去分发到 srv1、srv2、srv3 这三台服务器。如果某台服务器宕机了,则会自动跳过该服务器,继续分配下一个服务器。

优点:保证请求可以平均的分发到每台服务器上,使他们的负载大致基本相同。该方式适合服务器配置差不多得场景。

缺点:如果客户端有一个非常耗时耗资源的请求到某个服务器上,这时候这台服务器负载会很高。但是轮询方式不会管这台服务器负载是否过高,还是会把请求分发到这台服务器。


最少连接分发

upstream myapp1 {
	least_conn;
	server srv1.example.com;
	server srv2.example.com;
	server srv3.example.com;
}

least_conn 最少连接分发机制就是将请求发送给当前最少连接数的服务器上。


ip_hash 负载均衡方式

upstream myapp1 {
    ip_hash;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

对于轮询分发和最少连接分发方式,他们有个共同的缺点,就是会造成客户端每次请求都会请求不同的服务器,无法保证客户端每次请求都请求同一台服务器。这样会造成一个简单的问题,就是无法保证用户 session 的同步。

比如用户第一次请求 a 服务器登录,第二次请求 b 服务器访问,这样就会出现用户未登录的情况。

为了避免这种情况,可以使用 ip_hash 负载平衡机制。nginx 将通过对客户端 IP 计算哈希值之后,再对服务器数量取模得到目标服务器的序号,来实现会话粘滞。

此方法可以保证同一客户端的请求始终指向同一台服务器。如果根据 ip_hash 分配的服务器宕机了,会自动分配到其它的服务器。这样用户每次都会访问一台服务器,就可以解决这种问题了。

题外话:没有必要为了解决 session 不同步就使用 ip_hash 来做负载均衡,负载均衡共享 session 的方式有很多种,比如数据库、memcache、redis 都可以。


加权重负载均衡方式

upstream myapp1 {
	server srv1.example.com weight=3;
	server srv2.example.com;
	server srv3.example.com;
}

使用 weight 关键字来加权重就可以影响负载均衡的算法。

比如有 a、b、c,3 台服务器来负载客户端访问,使用循环分发方式,每台服务器都会分发一次,但是这之后 b、c 服务器的配置可能会稍微差一点,我们想让 a 服务器多承担一点负载,b、c 少承担一点,这时候我们就在 a 服务器后面加上 weight=3(等号两边不要有空格)就像上面这段代码。这样每五个请求会向 srv1 服务器分发3次,然后 srv2、srv3 各分发一次,这就是能者多劳。


fair 分发方式

upstream myapp1 {
	server srv1.example.com;
	server srv2.example.com;
	server srv3.example.com;
	fair;
}


一台服务器的负载均衡配置小 demo

events {

}
http {
    #upstream 模块应放于 http 模块中,默认的分配方式是轮寻
	upstream myweb {
		server 98.142.138.117:8081 weight=3;#weight 分配权重,值越大负载的权重越大
		server 98.142.138.117:8082  down;#表示当前 server 不参与负载
		#以下表明如果此分发节点在10秒内出现2次不可用,那10秒内不会再分发到此节点,直到10秒后再次重新检测节点健康情况
		#如果将 max_fails=0 则停止检查节点健康情况 
		server 98.142.138.117:8083 max_fails=2 fail_timeout=10s;
	}

	server {
		listen 80;

	    location / {
	        #配置一个反向代理,这里的设置和上面的 upstream 相对应的,你也可以将这里设置成 http://www.baidu.com
	        #这样你访问你的ip浏览器出现的就是百度的内容了
	        #如果你将这里设置成和上面的 myweb 一样,那么所有的请求都代理到 upstream 服务器组 myweb
			proxy_pass http://myweb;
	    }
	}
	
	server {
		listen 8081;
		root /usr/local/nginx/html/web1;
	}
	
	server {
		listen 8082;
		root /usr/local/nginx/html/web2;
	}
}

为了方便演示效果,我用一台服务器搭建了下。正常来讲如果你没有多台服务器,你可以用虚拟机装一台环境,然后再克隆出两台就可以了,然后一台做负载均衡服务器分发服务,另外两台来接受分发来学习负载均衡。

上面的这段我已经配置好了,你只需要将这段配置替换掉你 nginx.conf 中的配置,然后 ip 换成你服务器的 ip 然后在 web1 和 web2 目录下新建两个不同的 index.html 文件就可以了,然后就可以访问查看效果了。


用户上传文件同步的问题

负载均衡是多台服务器同时负载客户端请求,客户端有可能访问 a 服务器,也有可能访问 b 服务器。假如用户上传一张图片上传到 a 服务器,然后要访问图片的时候却被负载到 b 服务器,这时候图片是访问不到的,因为 b 服务器上没有这张图片。

(1)使用第三方来存储文件,用户上传访问都去访问第三方存储。比如七牛、腾讯、阿里的对象存储功能。

(2)或者对于上传操作都指定上传到一台服务器,然后访问也访问这一台服务器,这样会增加这台服务器的压力。


代码部署同步的问题

假如开发的项目要上线了,或者是修改某些功能要上线了,对于负载均衡的多台服务器部署代码的问题。

(1)代码部署到主服务器,然后使用 rsync 同步到其它从服务器。有一个风险,万一主服务器挂掉,也就无法部署和同步了。

(2)如果用 git 或者 jenkins 的话就更方便了。


参考:

Using nginx as HTTP load balancer

结束语:感谢您对本网站文章的浏览,欢迎您的分享和转载,但转载请说明文章出处。
Top