聊聊不同集群的微服务如何通过feign调用

前言

之前业务部门的某项目微服务调用关系如下图


后因业务改造需要,该项目需要将服务A部署到另外一个集群,但服务A仍然需要能调用到服务B,调用关系如下图

之前调用方式是负责服务B的开发团队提供相应的feign客户端包给到服务A开发团队,服务A开发团队直接将客户端包引入到项目,在通过@EnableFeignClients来激活feign调用,现在跨了不同集群,而且2个集群间的注册中心也不一样,之前的调用方式就不大适用了。

业务部门的技术负责人就找到我们部门,看我们有没有什么方案。当时我们提供的方案,一种是服务A团队自己开发客户端接口去调用服务B,但这个方案工作量比较大。另外一种方案,就是通过改造openfeign。在业内一直很流行一句话,没有什么是加一层解决不了的

破局

后面我们提供的方案如下图


本质上就是原来服务A直接调用服务B,现在是服务A先通过和服务B同集群的网关,间接调用服务B。思路已经有了,但是我们需要实现业务能够少改代码,就能实现该需求

实现思路

通过feign的url + gateway开启基于服务注册中心自动服务路由功能

改造步骤

1、自定义注解EnableLybGeekFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(LybGeekFeignClientsRegistrar.class)
public @interface EnableLybGeekFeignClients {/*** Alias for the {@link #basePackages()} attribute. Allows for more concise annotation* declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of* {@code @ComponentScan(basePackages="org.my.pkg")}.* @return the array of 'basePackages'.*/String[] value() default {};/*** Base packages to scan for annotated components.* <p>* {@link #value()} is an alias for (and mutually exclusive with) this attribute.* <p>* Use {@link #basePackageClasses()} for a type-safe alternative to String-based* package names.* @return the array of 'basePackages'.*/String[] basePackages() default {};/*** Type-safe alternative to {@link #basePackages()} for specifying the packages to* scan for annotated components. The package of each class specified will be scanned.* <p>* Consider creating a special no-op marker class or interface in each package that* serves no purpose other than being referenced by this attribute.* @return the array of 'basePackageClasses'.*/Class<?>[] basePackageClasses() default {};/*** A custom <code>@Configuration</code> for all feign clients. Can contain override* <code>@Bean</code> definition for the pieces that make up the client, for instance* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.** @return list of default configurations*/Class<?>[] defaultConfiguration() default {};/*** List of classes annotated with @FeignClient. If not empty, disables classpath* scanning.* @return list of FeignClient classes*/Class<?>[] clients() default {};
}

其实是照搬EnableFeignClients,差别只是import的bean不一样

2、扩展原生的FeignClientsRegistrar

扩展的核心内容如下

 @SneakyThrowsprivate void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {String className = annotationMetadata.getClassName();Class feignClientFactoryBeanClz = ClassUtils.forName("org.springframework.cloud.openfeign.FeignClientFactoryBean",Thread.currentThread().getContextClassLoader());String name = getName(attributes);String customUrl = getCustomUrl(getUrl(attributes),name);。。。省略其他代码}private String getCustomUrl(String url,String serviceName){if(StringUtils.hasText(url)){return url;}String gateWay = environment.getProperty("lybgeek.gateWayUrl");if(StringUtils.isEmpty(gateWay)){return url;}if(serviceName.startsWith("http://")){serviceName = StrUtil.trim(serviceName.replace("http://",""));}String customUrl = URLUtil.normalize(gateWay + "/" + serviceName);log.info("feign customed with new url:【{}】",customUrl);return customUrl;}

3、gateway开启基于服务注册中心自动服务路由功能

spring:cloud:gateway:discovery:locator:enabled: truelower-case-service-id: true

测试

测试提供一个消费者、服务提供者、网关、注册中心

在消费者的启动类去掉原生的EnableFeignClients注解,采用我们自定义注解EnableLybGeekFeignClients

@SpringBootApplication
@EnableLybGeekFeignClients(basePackages = "com.github.lybgeek")
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class);}}

消费者application.yml开启feign调用日志

logging:level:# feign调用所在的包com.github.lybgeek.api.feign: debugfeign:client:config:default:# 开启feign记录请求和响应的标题、正文和元数据loggerLevel: FULL

通过消费端调用服务提供者

可以正常访问,我们观察消费者控制台输出的信息


我们可以发现,此次调用,是服务与服务之间的调用,说明我们扩展的feign保留了原本feign的能力

我们对消费者的application.yml,新增如下内容

lybgeek:gateWayUrl: localhost:8000

再通过消费端调用服务提供者


可以正常访问,我们观察消费者控制台输出的信息

同时观察网关控制台输出的信息

我们可以发现,此次调用,是通过网关路由到服务再产生调用,说明我们扩展的feign已经具备通过网关请求服务的能力

总结

可能有朋友会说,何必这么麻烦扩展,直接通过

@FeignClient(name = "${feign.instance.svc:provider}",url="${lybgeek.gateWayUrl: }/${feign.instance.svc:provider}",path = InstanceServiceFeign.PATH,contextId = "instance")

不也可以实现。其实如果带入当时的业务场景考虑,就会发现这种方式,需要改的地方比直接扩展feign多得多,而且一旦出问题,不好集中回滚。有时候脱离业务场景,去谈论技术实现,会容易走偏

demo链接

https://github.com/lyb-geek/springboot-cloud-metadata-ext

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/14215.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程间通信的介绍

目录 进程间通信的目的 进程间通信发展 进程间通信分类 进程间通信的分析 进程间通信的目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程资源共享&#xff1a;多个进程之间共享同样的资源。通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&a…

星辰天合公司产品完成阿里云 PolarDB 数据库产品生态集成认证

近日&#xff0c;XSKY星辰天合旗下产品与阿里云 PolarDB 开源云原生数据库展开产品集成认证测试&#xff0c;并获得阿里云颁发的产品生态集成认证证书。 测试结果表明&#xff0c;星辰天合旗下的融合计算管理平台 XHERE&#xff08;V2&#xff09;、统一数据平台 XEDP&#xf…

基于Spring Boot的医院信息管理系统设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的医院信息管理系统设计与实现 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 后端&#xff1a;Java springboot框架 mybatis 数据库&#xff1a;mysql5.7 开发工具:IDEA…

Java 定义返回一个不能被修改、删除元素的List

为啥突然分享下这个&#xff0c;也是从mybatis源码看到了&#xff0c;所以想分享下&#xff1a; org.apache.ibatis.plugin.InterceptorChain 使用 Collections.unmodifiableList(); 示例&#xff1a; public static void main(String[] args) {List<String> canNotEdit…

C数据结构与算法——单链表 应用

实验任务 (1) 掌握单链表结构及其 C 语言实现&#xff1b; (2) 掌握插入、删除等基本算法&#xff1b; (3) 掌握单链表的基本应用&#xff08;将两个有序线性表合并为一个有序表&#xff09;。 实验内容 使用 C 语言实现单链表的类型定义与算法函数&#xff1b;编写 main()函…

分布式应用之监控Zabbix

分布式应用之监控Zabbix 一、什么是Zabbix? ●zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 ●zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存…

解决Ruoyi单体版本集成Echarts多图表时在Tab模式下不展示问题

目录 背景 一、Tab拆分后无法展示 1、环境简介 2、原始报表功能说明 3、tab切分遇到的问题 二、问题分析及解决 1、问题分析 2、问题解决 3、初始化时图表渲染 4、Tab切换时重渲 总结 背景 最近在使用ruoyi的单体化版本进行Echarts多图表展示时遇到一个问题&#xff0c;r…

【IMX6ULL驱动开发学习】15.IMX6ULL驱动开发问题记录(sleep被kill_fasync打断)

发现问题的契机&#xff1a; 学习异步通知的时候&#xff0c;自己实现一个功能&#xff1a;按键控制蜂鸣器&#xff0c;同时LED灯在闪烁 结果&#xff1a;LED好像也同时被按键控制了 最后调试结果发现&#xff1a; 应用层的sleep被驱动层的kill_fasync打断&#xff0c;所以sle…

JavaScript 将对象数组按字母顺序排序

原文链接&#xff1a;JavaScript 将对象数组按字母顺序排序 这里给出三种解决方案&#xff1a; 1.if条件语句 sort() 2.localeCompare() sort() 3.Collator() sort() sort 用法 语法 array.sort(compareFunction)参数值 参数描述compareFunction可选。定义替代排序顺序…

基于matlab使用PointNet深度学习进行点云分类(附源码)

一、前言 此示例演示如何训练 PointNet 网络以进行点云分类。 点云数据由各种传感器获取&#xff0c;例如激光雷达、雷达和深度摄像头。这些传感器捕获场景中物体的3D位置信息&#xff0c;这对于自动驾驶和增强现实中的许多应用非常有用。例如&#xff0c;区分车辆和行人对于…

【Django学习】(十一)APIView_请求与响应_GenericAPIView

继承DRF中APIView之后&#xff0c;那么当前视图就具备了认证、授权、限流等功能 继承DRF中APIView之后&#xff0c;每一个实例方法中的request为Request对象 Request类拓展了Django中的HttpRequest类&#xff0c;具备很多额外优秀的功能Request类与HttpRequest类中的所有功能兼…

03-MySQL-基础篇-SQL之DDL语句

SQL之DDL语句 前言DDL数据库操作表操作查询操作数据类型案例修改删除 前言 本篇来学习下SQL中的DDL语句 DDL 全称Data Definition Language&#xff0c;数据定义语言&#xff0c;用来定义数据库对象(数据库&#xff0c;表&#xff0c;字段) 数据库操作 查询所有数据库 sh…