什么是负载均衡
负载均衡,其英文简称 Load Balancing,是一种计算机技术。用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。宏观上的意思是将负载(工作任务,访问请求)进行平衡、分摊到多个操作单元(服务器,组件)上进行执行以解决高性能,单点故障(高可用),扩展性(水平伸缩)等高流量下常见的问题
负载均衡分类
由于载均衡技术是要对网络传输中的报文控制,因此会涉及到网络七层模型,因此负载均衡分类可以按照网络层次进行分类,比如二层、三层、四层、七层负载均衡。其中最常见的是四层和七层负载均衡。此外,还有其他方面进行分类的,有软件负载均衡、硬件负载均衡等。
二层负载均衡
负载均衡服务器对外依然提供一个VIP,集群中不同的机器采用相同的ip地址,但是机器mac地址不一样,当负载均衡服务器接收到了请求之后,通过改写报文的目标mac地址的方式将请求转发到目标机器实现负载均衡。
三层负载均衡
与二层负载均衡类似,负载均衡服务器对外依然提供一个vip ,集群中不同的机器采用不同的ip地址,当负载均衡服务器接受到请求后,根据不同的负载均衡算法,通过ip 将请求转发至不同的真实服务器。
四层负载均衡
四层负载均衡在OSI 模型的传输层,由于在传输层,只有TCP/UDP协议,这两种协议中除了包含源ip、目标ip之外,还包含源端口号和目标端口号。四层负载均衡服务器在接受客户端请求之后,以后通过修改数据包的的地址信息(ip + prot)将流量转发到应用服务器
七层负载均衡
七层负载均衡在OSI模块下的应用层,应用层协议较多,常用的有http、dns等等。七层负载基于这些协议工作。这些应用层协议中包含很多有意义的内容,例如同一个web服务器的负载均衡,除了根据ip+port 负载之外,还可以根据七层的URL、浏览器类别、语言等来决定是否要进行负载均衡。
目前有很多开源的负载均衡工具,大部分是四层和七层,其中nginx、LVS、haproxy比较有名,LVS主要做四层负载均衡,nginx和haproxy主要做七层负载均衡,但是nginx和haporxy都支持四层负载,nginx中的stream模块除了支持四层反向代理还支持四层负载均衡。
负载均衡算法
大致可以分为两大类,一类是静态负载均衡算法,常见的有轮训、权重等; 另一类是动态负载均衡算法,常见的有最少链接、最快响应、服务类型、服务质量等,还有策略很多其他策略(hash等),不同的软件实现的不同的负载均衡算法
Nginx中的负载均衡配置
Nginx 的 stream 模块和 http 模块分别支持四层和七层模块的负载均衡。其用法和支持的负载均衡策略大致相同。首先使用 upstream 指令块 和 server 指令指定上游的服务,upstream 指令的用法如下:
Syntax: upstream name { ... }
Default: —
Context: http
官方示例:
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;
}
默认情况下,将服务器的权重设置为1,使用加权循环平衡方法在服务器之间分配请求。在上面的示例中,每个7个请求将按如下方式分发:5个请求发送到backend1.example.com (因为设置了weight=5),一个请求发送给第二和第三服务器。如果在与服务器通信期间发生错误,则请求将被传递到下一个服务器,以此类推,直到尝试所有正在运行的服务器。如果无法从任何服务器获得成功的响应,客户端将收到与最后一台服务器通信的结果
这里定义了三类服务器 , 分别是域名、iP+port、socket 形式指定地址,后面跟上若干配置参数。默认情况下,upstream 指令块中采用的是加权 Round-Robin负载均衡算法。该算法通过加权轮询的方式访问 upstream 中 server 指令指定的上游服务。此时,在 server 指令中我们可以添加一些关于服务的静态配置,比如指定服务的权重(weight)、server 的最大并发连接数(max_conns)、max_fails和 fail_timeout 等。
除了默认的 Round-Robin 算法外,Nginx 中常用的负载均衡策略还有基于客户端ip 地址的 Hash 算法。该算法以客户端的 ip 地址作为 hash 算法的关键字,映射到特定的上游服务器中,当然也可以根据客户段的其他 key 来进行 hash 算法。涉及的配置指令为 ip_hash 和 hash,用法如下:
Syntax: ip_hash;
Default: —
Context: upstream
Syntax: hash key [consistent];
Default: —
Context: upstream
This directive appeared in version 1.7.2.
最后 Nginx 中一种常用的动态负载均衡算法是最少连接数算法。该算法会从所有的上游服务器中找到并发连接数最少的一个,然后将请求转发给它,如果出现多个最少连接数的服务器,则会在这些最少连接数的服务器中继续应用Round-Robin 算法。配置该策略的指令为 least_conn,其指令格式如下
Syntax: least_conn;
Default: —
Context: upstream
This directive appeared in versions 1.3.1 and 1.2.2.
# 将请求传递给活动连接数最少的服务器,同时考虑服务器的权重。
实验
Nginx 四层负载均衡实验
nginx.conf 中添加如下 stream 指令
stream {
server {
listen 3000;
return '3000 sever get ip:$remote_addr!\n';
}
server {
listen 3001;
return '3001 server get ip:$remote_addr!\n';
}
upstream servers {
server 127.0.0.1:3000 weight=2;
server 127.0.0.1:3001;
}
server {
listen 3002;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass servers;
}
}
测试:上述配置用端口 3000 和 3001 模拟两个上游服务器,然后在 upstream 指令块中指定这两个上游服务器的地址,同时给第一个设置权重为 2。由于默认采用的是加权的 Round-Robin 算法,默认服务器的权重为 1。设置为 2,表明 3 次请求中,2 次会转发到 3000 端口,一次会转发到 3001 端口。
[root@centos-13 conf]# curl 127.0.0.1:3002
3000 sever get ip:127.0.0.1!
[root@centos-13 conf]# curl 127.0.0.1:3002
3000 sever get ip:127.0.0.1!
[root@centos-13 conf]# curl 127.0.0.1:3002
3001 server get ip:127.0.0.1!
[root@centos-13 conf]# curl 127.0.0.1:3002
3000 sever get ip:127.0.0.1!
[root@centos-13 conf]# curl 127.0.0.1:3002
3000 sever get ip:127.0.0.1!
[root@centos-13 conf]# curl 127.0.0.1:3002
3001 server get ip:127.0.0.1!
Nginx七层负载均衡实验
七层和四层配置类似。
简单七层负载均衡
Nginx配置如下http指令块
user root;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 8000;
return 200 '8000,server\n';
}
server {
listen 8001;
return 200 '8001 ,server \n';
}
server {
listen 8002;
return 200 '8002 , server\n';
}
upstream backends {
# ip_hash
# hash user_$arg_username;
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
}
server {
listen 80;
location / {
proxy_pass http://backends;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
用 8000,8001 和 8002 三个端口模拟了 3 个服务器,默认使用轮询负载均衡算法,而且三个的权重均为 1。进行如下的 http 请求操作,可以看到 Nginx 转发 http 请求会均匀地分配到 3 个服务器上。
[root@centos-13 conf]# curl 192.168.232.13
8000,server
[root@centos-13 conf]# curl 192.168.232.13
8001 ,server
[root@centos-13 conf]# curl 192.168.232.13
8002 , server
[root@centos-13 conf]# curl 192.168.232.13
8000,server
[root@centos-13 conf]# curl 192.168.232.13
8001 ,server
[root@centos-13 conf]# curl 192.168.232.13
8002 , server
[root@centos-13 conf]# curl 192.168.232.13
8000,server
负载均衡hash算法
打开 ip_hash 指令的注释,这个时候默认是使用客户端的 ip 地址作为
hash 的 key,然后重启 Nginx 服务并进行如下的命令行操作
user root;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 8000;
return 200 '8000,server\n';
}
server {
listen 8001;
return 200 '8001 ,server \n';
}
server {
listen 8002;
return 200 '8002 , server\n';
}
upstream backends {
ip_hash;
# hash user_$arg_username;
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
}
server {
listen 80;
location / {
proxy_pass http://backends;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
测试本机curl
[root@centos-13 conf]# curl 192.168.232.13
8000,server
[root@centos-13 conf]# curl 192.168.232.13
8000,server
[root@centos-13 conf]# curl 192.168.232.13
8000,server
[root@centos-13 conf]# curl 192.168.232.13
8000,server
[root@centos-13 conf]# curl 192.168.232.13
8000,server
使用其他 key 做 hash
注释 ip_hash 指令,我们打开 hash user_$arg_username 这行配置的注释, hash 指令可以让我们根据我们设置的 key 进行 hash,然后根据 hash 值选择上游的服务器。具体测试参看下面的 Linux 命令
user root;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 8000;
return 200 '8000,server\n';
}
server {
listen 8001;
return 200 '8001 ,server \n';
}
server {
listen 8002;
return 200 '8002 , server\n';
}
upstream backends {
# ip_hash;
hash user_$arg_username;
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
}
server {
listen 80;
location / {
proxy_pass http://backends;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
在请求中带上 username 参数,Nginx 中配置的 hash 算法会根据请求中带的 username 参数作为 key 去进行 hash,然后在根据 hash 结果映射上游服务器。username 相同时,选择的上游服务器肯定是一样的,只有在 username 的值发生变化时,返回的响应才可能有变化
[root@centos-13 conf]# curl 192.168.232.13?username=1
8002 , server
[root@centos-13 conf]# curl 192.168.232.13?username=1
8002 , server
[root@centos-13 conf]# curl 192.168.232.13?username=1
8002 , server
[root@centos-13 conf]# curl 192.168.232.13?username=2
8001 ,server
[root@centos-13 conf]# curl 192.168.232.13?username=3
8001 ,server
[root@centos-13 conf]# curl 192.168.232.13?username=qwer
8002 , server