webflux使用seata失效解决

目录

项目初始配置:

项目模块:

order模块关键代码

goods模块关键代码 

依赖:

seata版本:

子项目pom:

父pom:

概述:

场景复现:

排查思路:

解决方案:

结果:

场景用例:


项目初始配置:

项目模块:

                订单模块order、商品模块goods

order模块关键代码

goods模块关键代码 

依赖:

seata版本:

        seata-server-1.6.1

子项目pom:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.seata</groupId>
                    <artifactId>seata-spring-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

父pom:


    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>2.3.2.RELEASE</spring-boot.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>
    </properties>


    <dependencyManagement>
        <dependencies>
            <!-- SpringBoot 依赖配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>1.4.2</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
  • 概述:

由请求关键字,触发异常(可由关键字确定是goods\order中心抛出异常);

测试请求:由订单中心抛出异常,看是否商品中心能回滚成功(说明:商品远程事务先执行成功

场景复现

由postman发起请求后,订单中心抛出异常。order表无数据但goods-chang-log表有数据,不一致

排查思路:

seata的分布式事务,在一个业务请求连路中,多个分布式业务模块共用一个事务id,即TX_XID,和全局数据一致性相关。

添加日志后,发现到商品中心的请求头有TX_XID,但seata上下文RootContext中getXID()为null。

所以初步思路,在进入子模块前,获取请求中的TX_XID,并设置到RootContext该上下文中

解决方案:

import io.seata.common.util.StringUtils;
import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.stereotype.Component;

@Component
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class MyFilter implements WebFilter {

    private static final Logger log = LoggerFactory.getLogger(MyFilter.class);

   /**
     * 处理过程参照其servlet实现:
     * @see com.alibaba.cloud.seata.web.SeataHandlerInterceptor
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String xid = RootContext.getXID();
        String rpcXid = exchange.getRequest().getHeaders().getFirst(RootContext.KEY_XID);
        if (log.isDebugEnabled()) {
            log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
        }
        if (StringUtils.isBlank(xid) && rpcXid != null) {
            RootContext.bind(rpcXid);
            if (log.isDebugEnabled()) {
                log.debug("bind {} to RootContext", rpcXid);
            }
        }
        Mono<Void> mono = chain.filter(exchange);
        return mono.then(Mono.defer(() -> {
            if (StringUtils.isNotBlank(RootContext.getXID())) {
                if (StringUtils.isNotEmpty(rpcXid)) {
                    String unbindXid = RootContext.unbind();
                    if (log.isDebugEnabled()) {
                        log.debug("unbind {} from RootContext", unbindXid);
                    }
                    if (!rpcXid.equalsIgnoreCase(unbindXid)) {
                        log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
                        if (unbindXid != null) {
                            RootContext.bind(unbindXid);
                            log.warn("bind {} back to RootContext", unbindXid);
                        }
                    }
                }
            }
            return Mono.empty();
        }));

    }
}

结果:

商品中心成功获取到XID(debug时,未执行bind方法,getXID()的值完全由MyFilter设置而来)

出现回滚日志 

数据一致(用例前,已清空两表)

场景用例

  •  正常调用:其它正常接口不受影响

  • 异常调用:异常信息留存正常

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值