《企业实战分享 · Feign 使用合集》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍


写在前面的话

前不久博主整理了 《企业实战分享 · MyBatis 使用合集》,粉丝私信反响不错,今天继续整理它的姐妹篇。
博主所在公司的技术栈为:后端 SpringCloud + 前端 Vue/Nuxt,在后端开发中,内部服务间的调用采用了Feign来完成,这个和MyBatis一样属于企业开发的高频用法,因此也做一下知识汇总,希望与君共勉。


Feign 基础入门

技术简介
Feign 是 Spring Cloud 提供的声明式、模板化的 HTTP 客户端, 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
Spring Cloud 集成 Feign 并对其进行了增强,使 Feign 支持了 Spring MVC 注解;Feign 默认集成了 Ribbon,所以Fegin默认就实现了负载均衡的效果。
博主所在公司的新框架对内部远程调用做了一次升级,采用 OpenFeign 调用方式替代旧框架内部服务之间的 Dubbo 和 HttpUtils 调用方式。
OpenFeign 是一个声明式、模板化的 HTTP 客户端,可以帮助开发者更快捷、优雅地调用HTTP API。

使用前提

Tips:简单介绍使用Feign的一些前置步骤,这些其实都是框架搭建人员负责的,具体开发一般关心后续实质用法。

Step1、引入 Maven 依赖

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

Step2、在启动类添加@EnableFeignClients,或者为Feign接口添加@Component等注解,总之,需要被Spring框架识别为一个组件,以便Spring可以管理它。
Step3、在服务提供者定义一下 Controller,
Step4、在服务消费者定义Feign 客户端,并按需使用

基础用法
以门户调用医嘱的接口为例,实现 Feign 调用有两种方式:
1、服务消费方门户,自己开发对应的 Feign 接口,指向对方服务;
2、服务提供方医嘱, 在 api 模块书写对应的 Feign 接口,完成后发布私服,提供服务消费方门户调用;

Tips:两种方式各有优缺点,方案1可以减少不需要的接口和实体影响,改造期间减少对方服务影响;方案2可以减少重复编码,增加复用。原则上推荐在消费端书写 Feign 接口。

示例 - 服务提供方

@PostMapping(value="/extHandle")
ApiResult<String> extHandle(@RequestBody V2ShiftRecordPrintRo v2ShiftRecordPrintRo) throws Exception {
     //省去具体业务逻辑
}

示例 - Feign 接口

@FeignClient(value = FeignConstant.Service.PORTAL, url = FeignConstant.Url.PORTAL, path = "/inner/ext")
public interface PortalClient {

    @PostMapping(value="/extHandle")
    ApiResult<String> extHandle(@RequestBody V2ShiftRecordPrintRo v2ShiftRecordPrintRo);
}

示例 - 服务调用方

@Autowired
private PortalClient portalClient;

@PostMapping(value = "/feignTest")
public ResultVO<Map<String, Object>> feignTest(@RequestBody V2ShiftRecordPrintRo param) throws Exception {
    ApiResult<String> stringApiResult = portalClient.extHandle(param);
    return ResultVO.success("feign测试接口成功", param);
}

Feign 超时配置

Feign 客户端是默认开启支持 Ribbon 的,两个超时属性就是连接超时 ConnectTimeout 和读超时 ReadTimeout,在默认情况下,也就是没有任何配置下,Feign 的超时时间会被 Ribbon 覆盖,两个超时时间都是1秒。

ConnectTimeout 是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。
ReadTimeout 是建立连接后从服务端读取到可用资源的时间,它通常要考虑到服务端接口的耗时。

方案一:Ribbon全局配置
在调用方的yml配置文件中添加配置,设置超时时间为5s:

ribbon:
  #建立连接超时时间
  ConnectTimeout: 5000
  #建立连接之后,读取响应资源超时时间
  ReadTimeout: 5000

方案二:Feign配置

注: Feign配置会优先于Ribbon配置。配置完之后也是同样的效果。

feign:
  client:
    config:
      #这里填具体的服务名称(也可以填default,表示对所有服务生效)
      app-order:
        #connectTimeout和readTimeout这两个得一起配置才会生效
        connectTimeout: 5000
        readTimeout: 5000

目前公司框架,就网关有特别配置,各自项目自己配置,因为每个项目场景不太一样,这个让每个项目主管根据自己需要配置吧,架构部一般推荐接口访问要1秒以内,当然这是理想状态。

方案三:Feign配置具体接口
框架内部实现的自定义注解,允许通过添加此注解,按接口级别设置超时时间。
通过在对应的方法上添加 @RequestOption 注解来实现自定义超时配置(单位:秒),示例如下:

Tips:具体实现后续框架封装专栏再分享,这里先提供一个思路。

@FeignClient(contextId = "PatientClient", value = "demo-service", path = "/inner/patient")
public interface PatientClient {

    @GetMapping("/getById")
    @RequestOption(callTimeoutSeconds = 3)
    ApiResult<Patient> getById(@RequestParam String patientId);
}

Feign 常见问题

Feign POST not supported
Feign 调用的时候出现 Request method POST not supported的解决方案
背景说明:门户通过 Feign-Get 调用某个EMR接口,对方也是Get接口,但是却报错上面提示。

@GetMapping("/getAffiliateAccountByPrimaryAccount")
ApiResult<List<Map<String, Object>>> getAffiliateAccountByPrimaryAccount
    (@FeignBody("staffNo") String staffNo);

分析解决:排查发现是大概率注解使用不对,将@FeignBody修改为@RequestParam解决。
扩展说明:使用Feign来调用Get请求时,如果方法的参数是一个对象,则会被强行转变成Post请求,然后抛出服务被拒绝的错误,解决办法使用 @SpringQueryMap 注解。

@GetMapping("/search/page")
Page<User> pageSearchUser(@SpringQueryMap Page<User> page, @RequestParam String key);

关于Feign的入参有什么讲究
Feign接口通常有一个或者多个入参。
若接口有多个入参,应该将入参封装成实体参数,对字段有非空要求的,在实体内使用@NotNull、@NotEmpty等等注解,在接口中使用 @RequestBody 修饰实体。对于请求头的入参,直接添加在实体入参时候,用@RequestHeader修饰入参,无需放在实体内。除此之外,@RequestBody只支持修饰一个实体参数。
若接口只有一个入参,正常传参即可。

@FeignClient(contextId = "ManageAuthClient",value = OnelinkFeignConstant.Service.PORTAL, url = OnelinkFeignConstant.Url.PORTAL, path = "/inner/manage/auth")
public interface ManageAuthClient {

    @PostMapping("/addRoleAuthDefault")
    ApiResult<Void> addRoleAuthDefault(@RequestBody @Valid RoleAuthModel roleAuthModel,@RequestHeader("operator") String operator) throws Exception;
}

public class RoleAuthModel {

    @NotNull(message = "roleCode不能为空")
    //角色代码
    private String roleCode;

    @NotNull(message = "appCode不能为空")
    //系统代码
    private String appCode;

    //组件列表
    private List<Map<String, Object>> portletList;
}

如何打印Feign请求详细调试日志
在application.yml中配置日志级别与客户端包路径

feign:
  client:
    config:
      default:      
        # 日志级别为 full(该日志配置不能提交到线上环境)
        loggerLevel: full
logging:
  level:
    # 设置具体FeignClient的日志级别
    com.zoe.onelink.product.api.TrainProductClient: debug        

还有哪些常用配置:

feign:
  client:
    config:
      # 全局默认配置(若对以下配置不熟悉请勿随意配置)
      default:
        # 连接超时时间
        connect-timeout: 2000
        # 读取超时时间
        read-timeout: 3000
        # 自定义编码器
        encoder: feign.jackson.JacksonEncoder

ribbon:
  # 对当前节点的最大重试次数,不包括首次调用,默认值为0。
  MaxAutoRetries: 0
  # 下个节点数最大重试次数,不包括首个节点,默认值为1。不重试该值需要设置为-1 (0的话也不重试,但是会触发一次服务选举)
  MaxAutoRetriesNextServer: -1
  # 是否对所有请求进行失败重试, 设置为 false, 让feign只针对Get请求进行重试.
  OkToRetryOnAllOperations: false
  # true: 无论是接口请求超时、服务端处理失败、建立连接失败等,统一返回true,即可以重试
  okToRetryOnAllErrors: false
  # true: 只要是在跟服务端建立连接时出现错误,无论建立连接超时、建立连接失败等,统一返回true
  okToRetryOnConnectErrors: false

Feign 知识拓展

原理说明
1、将 Feign 接口的代理类扫描到Spring容器中:@EnableFeignClients 注解开启扫描,FeignClientsRegistrar 可以扫描被 @FeignClient 标识的接口,进而生成代理类,并把接口和代理类交给Spring的容器管理;
2、为接口的方法创建 RequestTemplate:当消费者调用 Feign 代理类时,代理类会通过调用SynchronousMethodHandler.invoke()创建 RequestTemplate 对象,进而代理类会通过 RequestTemplate 创建 Request,最后选择合适的请求客户端发出请求;

框架封装
博主所在公司框架对 Spring Cloud OpenFeign 做了二次封装,包含但不限于如下功能:
增加远程调用内部服务自动鉴权机制。
对远程调用返回结果进行了统一的格式封装。
对远程调用异常进行了统一的处理。
支持蓝绿、灰度流量、标签路由等功能。
注册中心实例上下线实时感知。
增强请求重试机制。

Tips:后续整理框架封装专栏再展开。

Feign拦截器
和其他技术一样,Feign 也可以通过自定义拦截器实现一些附加逻辑操作,这边是入门篇,不展开介绍,后续和上面其他框架内容一起展开。
关键词:RequestInterceptor


总结陈词

上文分享若干企业实际开发中,Feign的日常使用场景及应对方案,希望对大家有帮助。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

战神刘玉栋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值