【SpringCloud】使用OpenFeign进行微服务化改造

目录

    • 一、需求与背景
    • 二、OpenFeign 远程调用技术原理
    • 三、项目代码演示
      • 3.1 引入依赖
      • 3.2 实现@OpenFeign注解修饰接口
      • 3.3 指定 OpenFeign 远程调用接口的扫描路径
    • 四、OpenFeign 在日志中打印Request和Response
    • 五、OpenFeign 客户端超时配置
    • 六、使用 OpenFeign 实现服务降级
      • 6.1 实现降级
        • 6.1.1 引入依赖
        • 6.1.2 降级方式一
        • 6.1.2 降级方式二
      • 6.2 开启降级配置

一、需求与背景

  • 在分布式系统的场景下,大多数服务的拆分要进行微服务化,各微服务的内部不可能直接依赖其它微服务的实现,而是将该微服务内的POJO 类提取到一个公共模块,供其它的微服务进行依赖。
  • 为了让代码的职责更加明确,实现更加清晰,就需要将业务代码与框架使用的模版代码分离,让开发人员将精力集中到业务功能的实现上。

二、OpenFeign 远程调用技术原理

  • OpenFeign 是 Netflix 开源的一个项目,可以将远程的接口调用转换成本地调用的形式,简化了开发,并且做到了业务功能代码与框架模版代码分离的效果。
  • OpenFeign 的使用核心是基于 @FeignClient@EnableFeignClients 这2个注解。@EnableFeignClients 注解加在项目的启动类上,当项目在启动时,会扫描注解中指定的目录,会为所有使用了 @FeignClient 注解的接口创建一个动态代理对象,这些被创建的动态代理对象从属于@FeignClient注解所修饰的接口的实例,并把这些动态代理对象加载到 Spring 的 Bean 容器内,随后会被注入到需要进行远程调用的本地服务对象内。

三、项目代码演示

接下来使用一个积分项目进行演示。这个项目分为3个微服务:商品(goods)、用户(customer)、计算(caculation)。用户通过购买商品获取积分;不同的商品可能参加了不同的活动,有的商品不赠送积分;不同的用户等级可能通过购物获取的积分不同。

3.1 引入依赖

要使用 openFeign 组件,首先我们需要先引入相应的依赖。注意,使用openFeign 还需要引入loadBalancer 的依赖:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

由于在父项目中已经引入了 spring-cloud-dependencies 的依赖,上述2个组件的版本号可通过版本仲裁确定,无需指定。

3.2 实现@OpenFeign注解修饰接口

实现OpenFeign组件的远程调用,首先需要实现@FeignClient 注解修饰的接口,该接口会将本地调用转化成远程的接口调用。

@FeignClient(value = "coin-goods-svc", path = "/goods")
public interface GoodsService {
    // 获取指定商品
    @GetMapping("/getGoods")
    GoodsInfo getGoods(@RequestParam("id") Long id);
    
    // 获取整个购物车内的商品
    @GetMapping("/getBatch")
    Map<Long, GoodsCart> getGoodsCart(@RequestParam("ids") Collection<Long> ids);
}

原来我们是直接通过 webclient 直接发起的远程接口调用,内容如下:

webClientBuilder.build().get()
    .uri("http://coin-goods-svc/goods/getGoods?id=" + id)
    .retrieve()
    .bodyToMono(GoodsInfo.class)
    .block();        

现在我们直接注入 GoodsService 并调取对应方法:

@Autowired
private GoodsService goodsService;

public Integer getGoodsCoin(Long id) {
	// 忽略无关逻辑
	// 获取指定商品
	GoodsInfo goods = goodsService.getGoods(id);
}

现在我们不必再业务代码里面指定调用接口的URI 和 Method 了,做到了代码的职责分离。

3.3 指定 OpenFeign 远程调用接口的扫描路径

接下来,我们需要在微服务的启动类上加上 @EnableFeignClients 注解,这样服务启动时的,才会扫描指定目录下使用了 @FeignClient 注解的接口,并为其生成对应的动态代理实例。

// 省略其它注解
@EnableFeignClients(basePackages = {"com.fyup"})
public class CustomerApplication{
	// 忽略具体实现
}

我们也可以不指定具体的扫描路径,而是直接指定要进行代理的接口,不过这种方式在 OpenFeign 远程调用接口较多时会很麻烦,不具备扩展性,不符合开闭原则。

// 通过指定具体的远程调用接口
// 省略其它注解
@EnableFeignClients(clients = {GoodsService.class})
public class CustomerApplication{
	// 忽略具体实现
}

四、OpenFeign 在日志中打印Request和Response

OpenFeign 还提供了在日志中打印远程调用细节的功能,只需要开启相应配置,并向 Spring Bean 容器内注册对应的 Bean 即可。可打印的日志分为4个级别,这里贴一下源码的截图:

在这里插入图片描述
如上所示,如果我们选择 Level.FULL 级别,会打印完整的 Request 和 Response 的 Header、Body。
因为 OpenFeign 组件内的日志都是以 Debug 级别输出的,所以我们需要现将对应的远程调用接口的日志输出级别打开。

logging:
  level:
    com.fyup.coin.customer.feign.GoodsService: debug

因为要打印完整的 URL、Method、以及Request 和 Response 的 Header、Body 等信息,所以还需要在配置类中注入 Level.FULL 的 Bean。实现如下:

@Bean
Logger.Level feignLogger() {
    return Logger.Level.FULL;
}

五、OpenFeign 客户端超时配置

接口超时不响应会悬挂消费者请求,大量的请求超时未响应会给系统造成很大压力,在调用链路过长的情况下可能会在系统内部产生雪崩反应。
我们可以在Feign客户端的application.yml 文件中的 feign.client.config 配置项配置Feign 客户端远程调用的超时时间。feign.client.config.default 配置项配置全局的超时时间,通过 feign.client.config.${serviceName} 配置访问某个特定微服务的超时时间。 示例配置如下:

# 其它忽略
feign:
  client:
    config:
      # 全局超时配置
      default:
        connectTimeout: 1000
        readTimeout: 3000
      # 针对coin-goods-svc服务的超时配置
      coin-goods-svc:
        connectTimeout: 1000
        readTimeout: 2000

其中,connectTimeout 表示的是服务消费者与服务提供者建立远程连接的超时时间;
readTimeout 是指从发出请求开始,到服务端响应请求之前为客户端设置的请求超时时间。

六、使用 OpenFeign 实现服务降级

我们也可以使用 OpenFeign 组件实现服务降级,用法就是使用 @FeignClient 注解修饰远程调用接口时,使用 @FiegnClient 注解的 fallback 属性或者 fallbackFactory 属性指定降级方法的降级类即可。

6.1 实现降级

6.1.1 引入依赖

因为OpenFeign 实现服务降级依赖于 hystrix ,因此我们需要先引入hystrix 依赖如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.9.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-ribbon</artifactId>
        </exclusion>
    </exclusions>
</dependency>
6.1.2 降级方式一

下面实现了一个降级类示例,该降级类是通过 @FiegnClient 注解的 fallback 属性来指定的:

@Slf4j
@Component
public class GoodsServiceDowngrade implements GoodsService {

	// 获取指定商品
    @Override
    public GoodsInfo getGoods(Long id) {
    	// 对应的远程调用方法执行失败后会调用该方法,根据业务情况自行设计实现细节,此处仅打印日志
        log.info("fallback method in getGoods.");
        return null;
    }

	// 获取整个购物车内的商品
    @Override
    public Map<Long, GoodsCart> getGoodsCart(Collection<Long> ids) {
        // 对应的远程调用方法执行失败后会调用该方法,根据业务情况自行设计实现细节,此处仅打印日志
        log.info("fallback method in getGoodsCart.");
        return null;
    }
}

接下来在远程调用接口内指定降级类:

@FeignClient(value = "coin-goods-svc", path = "/goods", fallback=GoodsServiceDowngrade.class)
public interface GoodsService {
    // 获取指定商品
    @GetMapping("/getGoods")
    GoodsInfo getGoods(@RequestParam("id") Long id);
    
    // 获取整个购物车内的商品
    @GetMapping("/getBatch")
    Map<Long, GoodsCart> getGoodsCart(@RequestParam("ids") Collection<Long> ids);
}
6.1.2 降级方式二

下面实现的降级类,是通过 @FiegnClient 注解的 fallbackFactory 属性来指定的,与上面不同的是,使用降级工厂处理降级可以获取到远程调用方法失败的原因。

@Slf4j
@Component
public class GoodsServiceDowngradeFactory implements FallbackFactory<GoodsService> {

    @Override
    public GoodsService create(Throwable cause) {
        
        return new GoodsService() {
            @Override
            public GoodsInfo getGoods(Long id) {
            	// 根据业务情况自行设计实现细节,此处仅打印日志
                log.info("fallback method in getGoods.", cause);
                return null;
            }

            @Override
            public Map<Long, GoodsCart> getGoodsCart(Collection<Long> ids) {
                log.info("fallback method in getGoodsCart.", cause);
                return null;
            }
        };
    }
}

下面我们在@FeignClient 注解修饰的远程调用接口内使用我们实现的降级工厂:

@FeignClient(value = "coin-goods-svc", path = "/goods", fallbackFactory=GoodsServiceDowngradeFactory.class)
public interface GoodsService {
    // 获取指定商品
    @GetMapping("/getGoods")
    GoodsInfo getGoods(@RequestParam("id") Long id);
    
    // 获取整个购物车内的商品
    @GetMapping("/getBatch")
    Map<Long, GoodsCart> getGoodsCart(@RequestParam("ids") Collection<Long> ids);
}

注意我们在使用的时候,降级类和降级工厂同时只能选择其中一种。

6.2 开启降级配置

最后一步,开启降级的配置开关feign.circuitbreaker.enabled: true,使代码生效。即在 application.yml 文件中,原来的配置中加上:

# 其它忽略
feign:
  client:
    config:
      # 全局超时配置
      default:
        connectTimeout: 1000
        readTimeout: 3000
      # 针对coin-goods-svc服务的超时配置
      coin-goods-svc:
        connectTimeout: 1000
        readTimeout: 2000
  circuitbreaker:
    enabled: true

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【python】OpenCV—Tracking(10.1)

学习来自《Learning OpenCV 3 Computer Vision with Python》Second Edition by Joe Minichino and Joseph Howse 文章目录 检测移动的目标涉及到的 opencv 库cv2.GaussianBlurcv2.absdiffcv2.thresholdcv2.dilatecv2.getStructuringElementcv2.findContourscv2.contourAreacv2…

STM32 IIC电量计LTC2944

1 描述 LTC2944 可在便携式产品应用中测量电池充电状态、电池电压、电池电流及其自身温度。宽输入电压范围允许使用高达 60V 的多节电池。精密库仑反向积分电流通过电池正极端子与负载或充电器之间的检测电阻器。 电压、电流和温度由内部 14 位无延迟 ΔΣ™ ADC 测量。测量结…

09-信息收集-APP及其他资产等

信息收集-APP及其他资产等 信息收集-APP及其他资产等一、APP提取季抓包及后续配合1、某APK一键提取反编译2、利用bp抓取更多URL 二、某IP无web框架下的第三方测试1、各种端口一顿乱扫 —— 思路2、各种接口一顿乱扫 —— 思路3、接口部分一顿测试 —— 思路 三、**案例演示**1、…

KubeSphere 社区双周报|Fluent Bit 升级到 v2.2.2|2024.01.18-02.01

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2024.01.18-02.01…

新加坡大带宽服务器优势特点

随着互联网技术的不断进步&#xff0c;大带宽服务器在满足高速数据传输需求方面发挥着越来越重要的作用。新加坡&#xff0c;作为全球互联网基础设施的重要枢纽&#xff0c;其大带宽服务器在全球范围内备受关注。本文将深入探讨新加坡大带宽服务器的优势特点&#xff0c;以及如…

【Python基础】Numpy使用指南

文章目录 Numpy使用指南1 numpy简介2 numpy安装3 ndarray3.1 ndarry轴与秩3.2 ndarray 相关属性3.3 创建 ndarray 对象3.4 np.radom相关方法3.5 reshape方法3.6 ndarray对象转其他数据结构 4 numpy的数据类型5 numpy访问与修改5.1 一维array5.2 numpy中的轴 6 numpy计算6.1 基本…

Methodot低代码实战教程(一)——熟悉可视化Echart组件

一、产品介绍&#xff1a; Methodot是行云创新旗下一款面向研发使用的一站式云原生开发及应用托管平台&#xff0c;产品内有大量开箱即用的服务和开发工具&#xff0c;例如&#xff1a; 支持开发团队进行微服务架构设计&#xff08;例如一个袜子商店管理系统&#xff09;&…

病原菌共感染研究思路

近年来,多微生物共感染在临床上的报道日益增多。其中,多细菌共感染占据了细菌感染的25% ,其发病率和严重性也相应增加。尤其在形成生物膜后,这种共感染对疾病的发生、发展和临床治疗生重要影响,同时也给临床治疗带来了更大的挑战。 随着测序的发展&#xff0c;应用高通量转录组…

Java黑马——拼图小游戏

拼图小游戏&#xff08;GUI&#xff09; AWT包会有些兼容问题&#xff0c;不支持某些中文 在本次游戏的GUI开发中&#xff0c;我们将使用Swing包 一、主界面分析 这些东西统一称为组件&#xff0c;JFrame是一个组件、JMenuBar也是一个组件、等等 1、练习一&#xff1a;创建主…

离线生成双语字幕,一键生成中英双语字幕,基于AI大模型,ModelScope

离线生成双语字幕整合包,一键生成中英双语字幕,基于AI大模型 制作双语字幕的方案网上有很多&#xff0c;林林总总&#xff0c;不一而足。制作双语字幕的原理也极其简单&#xff0c;无非就是人声背景音分离、语音转文字、文字翻译&#xff0c;最后就是字幕文件的合并&#xff0c…

AI大模型开发架构设计(7)——人人都需要掌握的AI编程及应用案例实战

文章目录 人人都需要掌握的AI编程及应用案例实战1 AI代码生成模型与AI编程助手介绍程序设计方式的发展自动代码生成AI编程工具 2 AI编程助手的代码生成模型架构剖析以 CodeGeeX 为例-发展过程以 CodeGeeX 为例-训练过程以 CodeGeeX 为例-大规模代码数据处理以 CodeGeeX 为例-模…

消息中间件特性

一&#xff1a;消息队列的主要作用是什么&#xff1f; 1.消息队列的特性&#xff1a; 业务无关&#xff0c;一个具有普适性质的消息队列组件不需要考虑上层的业务模型&#xff0c;只做好消息的分发就可以了&#xff0c;上层业务的不同模块反而需要依赖消息队列所定义的规范进行…

Base64编码原理

Base64编码原理非常简单&#xff0c;首先确定好要编码的字符串&#xff0c;并查找其对应的 ASCII码将其转换为二进制表示&#xff0c;每三个8位的字节转换为四个6位的字节 &#xff08;384 624&#xff09;&#xff0c;把6位的最高位添两位数字0 &#xff0c;组成四个8位的字节…

内裤洗衣机有用吗?口碑好的小型洗衣机推荐

随着内衣洗衣机的流行&#xff0c;很多小伙伴在纠结该不该入手一款内衣洗衣机&#xff0c;专门来洗一些贴身衣物&#xff0c;答案是非常有必要的&#xff0c;因为我们现在市面上的大型洗衣机只能做清洁&#xff0c;无法对我们的贴身衣物进行一个高强度的清洁&#xff0c;而小小…

前端JavaScript篇之let、const、var的区别

目录 let、const、var的区别 let、const、var的区别 let、const和var是JavaScript中用于声明变量的关键字&#xff0c;它们之间有一些区别。 首先&#xff0c;var是在ES5中引入的关键字&#xff0c;而let和const是在ES6中引入的。在ES6之前&#xff0c;我们只能使用var来声明…

基于STM32F103C8T6最小系统板(对标某淘)

原理图和PCB都和某淘购买一样&#xff01;&#xff01;&#xff01; 原理图 PCB 3D图

炒股操作方法!南通怎么开股票账户佣金最低?炒股交易手续费最低?

炒股操作方法可以有很多种&#xff0c;以下是一些建议&#xff1a;这些是一些股票交易技巧&#xff0c;希望对你有帮助。请记住&#xff0c;投资有风险&#xff0c;决策请谨慎。 设定明确的投资目标&#xff1a;确定你的投资目标&#xff0c;是为了长期投资还是短期交易&#x…

BGP按组打包

按组打包技术将所有拥有共同出口策略的BGP邻居当作是一个打包组 每条待发送路由只被打包一次然后发给组内的所有邻居 RR1发给三个Client&#xff0c;需要发三份路由。 通过按组打包&#xff0c;可以将路由打包&#xff0c;一次发给所有组内的邻居&#xff08;前提是出口策略相…

【数据分享】1929-2023年全球站点的逐年最高气温数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01; 之前我们分享过1929-2023年全球气象站…

微服务-微服务Alibaba-Nacos 源码分析 (源码流程图)

客户端流程 客户端心跳与实例往服务端注册