前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【云原生】springcloud09——但愿发长久,空手撕Ribbon

【云原生】springcloud09——但愿发长久,空手撕Ribbon

作者头像
用户10127530
发布2022-10-26 19:42:53
1630
发布2022-10-26 19:42:53
举报
文章被收录于专栏:半旧的技术栈半旧的技术栈

前 言 🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端 ☕专栏简介:深入、全面、系统的介绍springcloud与springcloud Alibaba微服务常用技术栈 🌰 文章简介:本文将介绍Ribbon负载均衡的原理,深入源码进行分析,并且手撕轮询算法,建议收藏备用,创作不易,敬请三连哦 🥒文章推荐: 微服务架构与springcloud 01——微服务入门 微服务架构与springcloud02——父工程构建及支付模块实现 微服务架构与springcloud03——项目热部署与消费者订单模块 微服务架构与springcloud04——Eureka服务注册与发现 springcloud05——Zookeeper实现支付微服务 【云原生】springcloud06——订单服务注册zookeeper 【云原生】springcloud07—Consul的服务注册与发现 【云原生】springcloud08——Ribbon负载均衡调用

文章目录

1.Ribbon默认轮询算法原理

先将注解@RibbonClient注释掉。让它恢复到最开始的轮询算法。

轮询算法的原理如下。妙不妙?

2.RoundRobinRule源码解读

我们先解读下RoundRobinRule轮询算法的源码实现,方便后面仿照轮询算法实现默认的负载均衡算法。

先看接口IRule

代码语言:javascript
复制
public interface IRule {
    Server choose(Object var1);

    void setLoadBalancer(ILoadBalancer var1);

    ILoadBalancer getLoadBalancer();
}

里面有一个choose方法,看看在RoundRobinRule中的具体实现吧。

代码语言:javascript
复制
  public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }
    
  public Server choose(ILoadBalancer lb, Object key) {
  // 如果没有负载均衡算法,返回null
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        } else {
            Server server = null;
            int count = 0;

            while(true) {
                if (server == null && count++ < 10) {
                //获取状态为up(活着的)服务器
                    List<Server> reachableServers = lb.getReachableServers();
                    List<Server> allServers = lb.getAllServers();
                    int upCount = reachableServers.size();
                    int serverCount = allServers.size();
                    if (upCount != 0 && serverCount != 0) {
                        int nextServerIndex = this.incrementAndGetModulo(serverCount);
                        server = (Server)allServers.get(nextServerIndex);
                        if (server == null) {
                            Thread.yield();
                        } else {
                            if (server.isAlive() && server.isReadyToServe()) {
                                return server;
                            }

                            server = null;
                        }
                        continue;
                    }

                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }

                if (count >= 10) {
                    log.warn("No available alive servers after 10 tries from load balancer: " + lb);
                }

                return server;
            }
        }
    }

看看incrementAndGetModulo方法

代码语言:javascript
复制
  private int incrementAndGetModulo(int modulo) {
        int current;
        int next;
        do {
            current = this.nextServerCyclicCounter.get();
            next = (current + 1) % modulo;
        } while(!this.nextServerCyclicCounter.compareAndSet(current, next));

        return next;
    }

3.手写轮询算法

3.1 8001和8002微服务改造

在8001和8002的PaymentController中加上这个方法,用于测试我们的自定义轮询:

代码语言:javascript
复制
@GetMapping("/lb")
public String getPaymentLB(){
    return serverPort;
}

3.2 订单微服务改造

将订单微服务的负载均衡注解去掉

在springcloud包下新建lb.ILoadBalancer接口(自定义负载均衡机制(面向接口))

代码语言:javascript
复制
public interface LoadBalancer {

    // 传入具体的服务集合,返回服务实例
    ServiceInstance instances(List<ServiceInstance> instances);
}

在lb包下新建自定义ILoadBalancer接口的实现类,实现负载均衡的核心逻辑。下面用到了CAS自旋锁的知识,让代码很健壮。

代码语言:javascript
复制
@Component
public class MyLB implements LoadBalancer {
    // 新建一个原子整形实例,记录访问次数,使线程安全
    private AtomicInteger visitCount = new AtomicInteger(0);

    public final int getAndIncrement() {
        int current;
        int next;
        do {
            current = visitCount.get();
            //如果current是最大值,重新计算,否则加1(防止越界),
            // 正常情况肯定不会出现越界的情况,但是我们可以学习源码这种方式,提升代码健壮性
            next = current >= Integer.MAX_VALUE ? 0 : current + 1;
            // 当visitCount与current相等时,说明cas成功将visitCount更新为next,终止循环
            // 当visitCount与current不相等时,说明有其他线程操作atomicInteger,返回true,取反为false,循环操作
        } while (!this.visitCount.compareAndSet(current, next));
        System.out.println("****访问次数:" + next);
        // 返回的next即visitCount自增成功后的值
        return next;
    }

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

接着在我们的OrderController代码逻辑里来引入自己的自旋锁吧。

代码语言:javascript
复制
    @Resource
    private ILoadBalancer iLoadBalancer;
    @Resource
    private DiscoveryClient discoveryClient;
	
    @GetMapping("/payment/lb")
    public String getPaymentLB(){
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

        //判断服务有效
        if (instances ==null || instances.size() <=0){
            return null;
        }
        ServiceInstance serviceInstance = loadBalancer.instances(instances);

        URI uri = serviceInstance.getUri();
        System.out.println(uri);

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

    }

3.3 测试

启动Eureka Server集群7001,7002,支付微服务集群8001,8002,订单80微服务。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1.Ribbon默认轮询算法原理
  • 2.RoundRobinRule源码解读
  • 3.手写轮询算法
    • 3.1 8001和8002微服务改造
      • 3.2 订单微服务改造
        • 3.3 测试
        相关产品与服务
        负载均衡
        负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档