OpenFeign 如何使用?

OpenFeign 如何使用?

引入依赖

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

声明式实现

引入依赖之后,我们就可以使用OpenFeign了,其声明式实现如下:

@FeignClient(value = "user-service", configuration = FeignDecoderConfig.class)
public interface UserFeignClient {
​
    @GetMapping("/api/users/{username}")
    User getUserByUsername(@PathVariable("username") String username);
​
}

RestController 类似的。为什么返回值是 User 而不是 Result ,你可以看看我的这篇 Open Feign统一处理返回 文章。通过如上配置我们即可向我们的 user-service 服务发送请求。

当然,大多数情况下,我们会需要 token,即JWT令牌,我们 OpenFeign 如何携带 JWT令牌呢?

拦截器

我们需要设置一个请求拦截器。代码如下:

@Component
public class FeignTokenInterceptor implements RequestInterceptor {
​
    /**
     * 请求拦截器
     * @param template 请求模板
     */
    @Override
    public void apply(RequestTemplate template) {
        String token = TokenHolder.getToken(); // 从 ThreadLocal 或上下文中获取
​
//        if (token != null) {
//            template.header("Authorization", token.startsWith("Bearer ") ? token : "Bearer " + token);
//        }
        if (token != null) {
            template.header("token", token);
        }
    }
}

我用的是 Spring Security,我 JWT 过滤器效验 JWT令牌时,将 JWT 令牌放入了ThreadLocal 中,然后在 OpenFeign的拦截器中,从 ThreadLocal 中取出 token ,并将token 放入请求头中即可。

public class TokenHolder {
    private static final ThreadLocal<String> tokenThreadLocal = new ThreadLocal<>();
​
    public static void setToken(String token) {
        tokenThreadLocal.set(token);
    }
​
    public static String getToken() {
        return tokenThreadLocal.get();
    }
​
    public static void clear() {
        tokenThreadLocal.remove();
    }
}

这就大功告成了,因为 你设置了 @Component 标签,所以 OpenFeign 会自动将这个bean进行注入。之后你的所有 FeignClient 请求都会携带 token 啦。

但是其实在一些情况下有风险,因为你可能调用第三方API。如何调用第三方API呢?代码如下:

//'https://api.qweather.com/v7/weather/now?location=101010100'
@FeignClient(value = "weather-server", url = "https://api.qweather.com")
public interface WeatherFeignClient {
​
    
    @GetMapping("/v7/weather/now")
    String getCurrentWeather(
            @RequestHeader("X-QW-Api-Key")String apiKey,
            @RequestParam("location") String location);
​
//    @Test
//    void getWeather() {
//        String currentWeather = weatherFeignClient.getCurrentWeather(
//                "5c344f4278984bb6a134b6c99f1f2767", "101010100");
//        System.out.println(currentWeather);
//    }
}

上面我调用的是 和风天气 的第三方API,你为拦截器设置 @Component 标签 之后,那就是全局有效,包括调用第三方 API 的时候也有效,就可能造成 token 冲突了,因为你的系统token 和第三方api 的 token 肯定不一样,你是携带了自己的token还是 第三方api的token呢?

可能产生的冲突场景

场景结果问题
拦截器先执行,第三方手动后加Token请求头包含 两个Token第三方服务可能拒绝
拦截器后执行,覆盖手动添加的Token第三方收到 错误的Token认证失败
双方使用不同Header字段Authorization vs X-API-Key可能共存但混乱

所以建议进行 OpenFeign的配置。如下:

spring:
  cloud:
    openfeign:
      client:
        config:
          default:
            logger-level: full
            connect-timeout: 1000
            read-timeout: 2000
            request-interceptors:
              - com.dancos.interceptor.FeignTokenInterceptor
          #          为 远程调用为 user-service 的FeignClient 进行配置,该配置优先级大于默认配置
          user-service:
            connect-timeout: 3000
            read-timeout: 1000
            request-interceptors:
              - com.dancos.interceptor.FeignTokenInterceptor

上面不仅配置了 连接超时,读取超时,还配置了 request-interceptors 请求拦截器。

对于我们系统自己的业务,我们可以设置默认拦截器,对于第三方API的服务,我们就为其设置一个单独的啥也不干的拦截器。

其实最主要的是,如果 header 里面的字段一样,那就会出现是否会被覆盖的情况。

如果header里面的字段不一样,那其实影响不大。

兜底回调

我在使用兜底回调时遇到了问题,我的配置如下:

@Component
public class UserFeignClientFallback implements UserFeignClient {
​
    @Override
    public User getUserByUsername(String username) {
        System.out.println("兜底回调");
        return new User();
    }
}
@FeignClient(value = "user-service",
        fallback = UserFeignClientFallback.class,
        configuration = FeignDecoderConfig.class
)
public interface UserFeignClient {
​
    @GetMapping("/api/users/{username}")
    User getUserByUsername(@PathVariable("username") String username);
​
}
feign:
  sentinel:
    enabled: true

如果你要使用兜底回调,记得引入依赖:

<!--        sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

通过上面配置,无法实现兜底回调,一直失败。

原因在于我的配置类,如果你看过我的 这篇 Open Feign统一处理返回(我后续进行了修正),会发现我刚开始创建了一个这样的 bean

@Bean
public Feign.Builder decodeVoidFeignBuilder() {
    return Feign.builder().decodeVoid();
}

后续发现这样不可取,这就是导致一直兜底后调失败的罪魁祸首。为什么呢(来自于DeepSeek)?

1. 关键机制冲突
  • Feign.builder().decodeVoid() 创建了一个 全新的 Feign 构建器,它会:

    • 覆盖 Spring Cloud 自动配置的 Feign.Builder

    • 丢失 Sentinel 的装饰器(SentinelInvocationHandler

    • 导致所有通过该 Builder 创建的 Feign 客户端 失去熔断能力

2. Sentinel 集成原理

Spring Cloud Alibaba Sentinel 通过以下方式集成:

// 伪代码:Sentinel 的自动配置
@Bean
@ConditionalOnMissingBean // 依赖默认的 Feign.Builder
public Feign.Builder feignSentinelBuilder() {
    return SentinelFeign.builder(); // 关键:注入 Sentinel 逻辑
}

当您自定义 Feign.Builder 时:

  • @ConditionalOnMissingBean 条件不成立

  • Sentinel 的自动配置被跳过

3. decodeVoid() 的特殊性

该方法来自 feign-void 扩展库,会:

  • 修改默认的响应处理逻辑

  • 干扰 Spring Cloud 的异常转换链

  • 使 Sentinel 无法捕获原始异常

总结

其实还要重试机制,日志等等知识,但是我没写,嘿嘿嘿。

。。。

懒得写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值