SpringCloud Ribbon组件实现原理及自定义负载规则

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法。

SpringCloud通过Ribbon对服务消费方获取合适的服务提供方地址。

使用@LoadBalanced注解+RestTemplate实现调用。

RestTemplate类中存在List<ClientHttpRequestInterceptor> interceptors = new ArrayList();

Ribbon作用:

在消费方调用服务方之间加了一层拦截器,通过此拦截器来决定消费哪个服务提供方服务。

Ribbon定义了LoadBalancerInterceptor拦截器来实现服务负载

通过LoadBalancerInterceptorConfig来对RestTemplate进行添加拦截器。

@Configuration
    @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }
    }

拦截器内部逻辑

执行LoadBalancerInterceptor的intercept方法

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
    URI originalUri = request.getURI();
    String serviceName = originalUri.getHost();
    Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
    return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}

执行excute方法:

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
    ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
    Server server = this.getServer(loadBalancer, hint);
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    } else {
        RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
        return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
    }
}

执行getServer获取负载之后的服务提供方地址

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
    return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}

执行chooseServer决定具体提供方

public Server chooseServer(Object key) {
    if (this.counter == null) {
        this.counter = this.createCounter();
    }

    this.counter.increment();
    if (this.rule == null) {
        return null;
    } else {
        try {
            return this.rule.choose(key);
        } catch (Exception var3) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});
            return null;
        }
    }
}

此处根据约定的rule规则决定如何获取提供方地址(默认是轮询规则)

Rbbion提供了负载均衡算法

1.RandomRule    随机

2.AvailabilityFilteringRule  会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问

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

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

5.RoundRobinRule    轮询

6.WeightedResponseTimeRule():根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够会切换到WeightedResponseTimeRule

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

此处以随机为例

@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        return null;
    } else {
        Server server = null;

        while(server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            // 获取可用的服务
            List<Server> upList = lb.getReachableServers();
            // 获取全部服务
            List<Server> allList = lb.getAllServers();
            int serverCount = allList.size();
            if (serverCount == 0) {
                return null;
            }

            int index = this.chooseRandomInt(serverCount);
            server = (Server)upList.get(index);
            if (server == null) {
                Thread.yield();
            } else {
                if (server.isAlive()) {
                    return server;
                }

                server = null;
                Thread.yield();
            }
        }

        return server;
    }
}

自定义Rule规则获取服务提供方

自定义Ribbon负载均衡规则,当服务数大于等于5时,按照随机规则,小于5时按照轮询规则

public class NewRule  extends AbstractLoadBalancerRule {
    private static final RandomRule randomRule = new RandomRule();
    private static final RoundRobinRule roundRobinRule = new RoundRobinRule();
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }
    private Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;
        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();
            int serverCount = allList.size();
            if (serverCount == 0) {
                return null;
            }
            //如果服务总数大于等于5,采用随机
            //如果服务总数小于5,采用轮询
            if (serverCount < 5) {
                server = roundRobinRule.choose(lb,key);
            } else {
                server = randomRule.choose(lb,key);
            }
            if (server == null) {
                Thread.yield();
                continue;
            }
            if (server.isAlive()) {
                return (server);
            }
            server = null;
            Thread.yield();
        }
        return server;
    }
}

将规则交由Spring管理

@Configuration
public class RuleConfig {
    @Bean
    public NewRule newRule(){
        return new NewRule();
    }
}

使用@RibbonClient对客户端指定规则

@RibbonClient(name = "consumer" ,configuration = NewRule.class)

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值