Ribbon提供的负载均衡策略如下:
今天看一下默认的轮询策略是如何实现的
RoundRobinRule核心代码如下:
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.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)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
int nextServerIndex = incrementAndGetModulo(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) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
*
* @param modulo The modulo to bound the value of the counter.
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
通过上面源码可以看出,负载均衡的算法为: rest接口的第几次请求数 % 服务器集群总数量 = 实际调用服务器的下标,每次服务重启,rest接口计数从1开始
比如: 8001,8002两台机器成为集群,List[0] serviceInstances = 127.0.0.1:8002 ,List[1] serviceInstances = 127.0.0.1:8001 服务器集群总数量为2
- rest接口第一次请求时:1 % 2=1 ,得到服务器下标为1,则服务地址为127.0.0.1:8001
- rest接口第二次请求时:2 % 2=0 ,得到服务器下标为0,则服务地址为127.0.0.1:8002
- rest接口第三次请求时:3 % 2=1 ,得到服务器下标为1,则服务地址为127.0.0.1:8001
- rest接口第四次请求时:4 % 2=0 ,得到服务器下标为0,则服务地址为127.0.0.1:8002
- ..........
搞懂了这个,那我们自己实现一下轮询算法:
基础环境,还是使用之前的:
服务注册中心: cloud-eureka-server7001,cloud-eureka-server7002
服务提供者 cloud-provider-payment8001,cloud-provider-payment8002
服务消费者 cloud-consumer-order80
在服务提供者cloud-provider-payment8001,cloud-provider-payment8002 写个方法,简单返回下接口即可:
@GetMapping("/payment/getServer")
public String getServer(){
return this.serverPort;
}
在服务消费者 cloud-consumer-order80,注释掉LoadBalanced注解,我们自己写负载均衡算法
至此,基础环境配置完成
手写轮询算法
1.在服务消费者里新建负载均衡策略接口
public interface LoadBalancer {
/**
* @Description 获取服务实例
*
*/
ServiceInstance getServiceInstance(List<ServiceInstance> serviceInstances);
}
2.编写实现类,实现核心逻辑
@Component
@Slf4j
public class PollLoadBalancer implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int incrementAndGet() {
int current;
int next;
do {
current = atomicInteger.get();
//防溢出
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (!atomicInteger.compareAndSet(current, next));
log.info("===================第几次请求:"+next);
return next;
}
@Override
public ServiceInstance getServiceInstance(List<ServiceInstance> serviceInstances) {
// rest接口的第几次请求数 % 服务器集群总数量=index
int index = incrementAndGet() % serviceInstances.size();
return serviceInstances.get(index);
}
}
3.控制器
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancer loadBalancer;
@GetMapping("/consumer/payment/pollLb")
public String PollLb(){
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PROVIDER-SERVICE");
if (instances == null || instances.size() <= 0){
return null;
}
ServiceInstance instance = loadBalancer.getServiceInstance(instances);
URI uri = instance.getUri();
return restTemplate.getForObject(uri + "/payment/getServer", String.class);
}
}
效果如下,可以看出,我们自定义的轮询策略实现成功