一、冲突根源
⚠️ 关键区分:
- 限流规则(FlowRule):抛出BlockException,异常会向上抛出,不会干扰Seata事务,可以正常共存;
- 熔断降级规则(DegradeRule)+ Feign Fallback:吞异常,是唯一冲突点。
二、五大落地解决方案
方案1:分层开关隔离
生产标准首选,零侵入
核心思路:全局开启Sentinel限流能力,仅对@GlobalTransactional包裹的事务链路,单独关闭Feign Sentinel自动熔断;非事务查询接口保留熔断降级。
1)全局配置
开启限流,默认关闭Feign熔断
# 全局启用Sentinel
spring.cloud.sentinel.dashboard: 127.0.0.1:8080
# 默认全局关闭Feign sentinel降级
feign.sentinel.enabled: false
此时所有Feign调用不会触发Fallback降级,但接口QPS、热点限流规则依然正常生效。
2)非事务查询接口单独开启降级(局部开启)
不修改全局配置,单独给查询类Feign客户端指定Sentinel降级工厂:
@FeignClient(value = "goods-service",
fallbackFactory = GoodsFeignFallbackFactory.class,
configuration = FeignSentinelConfig.class)
public interface GoodsFeignClient {
// 纯查询接口,无分布式事务,允许降级
}
配套配置类单独开启该Feign客户端的Sentinel熔断,互不影响事务链路。
优势
- 写事务链路绝对安全,Seata回滚不受任何干扰;
- 查询接口正常享受熔断降级高可用保护;
- 无需改动业务代码,纯配置隔离,运维清晰。
方案2:事务内Feign降级后主动抛异常
无法全局关闭降级时,在Fallback降级方法里主动抛出业务运行时异常,让TM捕获异常触发全局回滚。
局限:下游RM根本没执行SQL,无分支事务,Seata只能回滚上游已执行本地事务,下游无任何操作,不会造成脏数据,但无法重试恢复下游服务。
1)Feign降级工厂代码
@Component
public class StockFeignFallbackFactory implements FallbackFactory<StockFeignClient> {
@Override
public StockFeignClient create(Throwable cause) {
return new StockFeignClient() {
@Override
public void deduct(Long goodsId, Integer num) {
log.error("库存服务远程调用熔断降级", cause);
// 主动抛出运行时异常,向上传递给TM
throw new RuntimeException("下游库存服务不可用,事务回滚");
}
};
}
}
2)TM全局事务注解
@GlobalTransactional(rollbackFor = Exception.class)
public void createOrder() {
insertOrder();
stockFeignClient.deduct(1L,10);
}
降级抛出异常 → TM感知 → 全局事务回滚订单插入操作。
适用场景
老项目已经全局开启feign.sentinel.enabled=true,不方便整体回滚,临时兼容改造。
方案3:返回值判断+手动触发Seata回滚
降级方法返回标记结果(布尔/枚举),TM收到降级标识后,手动调用Seata API强制回滚全局事务。
1)Feign接口定义返回值
public interface StockFeignClient {
// true:扣减成功;false:降级/调用失败
Boolean deduct(Long goodsId, Integer num);
}
2)降级方法返回失败标识
@Override
public Boolean deduct(Long goodsId, Integer num) {
log.warn("库存服务熔断降级");
return false;
}
3)TM业务代码主动判断并回滚
@GlobalTransactional
public void createOrder() {
insertOrder();
Boolean res = stockFeignClient.deduct(1L, 10);
if (!res) {
// 手动触发全局事务回滚
String xid = RootContext.getXID();
GlobalTransactionContext.reload(xid).rollback();
throw new RuntimeException("下游服务降级,事务已回滚");
}
}
方案4:线程隔离策略改造
禁止Sentinel线程池隔离
Sentinel两种隔离模式:
事务内Feign调用的Sentinel规则,强制设置隔离模式为信号量隔离,避免线程切换丢失XID。
⚠️ 该方案仅保证上下文不丢失,依然存在降级吞异常问题,必须搭配方案2一起使用,不单独推荐。
方案5:架构拆分根治
不使用Feign同步调用,改用RocketMQ本地消息表+可靠消息最终一致性异步解耦;
事务提交成功后发送MQ,库存、余额服务消费消息异步执行扣减,彻底规避同步Feign熔断冲突。
保留Feign + Sentinel熔断降级,本地缓存兜底,提升可用性。
三、配套边界约束细则
1)限流规则完全兼容,无需改动
Sentinel流控、热点参数限流触发时抛出BlockException,异常会向上抛出,Seata正常捕获回滚,可以放心共存,仅熔断降级需要隔离。
2)全局异常处理器注意事项
@RestControllerAdvice不能吞掉BlockException、业务运行时异常,否则Seata依然感知不到异常,事务无法回滚。
3)Nacos持久化规则拆分
Sentinel规则持久化到Nacos时,分开维护两套规则:
- 事务写接口:仅配置流控规则,不配置降级规则;
- 查询读接口:流控+降级规则完整配置。
四、最终落地选型建议
架构干净、无业务代码侵入、事务安全性最高,阿里微服务生产标准实践;
改动量小,只修改Fallback降级类,快速修复事务不一致问题;
直接采用方案5消息异步化拆分,从架构层面彻底消除同步调用冲突。
五、高频踩坑
降级时远程Feign根本没有发起HTTP请求,下游RM没有执行SQL、没有注册分支事务,只能回滚上游TM本地事务,下游无操作,不会产生脏数据,但无法重试恢复下游服务。
只有DegradeRule熔断降级会吞异常冲突,FlowRule限流无任何冲突,可正常使用。
仅保留XID上下文,无法解决降级吞异常问题,必须额外抛出异常触发回滚。




