解密Spring Cloud微服务调用:如何轻松获取请求目标方的IP和端口 ...

打印 上一主题 下一主题

主题 844|帖子 844|积分 2532

公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。
目的

Spring Cloud 线上微服务实例都是2个起步,如果出问题后,在没有ELK等日志分析平台,如何确定调用到了目标服务的那个实例,以此来排查问题

效果

可以看到服务有几个实例是上线,并且最终调用了那个实例

考虑到Spring Cloud在版本升级中使用了两种负载均衡实现,Robin和LoadBalancer,下面我们提供两种实现方案
Robin实现方案

1. 技术栈


  • Spring Cloud: Hoxton.SR6
  • Spring Boot: 2.3.1.RELEASE
  • Spring-Cloud-Openfeign: 2.2.3.RELEASE
2. 继承RoundRobinRule,并重写choose方法
  1. /**
  2. * 因为调用目标机器的时候,如果目标机器本身假死或者调用目标不通无法数据返回,那么feign无法打印目标机器。这种场景下我们需要在调用失败(目标机器没有返回)的时候也能把目标机器的ip打印出来,这种场景需要我们切入feign选择机器的逻辑,注入我们自己的调度策略(默认是roundrobin),在里面打印选择的机器即可。
  3. */
  4. @Slf4j
  5. public class FeignRule extends RoundRobinRule {
  6.     @Override
  7.     public Server choose(Object key) {
  8.         Server server = super.choose(key);
  9.         if (Objects.isNull(server)) {
  10.             log.info("server is null");
  11.             return null;
  12.         }
  13.         log.info("feign rule ---> serverName:{}, choose key:{}, final server ip:{}", server.getMetaInfo().getAppName(), key, server.getHostPort());
  14.         return server;
  15.     }
  16.     @Override
  17.     public Server choose(ILoadBalancer lb, Object key) {
  18.         Server chooseServer = super.choose(lb, key);
  19.         List<Server> reachableServers = lb.getReachableServers();
  20.         List<Server> allServers = lb.getAllServers();
  21.         int upCount = reachableServers.size();
  22.         int serverCount = allServers.size();
  23.         log.info("serverName:{} upCount:{}, serverCount:{}", Objects.nonNull(chooseServer) ? chooseServer.getMetaInfo().getAppName() : "", upCount, serverCount);
  24.         for (Server server : allServers) {
  25.             if (server instanceof DiscoveryEnabledServer) {
  26.                 DiscoveryEnabledServer dServer = (DiscoveryEnabledServer) server;
  27.                 InstanceInfo instanceInfo = dServer.getInstanceInfo();
  28.                 if (instanceInfo != null) {
  29.                     InstanceInfo.InstanceStatus status = instanceInfo.getStatus();
  30.                     if (status != null) {
  31.                         log.info("serverName:{} server:{}, status:{}", server.getMetaInfo().getAppName(), server.getHostPort(), status);
  32.                     }
  33.                 }
  34.             }
  35.         }
  36.         return chooseServer;
  37.     }
  38. }
复制代码
3.修改RibbonClients配置
  1. import org.springframework.cloud.netflix.ribbon.RibbonClients;
  2. import org.springframework.context.annotation.Configuration;
  3. /**
  4. * @description:feign 配置
  5. */
  6. @Configuration
  7. @RibbonClients(defaultConfiguration = {FeignRule.class})
  8. public class FeignConfig {
  9. }
复制代码
LoadBalancer实现方案

1. 技术栈


  • Spring Cloud: 2021.0.4
  • Spring Boot: 2.7.17
  • Spring-Cloud-Openfeign: 3.1.4
2. 继承ReactorServiceInstanceLoadBalancer,并实现相关方法
  1. @Slf4j
  2. public class CustomRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
  3.     final AtomicInteger position;
  4.     final String serviceId;
  5.     ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
  6.     public CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
  7.         this(serviceInstanceListSupplierProvider, serviceId, (new Random()).nextInt(1000));
  8.     }
  9.     public CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {
  10.         this.serviceId = serviceId;
  11.         this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
  12.         this.position = new AtomicInteger(seedPosition);
  13.     }
  14.     public Mono<Response<ServiceInstance>> choose(Request request) {
  15.         ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
  16.         return supplier.get(request).next().map((serviceInstances) -> {
  17.             return this.processInstanceResponse(supplier, serviceInstances);
  18.         });
  19.     }
  20.     private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
  21.         Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
  22.         if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
  23.             ((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
  24.         }
  25.         return serviceInstanceResponse;
  26.     }
  27.     private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
  28.         if (instances.isEmpty()) {
  29.             if (log.isWarnEnabled()) {
  30.                 log.warn("No servers available for service: " + this.serviceId);
  31.             }
  32.             return new EmptyResponse();
  33.         } else {
  34.             int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
  35.             ServiceInstance instance = instances.get(pos % instances.size());
  36.             log.info("serverName:{} upCount:{}",instance.getServiceId(),instances.size());
  37.             log.info("feign rule ---> serverName:{}, final server ip:{}:{}", instance.getServiceId(), instance.getHost(),instance.getPort());
  38.             return new DefaultResponse(instance);
  39.         }
  40.     }
  41. }
复制代码
2.修改LoadBalancerClients配置
  1. @Configuration
  2. @LoadBalancerClients(defaultConfiguration = CustomLoadBalancerConfiguration.class)
  3. public class CustomLoadBalancerConfig {
  4. }
  5. @Configuration
  6. class CustomLoadBalancerConfiguration {
  7.     /**
  8.      * 参考默认实现
  9.      * @see org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration#reactorServiceInstanceLoadBalancer
  10.      * @return
  11.      */
  12.     @Bean
  13.     public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
  14.         String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
  15.         return new CustomRoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
  16.     }
  17. }
复制代码
以上两部完成大功告成!
源码下载:
https://github.com/dongweizhao/spring-cloud-example/tree/SR6-OpenFeign
https://github.com/dongweizhao/spring-cloud-example/tree/EurekaOpenFeign

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

魏晓东

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表