四. Ribbon负载均衡服务调用 (3)

将com.netflix.loadbalancer.RoundRobinRule源码的负载均衡算法部分分析如下(代码中标注了中文注释):

package com.netflix.loadbalancer; /** * The most well known and basic load balancing strategy, i.e. Round Robin Rule. */ public class RoundRobinRule extends AbstractLoadBalancerRule { //... 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) { //获得还活着的健康的服务实例(机器)即可达的,也就是Status为up的实例 List<Server> reachableServers = lb.getReachableServers(); //获取所有服务实例,无论是死是活,只要注册进服务中心即可 List<Server> allServers = lb.getAllServers(); //Status为up的服务实例数量 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; } //调用服务器位置下标 = incrementAndGetModulo(服务器集群总数) 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; } } } 4.3 自己实现轮询负载均衡算法

首先我们将服务注册中心(7001/7002构成集群)启动,然后在服务提供方8001/8002中的Controller中添加功能,用来一会儿测试服务消费方80来轮询访问CLOUD-PAYMENT-SERVICE服务:

@GetMapping("/payment/lb") public String getPaymentLB(){ return serverPort; }

服务提供方的这个方法就是简单的在页面输出自己的端口号,也就是我们可以在页面区分访问的CLOUD-PAYMENT-SERVICE服务到底对应的是8001实例还是8002实例。

启动8001/8002,将两个服务实例注册进服务注册中心后,我们再改造服务消费方80服务,分为以下四步:

首先我们先让RestTemplate失去Ribbon中的负载均衡能力,取消掉@LoadBalanced注解即可:

@Configuration public class ApplicationContextConfig { @Bean // @LoadBalanced//使用该注解赋予RestTemplate负载均衡的能力 public RestTemplate getRestTemplate() { return new RestTemplate(); } } //applicationContext.xml <bean>

然后编写自己的负载均衡接口:

给接口定义了方法instances用于在服务提供方服务的所有服务实例中选择一个具体实例。

public interface LoadBalancer { /** * 从服务列表中用负载均衡算法选择出具体的实例 * @param serviceInstances 服务列表 * @return */ ServiceInstance instances(List<ServiceInstance> serviceInstances); }

用轮询负载均衡算法实现负载均衡接口:

RoundRobinRule源码中用for(;;)实现的自旋锁,这里我们用do{} while();实现自旋锁。

@Component public class MyLB implements LoadBalancer { private AtomicInteger atomicInteger = new AtomicInteger(0); public final int getAndIncrement() { int current; int next; //自旋锁 do { current = this.atomicInteger.get(); //初始值为0 next = current >= 2147483647 ? 0 : current + 1; //最大整数 } while (!this.atomicInteger.compareAndSet(current, next)); System.out.println("===> 访问次数next:" + next); return next; } /** * 从服务列表中用轮询负载均衡算法选择出具体的实例 * Rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 * * @param serviceInstances 服务列表 * @return */ @Override public ServiceInstance instances(List<ServiceInstance> serviceInstances) { int index = getAndIncrement() % serviceInstances.size(); return serviceInstances.get(index); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsppdf.html