一、使用Ribbon自带的随机策略
-
MyRandomRule
1 package com.sdkj.myrule.rule1; 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 /** 9 * @Author wangshuo 10 * @Date 2022/5/22, 15:31 11 * 自定义随机策略 12 */ 13 @Configuration 14 public class MyRandomRule { 15 16 //随机策略 17 @Bean 18 public IRule myRule(){ 19 20 return new RandomRule(); 21 } 22 }
注意,官方文档明确给出警告:
这个自定义配置类不能放在@Configuration所扫描的当前包及子包下,否则我们自定义的这个配置类就会被所有Ribbon客户端共享,达不到特殊化定制的目的了
如果想要Ribbon客户端共享,那边就放在@Configuration所扫描的地方
-
App
1 package com.sdkj; 2 3 import com.sdkj.myrule.rule1.MyRandomRule; 4 import com.sdkj.myrule.rule2.MyBalancedRule; 5 import org.springframework.boot.SpringApplication; 6 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 9 import org.springframework.cloud.netflix.ribbon.RibbonClient; 10 11 /** 12 * @Author wangshuo 13 * @Date 2022/5/19, 19:46 14 * Please add a comment 15 */ 16 @EnableEurekaClient/*注意这个是client和提供者server不一样*/ 17 @SpringBootApplication(scanBasePackages = {"com.sdkj"}) 18 //开启服务发现 19 @EnableDiscoveryClient 20 //使用自定义的Ribbon规则 21 @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MyRandomRule.class) 22 public class App { 23 24 public static void main(String[] args) { 25 SpringApplication.run(App.class); 26 } 27 }
二、自定义Ribbon均衡策略
-
MyBalancedRule
1 package com.sdkj.myrule.rule2; 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 /** 11 * @Author wangshuo 12 * @Date 2022/5/22, 17:06 13 * 自定义均衡策略 14 */ 15 public class MyBalancedRule extends AbstractLoadBalancerRule { 16 17 private int total = 0; // 总共被调用的次数,目前要求每台被调用5次 18 private int currentIndex = 0; // 当前提供服务的下标 19 20 public Server choose(ILoadBalancer loadBalancer, Object key) { 21 22 if (loadBalancer == null) { 23 return null; 24 } 25 Server server = null; 26 27 while (server == null) { 28 if (Thread.interrupted()) { 29 return null; 30 } 31 List<Server> upList = loadBalancer.getReachableServers(); //当前存活的服务 32 List<Server> allList = loadBalancer.getAllServers(); //获取全部的服务 33 34 int serverCount = allList.size(); 35 if (serverCount == 0) { 36 return null; 37 } 38 39 //int index = rand.nextInt(serverCount); 40 //server = upList.get(index); 41 if(total < 5) 42 { 43 server = upList.get(currentIndex); 44 total++; 45 }else { 46 total = 0; 47 currentIndex++; 48 if(currentIndex >= upList.size()) 49 { 50 currentIndex = 0; 51 } 52 } 53 54 if (server == null) { 55 Thread.yield(); 56 continue; 57 } 58 59 if (server.isAlive()) { 60 return (server); 61 } 62 63 // Shouldn't actually happen.. but must be transient or a bug. 64 server = null; 65 Thread.yield(); 66 } 67 return server; 68 } 69 70 public Server choose(Object key) { 71 return choose(getLoadBalancer(), key); 72 } 73 public void initWithNiwsConfig(IClientConfig clientConfig) { 74 75 } 76 }
-
App
1 package com.sdkj; 2 3 import com.sdkj.myrule.rule1.MyRandomRule; 4 import com.sdkj.myrule.rule2.MyBalancedRule; 5 import org.springframework.boot.SpringApplication; 6 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 9 import org.springframework.cloud.netflix.ribbon.RibbonClient; 10 11 /** 12 * @Author wangshuo 13 * @Date 2022/5/19, 19:46 14 * Please add a comment 15 */ 16 @EnableEurekaClient/*注意这个是client和提供者server不一样*/ 17 @SpringBootApplication(scanBasePackages = {"com.sdkj"}) 18 //开启服务发现 19 @EnableDiscoveryClient 20 //使用自定义的Ribbon规则 21 @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MyBalancedRule.class) 22 public class App { 23 24 public static void main(String[] args) { 25 SpringApplication.run(App.class); 26 } 27 }
三、自定义轮询均衡器
流程:使用DiscoveryClient发现服务—》通过自定义均衡器筛选服务—》使用RestTemplate调用
1、对外提供均衡器接口
1 package com.sdkj.myrule.rule3; 2 3 import org.springframework.cloud.client.ServiceInstance; 4 import org.springframework.stereotype.Component; 5 6 import java.util.List; 7 8 /** 9 * @Author wangshuo 10 * @Date 2022/5/22, 21:41 11 * Please add a comment 12 */ 13 public interface LoadBalancer { 14 15 ServiceInstance getInstance(List<ServiceInstance>serviceInstances); 16 }
2、编写接口实现类
1 package com.sdkj.myrule.rule3; 2 3 import org.springframework.cloud.client.ServiceInstance; 4 import org.springframework.stereotype.Component; 5 6 import java.util.List; 7 import java.util.concurrent.atomic.AtomicInteger; 8 9 /** 10 * @Author wangshuo 11 * @Date 2022/5/22, 21:43 12 * Please add a comment 13 */ 14 @Component 15 public class MyLB implements LoadBalancer{ 16 17 //轮询核心实现 18 @Override 19 public ServiceInstance getInstance(List<ServiceInstance> serviceInstances) { 20 int index = getAtomicInteger() % serviceInstances.size(); 21 return serviceInstances.get(index); 22 } 23 24 private AtomicInteger atomicInteger = new AtomicInteger(0); 25 26 //获取下一个值(线程安全并对最大值做了处理) 27 public final int getAtomicInteger() { 28 int current; 29 int next; 30 do { 31 current = atomicInteger.getAndIncrement(); 32 next = current >= Integer.MAX_VALUE ? 0 : current + 1; 33 }while (atomicInteger.compareAndSet(current,next)); 34 System.out.println("next = " + next); 35 return next; 36 } 37 }
3、编写controller方法
1 package com.sdkj.controller; 2 3 import com.sdkj.myrule.rule3.LoadBalancer; 4 import com.sdkj.response.CommonReturnType; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.cloud.client.ServiceInstance; 7 import org.springframework.cloud.client.discovery.DiscoveryClient; 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.CrossOrigin; 10 import org.springframework.web.bind.annotation.RequestMapping; 11 import org.springframework.web.bind.annotation.ResponseBody; 12 import org.springframework.web.client.RestTemplate; 13 14 import java.util.List; 15 16 /** 17 * @Author wangshuo 18 * @Date 2022/5/19, 19:52 19 * Please add a comment 20 */ 21 @Controller 22 @RequestMapping(value = "/order") 23 @CrossOrigin(origins = {"*"}, allowCredentials = "true") 24 public class OrderController { 25 26 private static final String url = "http://CLOUD-PAYMENT-SERVICE"; 27 @Autowired 28 private RestTemplate restTemplate; 29 @Autowired 30 private LoadBalancer loadBalancer; 31 @Autowired 32 private DiscoveryClient discoveryClient; 33 34 @RequestMapping("/getPaymentById") 35 @ResponseBody 36 public CommonReturnType getById(Long id) { 37 //不要忘记把参数传过去 38 return restTemplate.getForObject(url + "/payment/findById/?id=" + id, CommonReturnType.class); 39 } 40 41 @RequestMapping("/getPort") 42 @ResponseBody 43 public String getPort() { 44 45 return restTemplate.getForObject(url + "/payment/getPort", String.class); 46 } 47 48 @RequestMapping("/getLBPort") 49 @ResponseBody 50 public String getLbPort() { 51 52 List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); 53 if (instances == null || instances.size() == 0) 54 return null; 55 ServiceInstance instance = loadBalancer.getInstance(instances); 56 return restTemplate.getForObject(instance.getUri() + "/payment/getPort", String.class); 57 } 58 }
注意:
1)restTemplate对象,未被Ribbon包装:需要把AppConfig类中的@LoadBalanced注解去掉
2)此处的instance.getUri()获取的url地址,可能是主机名称+端口,需要改成是IP+端口。而要获取到的是IP+端口,需要服务提供者,在Eureka注册的时候,使用ip注册
服务端增加配置 最新配置如下(搭建提供者集群后,除去端口不同其余配置应一样):
server:
# 端口
port: 8001
spring:
#要配置集群、名字必须相同
application:
name: cloud-payment-service
#安全认证
security:
user:
name: root
password: root
#数据源基本配置
datasource:
url: jdbc:mysql://localhost:3306/test_springcloud
username: root
password: 678678
#druid
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
eureka:
instance:
#instance.getUri()获取到的就是这个值(默认为主机名称+端口 / 这里修改为IP地址+端口)
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
# 表示将自己注册进Eureka Server默认为true
register-with-eureka: true
# 是否从Eureka Server抓去已有的注册信息,默认是true
fetch-registry: true
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
service-url:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka1.com:8887/eureka
mybatis:
mapper-locations: classpath:mapping/*.xml
#开启优雅停服
#(必须通过POST请求向Eureka Client发起一个shutdown请求。请求路径为:http://ip:port/shutdown。可以通过任意技术实现,如:HTTPClient,AJAX等。)
management:
endpoint:
shutdown:
enabled: true
本文来自博客园,作者:荣慕平,转载请注明原文链接:https://www.cnblogs.com/rongmuping/articles/16299542.html