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

目的

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

效果

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

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

Robin实现方案

1. 技术栈

  • Spring Cloud: Hoxton.SR6
  • Spring Boot: 2.3.1.RELEASE
  • Spring-Cloud-Openfeign: 2.2.3.RELEASE

2. 继承RoundRobinRule,并重写choose方法

/**
* 因为调用目标机器的时候,如果目标机器本身假死或者调用目标不通无法数据返回,那么feign无法打印目标机器。这种场景下我们需要在调用失败(目标机器没有返回)的时候也能把目标机器的ip打印出来,这种场景需要我们切入feign选择机器的逻辑,注入我们自己的调度策略(默认是roundrobin),在里面打印选择的机器即可。
*/
@Slf4j
public class FeignRule extends RoundRobinRule { @Override
public Server choose(Object key) {
Server server = super.choose(key);
if (Objects.isNull(server)) {
log.info("server is null");
return null;
}
log.info("feign rule ---> serverName:{}, choose key:{}, final server ip:{}", server.getMetaInfo().getAppName(), key, server.getHostPort());
return server;
} @Override
public Server choose(ILoadBalancer lb, Object key) {
Server chooseServer = super.choose(lb, key); List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
log.info("serverName:{} upCount:{}, serverCount:{}", Objects.nonNull(chooseServer) ? chooseServer.getMetaInfo().getAppName() : "", upCount, serverCount);
for (Server server : allServers) {
if (server instanceof DiscoveryEnabledServer) {
DiscoveryEnabledServer dServer = (DiscoveryEnabledServer) server;
InstanceInfo instanceInfo = dServer.getInstanceInfo();
if (instanceInfo != null) {
InstanceInfo.InstanceStatus status = instanceInfo.getStatus();
if (status != null) {
log.info("serverName:{} server:{}, status:{}", server.getMetaInfo().getAppName(), server.getHostPort(), status);
}
}
}
} return chooseServer;
}
}

3.修改RibbonClients配置

import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;
/**
* @description:feign 配置
*/
@Configuration
@RibbonClients(defaultConfiguration = {FeignRule.class})
public class FeignConfig {
}

LoadBalancer实现方案

1. 技术栈

  • Spring Cloud: 2021.0.4
  • Spring Boot: 2.7.17
  • Spring-Cloud-Openfeign: 3.1.4

2. 继承ReactorServiceInstanceLoadBalancer,并实现相关方法

@Slf4j
public class CustomRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
final AtomicInteger position;
final String serviceId;
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; public CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
this(serviceInstanceListSupplierProvider, serviceId, (new Random()).nextInt(1000));
} public CustomRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.position = new AtomicInteger(seedPosition);
} public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances);
});
} private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());
} return serviceInstanceResponse;
} private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + this.serviceId);
} return new EmptyResponse();
} else { int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
ServiceInstance instance = instances.get(pos % instances.size());
log.info("serverName:{} upCount:{}",instance.getServiceId(),instances.size());
log.info("feign rule ---> serverName:{}, final server ip:{}:{}", instance.getServiceId(), instance.getHost(),instance.getPort());
return new DefaultResponse(instance);
}
}
}

2.修改LoadBalancerClients配置

@Configuration
@LoadBalancerClients(defaultConfiguration = CustomLoadBalancerConfiguration.class)
public class CustomLoadBalancerConfig {
} @Configuration
class CustomLoadBalancerConfiguration {
/**
* 参考默认实现
* @see org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration#reactorServiceInstanceLoadBalancer
* @return
*/
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new CustomRoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}

以上两部完成大功告成!

源码下载:

https://github.com/dongweizhao/spring-cloud-example/tree/SR6-OpenFeign

https://github.com/dongweizhao/spring-cloud-example/tree/EurekaOpenFeign

解密Spring Cloud微服务调用:如何轻松获取请求目标方的IP和端口的更多相关文章

  1. spring cloud 微服务调用--ribbon和feign调用

    这里介绍ribbon和feign调用两种通信服务调用方式,同时介绍如何引入第三方服务调用.案例包括了ribbon负载均衡和hystrix熔断--服务降级的处理,以及feign声明式服务调用.例子包括s ...

  2. Spring Cloud微服务Sentinel+Apollo限流、熔断实战总结

    在Spring Cloud微服务体系中,由于限流熔断组件Hystrix开源版本不在维护,因此国内不少有类似需求的公司已经将眼光转向阿里开源的Sentinel框架.而以下要介绍的正是作者最近两个月的真实 ...

  3. Spring Cloud微服务限流之Sentinel+Apollo生产实践

    Sentinel概述 在基于Spring Cloud构建的微服务体系中,服务之间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素.在并发流量比较高的情况下,由于网络调用之间存 ...

  4. Spring Cloud微服务系列文,服务调用框架Feign

    之前博文的案例中,我们是通过RestTemplate来调用服务,而Feign框架则在此基础上做了一层封装,比如,可以通过注解等方式来绑定参数,或者以声明的方式来指定请求返回类型是JSON.    这种 ...

  5. 【多线程】java多线程Completablefuture 详解【在spring cloud微服务之间调用,防止接口超时的应用】【未完成】

    参考地址:https://www.jianshu.com/p/6f3ee90ab7d3 示例: public static void main(String[] args) throws Interr ...

  6. Spring Cloud 微服务六:调用链跟踪Spring cloud sleuth +zipkin

    前言:随着微服务系统的增加,服务之间的调用关系变得会非常复杂,这给运维以及排查问题带来了很大的麻烦,这时服务调用监控就显得非常重要了.spring cloud sleuth实现了对分布式服务的监控解决 ...

  7. 如何优化Spring Cloud微服务注册中心架构?

    作者: 石杉的架构笔记 1.再回顾:什么是服务注册中心? 先回顾一下什么叫做服务注册中心? 顾名思义,假设你有一个分布式系统,里面包含了多个服务,部署在不同的机器上,然后这些不同机器上的服务之间要互相 ...

  8. 全链路实践Spring Cloud 微服务架构

    Spring Cloud 微服务架构全链路实践Spring Cloud 微服务架构全链路实践 阅读目录: 网关请求流程 Eureka 服务治理 Config 配置中心 Hystrix 监控 服务调用链 ...

  9. spring cloud微服务快速教程之(七) Spring Cloud Alibaba--nacos(一)、服务注册发现

    0.前言 什么是Spring Cloud Alibaba? Spring Cloud Alibaba 是阿里开源的,致力于提供微服务开发的一站式解决方案.此项目包含开发分布式应用微服务的必需组件,方便 ...

  10. Spring Cloud微服务技术概览

    Spring Cloud 是一系列框架的有序集合.它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均衡.断路器.数据监控等,都 ...

随机推荐

  1. AcWing 4798. 打怪兽题解

    可以从 \(1\) 枚举到 \(n\) 表示要打多少个怪兽. 因为你要打 \(t\) 个怪兽,并不管顺序,所以我们可以对 \([1, t]\) 这一段进行排序,然后计算 \(a[t], a[t - 2 ...

  2. 常见的 NoSQL 数据库有哪些?

    前言 今天我们来介绍一下工作开发中常见的一些NoSQL数据库及其基本特点.欢迎在评论区留下文章中没有介绍且好用的​NOSQL数据库. 什么是NOSQL数据库 非关系型数据库又被称为 NoSQL(Not ...

  3. 通过 tree shaking 移除无用代码

    tree shaking 依赖于ES Module 的静态语法分析,在项目编译时移除无用的代码以减少文件体积. usedExports 在文件中,我们可能定义了变量但是暂时又没有用到,这样会造成空间的 ...

  4. 手把手教你使用人工智能生成游戏 3D 素材

    引言 生成式 AI 已成为游戏开发中艺术工作流的重要组成部分.然而,正如我在 之前的文章 中描述的,从文本到 3D 的实用性仍落后于 2D.不过,这种情况正在改变.本文我们将重新审视 3D 素材生成的 ...

  5. Elasticsearch之环境搭建

    一.安装 elasticsearch -- 拉取镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:8.9.1 -- 创建 doc ...

  6. 推荐一个react上拉加载更多插件:react-infinite-scroller

    在开发网页和移动应用时,经常需要处理大量数据的展示和加载.如果数据量非常大,一次性全部加载可能会导致页面卡顿或崩溃.为了解决这个问题,我们可以使用无限滚动(Infinite Scroll)的技术.Re ...

  7. 第3章 Git最最常用命令大全

    相信来查命令的同学,根本不是来学具体某个命令的作用的,只是想来查看命令的语法,博主深知这一点(因为博主也是这样过来的),相信这篇文章,将会带给你在工作中最常用的命令,让你一打开就是命令大全!! 喜欢这 ...

  8. 《HelloGitHub》第 90 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweiha ...

  9. Codechef - Longest AND Subarray(位运算)

    题目大意   给定一个正整数N,其序列为[1, 2, 3, ..., N],找到一个长度最大的连续子列,使得其所有元素取与运算的结果为正(最终输出只需要输出最大长度即可). 思路   刚开始可能并不好 ...

  10. strcpy()函数详解

    strcpy()函数是C语言中的一个复制字符串的库函数,以下将详细解释说明一下: · 函数声明以及实现代码 char *strcpy(char *dst, const char *src);char ...