• 【SpringCloud】自定义Ribbon均衡策略(七)


      本章介绍Ribbon自定义规则

    Ribbon自带随机均衡策略

      1、搭建项目,参考:【SpringCloud】服务提供者集群与服务发现Discovery(三) 

      2、新增配置类,配置类中注入IRule的实现类

        在调用者项目(test-springcloud-order8000)中,新增一个配置类com.test.myrule.MySelfRule

     1 package com.test.myrule;
     2 
     3 import com.netflix.loadbalancer.IRule;
     4 import com.netflix.loadbalancer.RandomRule;
     5 import org.springframework.context.annotation.Bean;
     6 import org.springframework.context.annotation.Configuration;
     7 
     8 @Configuration
     9 public class MySelfRule {
    10 
    11     @Bean
    12     public IRule myRule(){
    13         // 定义随机规则
    14         return new RandomRule();
    15     }
    16 }

       注意,官方文档明确给出警告:

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

        如果想要Ribbon客户端共享,那边就放在@Configuration所扫描的地方

        

      3、使用新增的配置类对“CLOUD-PAYMENT-SERVICE”服务生效,在启动类com.test.springcloud.Application上增加注解

        @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)

        name指定针对哪个服务 进行负载均衡,而configuration指定负载均衡的算法具体实现类。

     1 package com.test.springcloud;
     2 
     3 import com.test.myrule.MySelfRule;
     4 import org.springframework.boot.SpringApplication;
     5 import org.springframework.boot.autoconfigure.SpringBootApplication;
     6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
     7 import org.springframework.cloud.netflix.ribbon.RibbonClient;
     8 
     9 @EnableEurekaClient
    10 @SpringBootApplication
    11 @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
    12 public class OrderMain80 {
    13     public static void main(String[] args) {
    14         SpringApplication.run(OrderMain80.class, args);
    15     }
    16 }

      4、启动项目,测试项目

        使用地址:http://localhost:8000/consumer/payment/get/1

        可以看到,的到的响应内容是从提供者的2个节点中,随机的到的。

    自定义Ribbon均衡策略

      1、新增一个IRule的实现类

     1 package com.test.myrule;
     2 
     3 import com.netflix.client.config.IClientConfig;
     4 import com.netflix.loadbalancer.AbstractLoadBalancerRule;
     5 import com.netflix.loadbalancer.ILoadBalancer;
     6 import com.netflix.loadbalancer.Server;
     7 
     8 import java.util.List;
     9 
    10 public class MyCustomeRule extends AbstractLoadBalancerRule {
    11 
    12     private int total = 0;             // 总共被调用的次数,目前要求每台被调用5次
    13     private int currentIndex = 0;    // 当前提供服务的下标
    14 
    15     public Server choose(ILoadBalancer loadBalancer, Object key) {
    16 
    17         if (loadBalancer == null) {
    18             return null;
    19         }
    20         Server server = null;
    21 
    22         while (server == null) {
    23             if (Thread.interrupted()) {
    24                 return null;
    25             }
    26             List<Server> upList = loadBalancer.getReachableServers(); //当前存活的服务
    27             List<Server> allList = loadBalancer.getAllServers();  //获取全部的服务
    28 
    29             int serverCount = allList.size();
    30             if (serverCount == 0) {
    31                 return null;
    32             }
    33 
    34             //int index = rand.nextInt(serverCount);
    35             //server = upList.get(index);
    36             if(total < 5)
    37             {
    38                 server = upList.get(currentIndex);
    39                 total++;
    40             }else {
    41                 total = 0;
    42                 currentIndex++;
    43                 if(currentIndex >= upList.size())
    44                 {
    45                     currentIndex = 0;
    46                 }
    47             }
    48 
    49             if (server == null) {
    50                 Thread.yield();
    51                 continue;
    52             }
    53 
    54             if (server.isAlive()) {
    55                 return (server);
    56             }
    57 
    58             // Shouldn't actually happen.. but must be transient or a bug.
    59             server = null;
    60             Thread.yield();
    61         }
    62         return server;
    63     }
    64 
    65     public Server choose(Object key) {
    66         return choose(getLoadBalancer(), key);
    67     }
    68     public void initWithNiwsConfig(IClientConfig clientConfig) {
    69 
    70     }
    71 }
    View Code

      2、修改配置类,在配置类中注入新编写的实现类,同上

      @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration =MyCustomeRule.class)

      3、启动项目,测试

      使用地址:http://localhost:8000/consumer/payment/get/1

      结果:每5次访问,之后切换节点

    自定义均衡器

      流程:使用DiscoveryClient发现服务—》通过自定义均衡器筛选服务—》使用RestTemplate调用

      1、自定义均衡器接口

    1 public interface LoadBalancer {
    2 
    3     // 筛选出服务
    4     ServiceInstance instances(List<ServiceInstance> serviceInstances);
    5 }

      2、编写接口实现类

     1 @Component
     2 public class MyLB implements  LoadBalancer {
     3 
     4     private AtomicInteger atomicInteger = new AtomicInteger(0);
     5 
     6     public final int getAndIncrement(){
     7         int current;
     8         int next;
     9         do {
    10             current = this.atomicInteger.getAndIncrement();
    11             next = current >= Integer.MAX_VALUE ? 0 : current + 1;
    12         } while (this.atomicInteger.compareAndSet(current, next));
    13         System.out.println("=======next:" + next);
    14         return next;
    15     }
    16 
    17     public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
    18 
    19         int index = getAndIncrement() % serviceInstances.size();
    20         return serviceInstances.get(index);
    21     }
    22 }

      3、编写controller方法

     1 @RestController
     2 @Slf4j
     3 public class OrderController {
     4 
     5     @Autowired
     6     private LoadBalancer loadBalancer;
     7 
     8     @Autowired
     9     private DiscoveryClient discoveryClient;
    10 
    11     // 未使用Ribbon包装restTemplate
    12     private RestTemplate restTemplate = new RestTemplate();
    13 
    14 
    15     @GetMapping("/consumer/payment/get/{id}/lb")
    16     public CommonResult<Payment> getPaymentLB(@PathVariable("id") Long id){
    17         List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
    18         if(instances == null || instances.size() == 0) {
    19             return null;
    20         }
    21 
    22         ServiceInstance instance = loadBalancer.instances(instances);
    23         URI uri = instance.getUri();
    24 
    25         return restTemplate.getForObject(uri + "/payment/get/" + id, CommonResult.class);
    26     }
    27 }

        注意:

        1)restTemplate对象,未被Ribbon包装

        2)此处的instance.getUri()获取的url地址,可能是主机名称+端口,需要改成是IP+端口。而要获取到的是IP+端口,需要服务提供者,在Eureka注册的时候,使用ip注册

        服务端注册增加配置如下:

    1 eureka:
    2   instance:
    3     #  instance:
    4     instance-id: ${spring.cloud.client.ip-address}:${server.port}
    5     # 访问路径可以显示IP地址
    6     prefer-ip-address: true

      4、启动项目,测试

        访问地址:http://localhost:8000/consumer/payment/get/1/lb

        结果:返回结果以达到轮询的目的

  • 相关阅读:
    拷贝某文件至某位置
    Java对象的序列化和反序列
    常见的RuntimeException异常有哪些
    array数组增删元素
    失眠怎么办
    构造函数和函数区别(关键的new操作符)
    匿名函数递归(arguments.callee)和命名函数递归
    localeCompare方法在chrome浏览器取值问题
    random()方法
    iframe 父子页面之间取值
  • 原文地址:https://www.cnblogs.com/h--d/p/12687015.html
Copyright © 2020-2023  润新知