解密Spring Cloud微服务调用:如何轻松获取请求目标方的IP和端口
公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。
目的
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方法
/**
* 因为调用目标机器的时候,如果目标机器本身假死或者调用目标不通无法数据返回,那么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和端口的更多相关文章
- spring cloud 微服务调用--ribbon和feign调用
这里介绍ribbon和feign调用两种通信服务调用方式,同时介绍如何引入第三方服务调用.案例包括了ribbon负载均衡和hystrix熔断--服务降级的处理,以及feign声明式服务调用.例子包括s ...
- Spring Cloud微服务Sentinel+Apollo限流、熔断实战总结
在Spring Cloud微服务体系中,由于限流熔断组件Hystrix开源版本不在维护,因此国内不少有类似需求的公司已经将眼光转向阿里开源的Sentinel框架.而以下要介绍的正是作者最近两个月的真实 ...
- Spring Cloud微服务限流之Sentinel+Apollo生产实践
Sentinel概述 在基于Spring Cloud构建的微服务体系中,服务之间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素.在并发流量比较高的情况下,由于网络调用之间存 ...
- Spring Cloud微服务系列文,服务调用框架Feign
之前博文的案例中,我们是通过RestTemplate来调用服务,而Feign框架则在此基础上做了一层封装,比如,可以通过注解等方式来绑定参数,或者以声明的方式来指定请求返回类型是JSON. 这种 ...
- 【多线程】java多线程Completablefuture 详解【在spring cloud微服务之间调用,防止接口超时的应用】【未完成】
参考地址:https://www.jianshu.com/p/6f3ee90ab7d3 示例: public static void main(String[] args) throws Interr ...
- Spring Cloud 微服务六:调用链跟踪Spring cloud sleuth +zipkin
前言:随着微服务系统的增加,服务之间的调用关系变得会非常复杂,这给运维以及排查问题带来了很大的麻烦,这时服务调用监控就显得非常重要了.spring cloud sleuth实现了对分布式服务的监控解决 ...
- 如何优化Spring Cloud微服务注册中心架构?
作者: 石杉的架构笔记 1.再回顾:什么是服务注册中心? 先回顾一下什么叫做服务注册中心? 顾名思义,假设你有一个分布式系统,里面包含了多个服务,部署在不同的机器上,然后这些不同机器上的服务之间要互相 ...
- 全链路实践Spring Cloud 微服务架构
Spring Cloud 微服务架构全链路实践Spring Cloud 微服务架构全链路实践 阅读目录: 网关请求流程 Eureka 服务治理 Config 配置中心 Hystrix 监控 服务调用链 ...
- spring cloud微服务快速教程之(七) Spring Cloud Alibaba--nacos(一)、服务注册发现
0.前言 什么是Spring Cloud Alibaba? Spring Cloud Alibaba 是阿里开源的,致力于提供微服务开发的一站式解决方案.此项目包含开发分布式应用微服务的必需组件,方便 ...
- Spring Cloud微服务技术概览
Spring Cloud 是一系列框架的有序集合.它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均衡.断路器.数据监控等,都 ...
随机推荐
- AcWing 4798. 打怪兽题解
可以从 \(1\) 枚举到 \(n\) 表示要打多少个怪兽. 因为你要打 \(t\) 个怪兽,并不管顺序,所以我们可以对 \([1, t]\) 这一段进行排序,然后计算 \(a[t], a[t - 2 ...
- 常见的 NoSQL 数据库有哪些?
前言 今天我们来介绍一下工作开发中常见的一些NoSQL数据库及其基本特点.欢迎在评论区留下文章中没有介绍且好用的NOSQL数据库. 什么是NOSQL数据库 非关系型数据库又被称为 NoSQL(Not ...
- 通过 tree shaking 移除无用代码
tree shaking 依赖于ES Module 的静态语法分析,在项目编译时移除无用的代码以减少文件体积. usedExports 在文件中,我们可能定义了变量但是暂时又没有用到,这样会造成空间的 ...
- 手把手教你使用人工智能生成游戏 3D 素材
引言 生成式 AI 已成为游戏开发中艺术工作流的重要组成部分.然而,正如我在 之前的文章 中描述的,从文本到 3D 的实用性仍落后于 2D.不过,这种情况正在改变.本文我们将重新审视 3D 素材生成的 ...
- Elasticsearch之环境搭建
一.安装 elasticsearch -- 拉取镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:8.9.1 -- 创建 doc ...
- 推荐一个react上拉加载更多插件:react-infinite-scroller
在开发网页和移动应用时,经常需要处理大量数据的展示和加载.如果数据量非常大,一次性全部加载可能会导致页面卡顿或崩溃.为了解决这个问题,我们可以使用无限滚动(Infinite Scroll)的技术.Re ...
- 第3章 Git最最常用命令大全
相信来查命令的同学,根本不是来学具体某个命令的作用的,只是想来查看命令的语法,博主深知这一点(因为博主也是这样过来的),相信这篇文章,将会带给你在工作中最常用的命令,让你一打开就是命令大全!! 喜欢这 ...
- 《HelloGitHub》第 90 期
兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweiha ...
- Codechef - Longest AND Subarray(位运算)
题目大意 给定一个正整数N,其序列为[1, 2, 3, ..., N],找到一个长度最大的连续子列,使得其所有元素取与运算的结果为正(最终输出只需要输出最大长度即可). 思路 刚开始可能并不好 ...
- strcpy()函数详解
strcpy()函数是C语言中的一个复制字符串的库函数,以下将详细解释说明一下: · 函数声明以及实现代码 char *strcpy(char *dst, const char *src);char ...