负载均衡
服务器端负载均衡
负载均衡是我们处理高并发、缓解网络压力和进行服务端扩容的重要手段之一,但是一般情况下我们所说的负载均衡通常都是指服务端负载均衡,服务端负载均衡又分为两种,一种是硬件负载均衡,还有一种是软件负载均衡。
- 硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,常见的如F5。
- 软件负载均衡则主要是在服务器上安装一些具有负载均衡功能的软件来完成请求分发进而实现负载均衡,常见的就是Nginx。
无论是硬件负载均衡还是软件负载均衡都会维护一个可用的服务端清单,然后通过心跳机制来删除故障的服务端节点以保证清单中都是可以正常访问的服务端节点,此时当客户端的请求到达负载均衡服务器时,负载均衡服务器按照某种配置好的规则从可用服务端清单中选出一台服务器去处理客户端的请求。这就是服务端负载均衡。
在Spring Cloud中服务的发现与消费功能模块里面, Ribbon就是一个基于HTTP和TCP的客户端负载均衡器,当我们将Ribbon和Eureka一起使用时,Ribbon会从Eureka注册中心去获取服务端列表,然后进行轮询访问以到达负载均衡的作用,客户端负载均衡中也需要心跳机制去维护服务端清单的有效性,当然这个过程需要配合服务注册中心一起完成。我们可以看出,客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置。在客户端负载均衡中,所有的客户端节点都有一份自己要访问的服务端清单,这些清单统统都是从Eureka服务注册中心获取的。在Spring Cloud中我们如果想要使用客户端负载均衡,方法很简单,开启@LoadBalanced注解即可,这样客户端在发起请求的时候会先自行选择一个服务端,向该服务端发起请求,从而实现负载均衡。
服务器端的软件负载均衡-Nginx
①负载均衡
单位时间内一台服务器的访问量过大将会使服务器的压力变大,压力难以承受时甚至会导致服务器崩溃。负载均衡服务就是为了避免这样的事情发生。负载均衡就是通过建立一个服务器群,当用户访问资源时先通过一个中间服务器将这次的访问引到服务器群里压力较小服务器去访问。使用这样的方式就可以平衡了服务器的压力,充分发挥了服务器群的性能。
②Nginx负载均衡
负载均衡可以通过负载均衡网络硬件设备和web服务器软件来实现,使用硬件设备的成本比较高,所以我们一般首选web服务器软件来实现负载均衡服务。Nginx通过其反向代理的配置来实现负载均衡。
轮询策略
- 轮询(默认)
将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
- 权重策略
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
- ip_hash
该策略是将前端的访问IP进行hash操作,然后根据hash结果将请求分配到不同的后端服务器节点。这样会使得每个前端访问IP会固定访问一个后端服务器节点,好处是前端用户的session只在一个后端服务器节点上,不必考虑session共享的问题。
- fair
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
- url_hash
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。一般来讲,要用到urlhash,是要配合缓存命中来使用。
举一个例子:有一个服务器集群A,需要对外提供文件下载,由于文件上传量巨大,没法存储到服务器磁盘中,所以用到了第三方云存储来做文件存储。服务器集群A收到客户端请求之后,需要从云存储中下载文件然后返回,为了省去不必要的网络带宽和下载耗时,在服务器集群A上做了一层临时缓存(缓存一个月)。由于是服务器集群,所以同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。在此类场景下,为了使得缓存命中率提高,很适合使用url_hash策略,同一个url(也就是同一个资源请求)会到达同一台机器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取,既减少了带宽,也减少的下载时间。
实践验证
一、用于做测试的项目,无任何业务逻辑,只输出项目的端口号;
package com.yj;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class Application{
@Value("${server.port}")
private String port;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@RequestMapping("/test")
public String test(){
return port;
}
}
二、两台虚拟机
①192.168.37.131的服务器,部署Nginx并启动。
②192.168.37.132的服务器,用于部署前面用于测试项目,依次启动port=8001,8002,8003三个实例,并且设置Centos开放8001,8002,8003这三个端口
ps:在实际的项目集群来说,应该是将同一个项目部署在不同的服务器上面,比如192.168.37.133:8001,192.168.37.134:8001,192.168.37.135:8001这样,此处由于服务器有限,直接部署在一台服务器上面,用不同的端口号以示区别。
三、验证
① 轮询(默认)
events {
worker_connections 1024;
}
http {
upstream yj{
server 192.168.37.132:8001;
server 192.168.37.132:8002;
server 192.168.37.132:8003;
}
server{
listen 80;
location / {
proxy_pass http://yj;
}
}
}
② 权重策略
events {
worker_connections 1024;
}
http {
upstream yj{
server 192.168.37.132:8001 weight=2;
server 192.168.37.132:8002 weight=2;
server 192.168.37.132:8003 weight=6;
}
server{
listen 80;
location / {
proxy_pass http://yj;
}
}
}
③ ip_hash
events {
worker_connections 1024;
}
http {
upstream yj{
ip_hash;
server 192.168.37.132:8001;
server 192.168.37.132:8002;
server 192.168.37.132:8003;
}
server{
listen 80;
location / {
proxy_pass http://yj;
}
}
}
④ fair
events {
worker_connections 1024;
}
http {
upstream yj{
server 192.168.37.132:8001;
server 192.168.37.132:8002;
server 192.168.37.132:8003;
fair;
}
server{
listen 80;
location / {
proxy_pass http://yj;
}
}
}
⑤ url_hash
events {
worker_connections 1024;
}
http {
upstream yj{
hash $request_uri;
server 192.168.37.132:8001;
server 192.168.37.132:8002;
server 192.168.37.132:8003;
}
server{
listen 80;
location / {
proxy_pass http://yj;
}
}
}
访问地址:
http://192.168.37.131/test
可以看到,http://192.168.37.131/test 被反向代理到了 http://192.168.37.132:8001/test,http://192.168.37.132:8002/test,http://192.168.37.132:8003/test,并且实现了负载均衡;