Resilience4j 开启熔断保护后 Feign 调用时 Seata XID 未成功写入 Header

项目场景:

SpringCloud OpenFeign + Resilience4j + Seata 构建的微服务应用


问题描述

应用集成 SpringCloud OpenFeign 并使用 Resilience4j 开启熔断保护后,Seata 开启全局事务后,相关业务 Feign 调用时 Header 中未成功写入 XID
在这里插入图片描述

原因分析:

Seata XID 传播:
1、Seata 开启全局事务后会将 xid 写入执行当前业务的 Tomcat 线程的 ThreadLocal 中;
2、Seata 会实现 OpenFeign 的 RequestInterceptor 自动将当前线程的 ThreadLocal 中的 XID 写入本次 Feign 调用的 Header 中;

Resilience4j 熔断保护 Feign 调用:
1、Openfign 如果使用了 Resilience4j 熔断保护,默认采用线程池隔离模式,每次 Feign 调用都是用的 Resilience4j 的线程进行的,而非 Tomcat 的线程;
2、因为接入了 resilience4j 熔断保护现在 feign 调用使用了 resilience4j 的线程来执行,导致 seata 在使用 RootContext.getXID() 时值为 null,故无法成功将已开启的全局事务的 xid 添加到 feign header 中;


解决方案:

1、pom.xml 添加 resilience4j-bulkhead 依赖

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-bulkhead</artifactId>
</dependency>

2、应用中添加 SeataPropagator 配置类实现 resilience4j ContextPropagator 完成将 tomcat 线程中的 ThreadLocal 中的 xid 拷贝到 resilience4j 的线程中

package cn.xxx.common.transaction;

import io.github.resilience4j.core.ContextPropagator;
import io.seata.core.context.RootContext;
import org.jetbrains.annotations.NotNull;

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * seata xid 传播处理
 *
 * @author jxh
 * @version 1.0.0
 * @since 2023/11/15 16:18
 */
public class SeataPropagator implements ContextPropagator<String> {

    @NotNull
    @Override
    public Supplier<Optional<String>> retrieve() {
        return () -> Optional.ofNullable(RootContext.getXID());
    }

    @NotNull
    @Override
    public Consumer<Optional<String>> copy() {
        return s -> s.ifPresent(RootContext::bind);
    }

    @NotNull
    @Override
    public Consumer<Optional<String>> clear() {
        return s -> RootContext.unbind();
    }
}

3、应用配置文件添加 resilience4j 配置

resilience4j:
  thread-pool-bulkhead:
    configs:
      default:
        context-propagators:
          - cn.xxx.common.transaction.SeataPropagator

4、大功告成~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以通过在Feign客户端上添加Resilience4j的拦截器来实现熔断逻辑。下面是一些步骤来实现这个过程: 1. 首先,确保你已经添加了Resilience4j的依赖到你的项目中。你可以在Maven或者Gradle配置文件中添加以下依赖: ```xml <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-cloud2</artifactId> <version>1.6.1</version> </dependency> ``` 2. 创建一个实现Feign的RequestInterceptor接口的类,用于拦截Feign的请求。以下是一个示例: ```java import feign.RequestInterceptor; import feign.RequestTemplate; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Resilience4jFeignInterceptor implements RequestInterceptor { private final CircuitBreakerRegistry circuitBreakerRegistry; @Autowired public Resilience4jFeignInterceptor(CircuitBreakerRegistry circuitBreakerRegistry) { this.circuitBreakerRegistry = circuitBreakerRegistry; } @Override public void apply(RequestTemplate requestTemplate) { String serviceName = requestTemplate.feignTarget().name(); circuitBreakerRegistry.circuitBreaker(serviceName).executeRunnable(() -> { // 在这里执行你的Feign请求 requestTemplate.header("Authorization", "Bearer your-token"); }); } } ``` 3. 在你的Feign客户端接口上添加`configuration`属性,使用上述的拦截器类。例如: ```java import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "your-service-name", configuration = Resilience4jFeignInterceptor.class) public interface YourFeignClient { @GetMapping("/your-endpoint") String yourFeignMethod(); } ``` 这样,当你使用`YourFeignClient`接口的方法发送请求,请求将会通过Resilience4j的断路器进行拦截和熔断处理。 请注意,上面的示例中使用了`CircuitBreakerRegistry`来获取相应的断路器实例。你需要根据你的实际需求进行配置和使用Resilience4j的断路器。另外,你还可以根据需要添加其他的Resilience4j功能,如限流、重试等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值