项目代码GitHub地址:SpringCloud入门学习
何为负载均衡:通俗点讲就是将一次请求分摊到多个操作单元上。在分布式系统架构中,为了提高系统的可用性,通常会有很多相同的服务提供者,那每一次请求该如何选择哪一个服务呢?这就是负载均衡存在的意义,明确如何选择。
Spring Cloud Ribbon:是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。,它不像服务注册中心、配置中心、API网关那样需要独立部署,它默认存在于每一个Spring Cloud构建的微服务和基础设施中。
-
其实在使用Eureka的时候,你就已经不经意间使用了Ribbon,引入Eureka依赖就会自动引入Ribbon的依赖
当然你也可以再单独引入<!-- 其实上面引入eureka的时候已经自动引入了ribbon相关依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
-
在你使用标注了
@LoadBalanced
的RestTemplate
调用服务的时候,就是用了Ribbon,开启了负载均衡
Ribbon默认使用的是RoundRobinRule
轮询算法(每个服务按顺序请求一次)
Ribbon自带负载均衡策略:
BestAvailableRule :选择一个最小的并发请求的server
AvailabilityFilteringRule :过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)
WeightedResponseTimeRule :根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低
RetryRule :对选定的负载均衡策略机上重试机制
RoundRobinRule:roundRobin方式轮询选择server
RandomRule:随机选择一个server
ZoneAvoidanceRule:复合判断server所在区域的性能和server的可用性选择server -
切换Ribbon默认的负载均衡算法
/** * 使用随机负载均衡算法 */ @Bean public RandomRule randomRule() { return new RandomRule(); }
加入到Ioc容器中即可
-
自定义负载均衡算法
要求:每个服务轮询访问3次
对照着RoundRobinRule
源码,进行了改写,代码如下package com.fei.springcloudconsumer.config; import com.fei.springcloudconsumer.config.log.Loggable; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * @Author: xiaoshijiu * @Date: 2019/7/2 * @Description: 自定义负载均衡算法,每个服务轮询3次 */ public class MyRule extends AbstractLoadBalancerRule implements Loggable { private AtomicInteger nextServerCyclicCounter; // 定义轮询次数 private AtomicInteger totle; public MyRule() { nextServerCyclicCounter = new AtomicInteger(0); totle = new AtomicInteger(0); } public MyRule(ILoadBalancer lb) { this(); setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { getLog().warn("no load balancer"); return null; } Server server = null; int count = 0; while (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if ((upCount == 0) || (serverCount == 0)) { getLog().warn("No up servers available from load balancer: " + lb); return null; } int nextServerIndex = myincrementAndGetModulo(serverCount); server = allServers.get(nextServerIndex); if (server == null) { /* Transient. */ Thread.yield(); continue; } if (server.isAlive() && (server.isReadyToServe())) { return (server); } // Next. server = null; } if (count >= 10) { getLog().warn("No available alive servers after 10 tries from load balancer: " + lb); } return server; } /** * 具体实现算法。可能写的不好,实现效果 */ private int myincrementAndGetModulo(int modulo) { for (;;) { int current = nextServerCyclicCounter.get(); int next = (current + 1) % modulo; // 预期一样的话就把value更新为next if (totle.get() < 3 && nextServerCyclicCounter.compareAndSet(current, current)) { // 获取并自增 totle.getAndIncrement(); if (totle.get() == 3) { // 重新赋值 totle.getAndSet(0); nextServerCyclicCounter.getAndSet(next); } return next; } } } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } }
跟
RoundRobinRule
源码一样,使用AtomicInteger原子类 -
将自定义的算法
MyRule
加入到IOC容器中/** * 自定义负载均衡算法,每个服务轮询3次 */ @Bean public MyRule myRule() { return new MyRule(); }
-
官方文档指出:
这个自定义的类不能放在
@ComponentScan
所扫描的当前包以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon 客户端所共享,也就是我们达不到特殊化指定的目的了。 -
可以使用
@RibbonClient(name = "SPRINGCLOUD-EMPLOYEE-PROVIDER", configuration = MyRule.class)
明确指定针对哪个服务进行负载均衡,而configuration指定负载均衡的算法具体实现类。
当然也可以没有,即对所有服务生效。