04-Ribbon负载均衡
四、Ribbon负载均衡
前面有讲到,添加了@LoadBalanced注解,即可实现负载均衡,其原理是什么呢?
4.1、负载均衡原理
- SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的
- 那么我们发出的请求命名是http://userservice/user/1,怎么就变成了http://localhost:8081的呢?
- 接下来我们简单分析一下源码
4.2、源码跟踪
为什么只输入了service名称就可以访问了呢?之前还要获取ip和端口号
- 显然,必定有一个类或者方法帮我们根据service名称,获取到了服务实例的ip和端口
- 它就是
LoadBalancerInterceptor
,这个类会对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id - 接下来进行源码跟踪
- ①、全局搜索
LoadBalancerInterceptor
- ②、以debug模式运行OrderApplication
- ③、在LoadBalancerInterceptor中的54行设置断点
- ④、点进去execute方法,在RibbonLoadBalancerClient的116行设置断点
- ⑤、在浏览器上再次访问
- ①、全局搜索
4.2.1、LoadBalancerInterceptor
- 可以看到这里的intercept方法,拦截了用户的HttpRequest请求,然后做了如下几件事情
- ①、
request.getURI()
- 获取请求的URI,本例中就是http://userservice/user/1
- ②、
originalUri.getHost()
- 获取uri路径的主机名称,其实就是服务id,userservice
- ③、
this.loadBalancer.execute()
- 处理服务id和用户请求
- ①、
- 这里的
this.loadBalancer
是LoadBalancerClient
类型。接下来继续分析
4.2.2、RibbonLoadBalancerClient
- 进入execute方法
- 该方法的逻辑如下所示
- ①、
getLoadBalancer(serviceId)
- 根据服务id获取
ILoadBalancer
, 而ILoadBalancer
会拿着服务id去eureka中获取服务列表并保存起来
- 根据服务id获取
- ②、
getServer(loadBalancer)
- 利用内置的负载均衡算法,从服务列表中选择一个服务(这里选到的是8082端口)
- ①、
- 放行后,再次访问并跟踪,发现获取的端口号是8083
- 负载均衡就这样实现了!
4.2.3、负载均衡策略IRule
- 在刚才的代码中,可以看到获取服务是通过
getServer
方法来做负载均衡 - 接下来进入这个方法
- 这里是调用了
chooseServer
方法,进入其方法体,发现其是属于BaseLoadBalancer类中,其方法如下所示- 进入到其父类中
- 使用了这个rule变量,进行服务的选择,接下来,跟踪这个变量
- 发现rule的默认值是一个
RoundRobinRule
,可以看到上面的类介绍 - 不难发现,其就表示轮询的意思;到这里整个Ribbon的负载均衡流程就理清楚了
4.2.4、总结
- Spring Cloud Ribbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。整体流程图如下所示
- 基本流程
- 1、拦截我们请求的url地址
- 2、RibbonLoadBalancerClient会从请求url中获取服务名称,也就是userservice
- 3、Ribbon根据服务名称到eureka拉取服务列表
- 4、eureka返回列表:【localhost:8081、localhost:8082、localhost:8083】
- 5、Ribbon内置有多种负载均衡策略,根接口是IRule,默认配置的是轮询策略
- 6、经过负载均衡策略选择好服务后,返回给RibbonLoadBalancerClient
- 7、RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1
- 8、发送真实请求
4.3、负载均衡策略
4.3.1、负载均衡策略
-
负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类,如下所示
-
不同规则的含义如下表所示
-
内置负载均衡规则类 规则描述 RoundRobinRule 轮询规则:简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 AvailabilityFilteringRule 可用性过滤规则:对以下两种服务器进行忽略:
(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。
(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit
属性进行配置。WeightedResponseTimeRule 响应权重时间规则:为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 ZoneAvoidanceRule 规避区域规则:以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 BestAvailableRule 最可用规则:忽略那些短路的服务器,并选择并发数较低的服务器。 RandomRule 随机规则:随机选择一个可用的服务器。 RetryRule 重试实现:重试机制的选择逻辑 默认的实现就是
ZoneAvoidanceRule
,是一种轮询方案
-
4.3.2、自定义负载均衡策略
-
如果不想使用系统默认的策略,也可以使用以下方式自己选择不同的策略,通过定义IRule实现可以修改负责均衡规则。主要有两种方式可以实现
-
1、代码方式
-
①、在order-service中的OrderApplication类中,定义一个新的IRule
-
/** * 自定义负载均衡:这种方式是全局的配置,无论以后访问哪个微服务都使用随机的方式 */ @Bean public IRule iRule() { System.out.println("选择随机的负载均衡策略"); return new RandomRule(); }
-
-
②、重新启动OrderApplication
-
③、刷新网页,访问六次
-
④、查看UserApplication的控制台是哪个控制台数据SQL语句,会发现是随机的选择不同的服务器
- 本次个人测试是3次8081,3次8083,8082端口没有被访问
-
-
2、配置文件方式
-
①、注释掉OrderApplication中规则的方法,这时又还原成轮询的方式
-
②、在order-service的application.yml文件中,添加新的配置规则,userservice是顶层元素
-
# 给某个微服务配置负载均衡规则,只对这个微服务起作用 userservice: ribbon: # 负载均衡规则的类全名 NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
-
-
不过一般是使用默认的负载均衡规则,不需要修改
-
4.3.3、小结
- 修改负载均衡规则的方式
- 1、编码方式
- 特点是全局应用
- 2、配置文件方式
- 特点是针对指定的服务定义的负载均衡策略
- 1、编码方式
4.4、饥饿加载
-
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长;下面是演示过程
- 1、重启OrderApllication服务器
- 2、在浏览器访问,并且显示第1次和第2次访问的时长
- 第一次访问
- 第二次访问
- 第一次访问
- 3、在服务器端的控制台也能见到相应的信息
-
饥饿加载的配置
-
1、饥饿加载会在项目启动的时候创建,降低第一次访问的耗时,通过下面配置开启饥饿加载(ribbon是顶层元素)
-
ribbon: eager-load: enabled: true # 开启饥饿加载 clients: - userservice # clients是一个List集合。如果要配置多个服务名字,则换一行。用-做为前缀,每行写一个
-
-
2、再次重启OrderApplication,会发现一启动就加载了负载均衡的服务
-
3、查看浏览器访问时间,时间少了一半
-