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

前 言
🍉 作者简介:半旧518,定制化开发长跑型选手,定制化开发立志坚持写10年博客,专注于java后端
☕专栏简介:深入、全面、定制化开发系统的介绍springcloud与springcloud Alibaba定制化开发微服务常用技术栈
🌰 文章简介:定制化开发本文将介绍负载均衡的原理,定制化开发深入源码进行分析,并且手撕轮询算法,建议收藏备用,创作不易,敬请三连哦
🥒文章推荐:







文章目录

1.Ribbon默认算法原理

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

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

2.RoundRobinRule源码解读

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

先看接口IRule

public interface IRule {    Server choose(Object var1);    void setLoadBalancer(ILoadBalancer var1);    ILoadBalancer getLoadBalancer();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

  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;            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

看看incrementAndGetModulo方法

  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;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.手写轮询算法

3.1 8001和8002微服务改造

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

@GetMapping("/lb")public String getPaymentLB(){    return serverPort;}
  • 1
  • 2
  • 3
  • 4
  • 5

3.2 订单微服务改造

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

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

public interface LoadBalancer {    // 传入具体的服务集合,返回服务实例    ServiceInstance instances(List<ServiceInstance> instances);}
  • 1
  • 2
  • 3
  • 4
  • 5

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

@Componentpublic 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);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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

    @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);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3.3 测试

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

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发