负载均衡轮询算法和服务器性能,SpringCloud-Ribbon负载均衡机制、手写轮询算法

Ribbon 内置的负载均衡规则

在 com.netflix.loadbalancer 包下有一个接口 IRule,它可以根据特定的算法从服务列表中选取一个要访问的服务,默认使用的是「轮询机制」

RoundRobinRule:轮询

RandomRule:随机

RetryRule:先按照 RoundRobinRule 的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务

WeightedResponseTimeRule:对 RoundRobinRule 的扩展,响应速度越快的实例选择权重越大,越容易被选择

BestAvailableRule:会过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例

ZoneAvoidanceRule:默认规则,复合判断 server 所在区域的性能和 server 的可用性选择服务器

负载规则的替换

如果不想使用 Ribbon 默认使用的规则,我们可以通过自定义配置类的方式,手动指定使用哪一种。

需要注意的是,自定义配置类不能放在 @ComponentScan 所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的 Ribbon 客户端所共享,达不到特殊化定制的目的了。

因此我们需要在 Spring Boot 启动类所在包的外面新建一个包存放自定义配置类

@Configuration

public class MyselfRule {

@Bean

public IRule rule(){

//随机

return new RandomRule();

}

}

然后在启动类上添加如下注解,指定服务名及自定义配置类

@RibbonClient(value = "CLOUD-PAYMENT-SERVICE", configuration = MyselfRule.class)

Ribbon 默认负载轮询算法的原理

算法概述

rest 接口第几次请求数 % 服务器集群总个数 = 实际调用服务器位置下标,服务每次重启后 rest 请求数变为1

源码

public Server choose(ILoadBalancer lb, Object key) {

if (lb == null) {

log.warn("no load balancer");

return null;

}

Server server = null;

int count = 0;

//循环获取服务,最多获取10次

while (server == null && count++ < 10) {

List reachableServers = lb.getReachableServers();

List 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;

}

//通过此方法获取服务的下标,使用了 CAS 和自旋锁

private int incrementAndGetModulo(int modulo) {

for (;;) {

int current = nextServerCyclicCounter.get();

int next = (current + 1) % modulo;

if (nextServerCyclicCounter.compareAndSet(current, next))

return next;

}

}

手写轮询算法

在服务提供者写一个方法,返回端口号看效果就行

@GetMapping("/payment/lb")

public String roundLb(){

return this.serverPort;

}

负载均衡接口

public interface LoadBalancer {

/**

* 获取服务实例

*/

ServiceInstance getInstance(ListserviceInstances);

}

算法实现类

@Component

public class MyLb implements LoadBalancer {

private AtomicInteger atomicInteger = new AtomicInteger(0);

/**

* 使用「自旋锁」和「CAS」增加请求次数

*/

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));

return next;

}

@Override

public ServiceInstance getInstance(List serviceInstances) {

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

int index = incrementAndGet() % serviceInstances.size();

return serviceInstances.get(index);

}

}

编写服务消费者方法,记得注释 @LoadBalanced 注解,否则不生效

@GetMapping("/consumer/payment/lb")

public String roundLb(){

List instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

if (instances == null || instances.size() <= 0){

return null;

}

ServiceInstance instance = loadBalancer.getInstance(instances);

URI uri = instance.getUri();

return restTemplate.getForObject(uri + "/payment/lb", String.class);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值