本章介紹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 }
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
結果:返回結果以達到輪詢的目的
