查看源码

负载均衡轮询算法 : rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。

先来看看它的规则实现类接口,即我们要看的轮询规则 --> RoundRobinRule类

	private AtomicInteger nextServerCyclicCounter;

	public RoundRobinRule() {
   
        nextServerCyclicCounter = new AtomicInteger(0);
    }
    
	public RoundRobinRule(ILoadBalancer lb) {
   
        this();
        setLoadBalancer(lb);
    }

通过构造方法初始化规则
① 无参的初始化了一个原子操作类,为保证线程安全;
② 有参构造类设置了加载的服务器集群LoadBalancer。

然后就是通过其中实现的choose方法来选择要指定的服务器。
其中还有一个incrementAndGetModulo方法通过自旋锁来获取服务器对应下标。

	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(); // up 状态的服务器数
            int serverCount = allServers.size(); // 服务器集群总数量

            if ((upCount == 0) || (serverCount == 0)) {
    // 如果没有up状态的服务器或者没有服务器,直接退出选择
                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; // 返回选择的服务器
    }

	private int incrementAndGetModulo(int modulo) {
   
        for (;;) {
    // 自旋锁 得到下标
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

手写轮询规则

根据源码,写一个轮序算法,首先写一个LoadBalancer接口,根据接口写需求。

接口中有一个instances方法,传入的参数就是我们的服务器集群,返回的就是我们要选择的服务器

public interface LoadBalancer {
   

    ServiceInstance instances(List<ServiceInstance> instances);

}

写一个实现类,实现LoadBalancer接口。
首先设置一个全局变量,原子操作类atomicInteger,保证线程安全,减少锁的使用,根据源码的incrementAndGetModulo方法,写出我们的getAndIncrement方法,其中的current就是我们当前的atomicInteger的值,在自旋锁中,直到获取我们的next值并返回,即我们的访问次数,通过访问次数来获取服务器下标
instances方法就是我们要接收传入的服务器集群,通过文章第一行就说过的,

rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标

通过 --> next % 服务器数量 获取 对应选择的服务器下标,通过get方法获得对应的服务器并返回。

@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();
            next = current >= 2147483647 ? 0  : current + 1;
        } while (!this.atomicInteger.compareAndSet(current, next));
        System.out.println("****** 第几次访问 ,next = " + next);
        return next;
    }

    @Override
    public ServiceInstance instances(List<ServiceInstance> instances) {
   
        int index = getAndIncrement() % instances.size();
        return instances.get(index);
    }

}

这里测试一下我们的轮询算法,在我们的controller中进行测试。
通过discoveryClient类通过名称发现我们的服务器集群,然后进行判断,是否为空等条件,如果都通过,就通过负载均衡算法获取对应的服务器,获取服务器的uri,调用服务器的对应方法。


@RestController
@Slf4j
public class OrderController {
   

	@Resource
    private RestTemplate restTemplate;

    @Resource
    private LoadBalancer loadBalancer;

    @Resource
    private DiscoveryClient discoveryClient;

	...

	@GetMapping("/consumer/payment/lb")
    public String getPaymentLB() {
   
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.size() <= 0) {
   
            return null;
        }
        ServiceInstance instance = loadBalancer.instances(instances);
        URI uri = instance.getUri();
        return restTemplate.getForObject(uri + "/payment/lb", String.class);
    }
}

测试结果为成功,调用的服务器轮换调用。

太久没记笔记了0.0…更新一下目前学习cloud的内容,继续努力!