Feign calls the service, and the called service seata transaction is not opened or the xid is empty

Current scenario: The app interface service calls service A and service B. The app interface will be rolled back, and the A service and the B service will not be rolled back.

App interface

    @GlobalTransactional(rollbackFor = Exception.class)
    public Result uploadFile(SaveLogDataReq req) {
        System.out.println("seata全局事务id====================>"+ RootContext.getXID());
        
        servieFeignA.addAAA(req);
        servieFeignB.addBBB(req);
        
        int i = 1/0;
        
        return Resu.ok();
    }

A service

   
    public Result addAAA(SaveLogDataReq req) {
        System.out.println("seata全局事务id====================>"+ RootContext.getXID());
        
        //todo AAA代码逻辑......
        
        return Resu.ok();
    }

 B service

 
    public Result addBBB(SaveLogDataReq req) {
        System.out.println("seata全局事务id====================>"+ RootContext.getXID());
        
        //todo BBB代码逻辑......
        
        return Resu.ok();
    }

Analysis: Viewing the print log shows that the xids of service A and service B are empty, or inconsistent with the xid of the APP interface service.

solution:

Solution 1, pass the current xid into the header when fein is called remotely

1. Add request interceptor


public class MultipartSupportConfig implements RequestInterceptor {

    /**
     * 解决服务直接调用请求头不传递的问题
     * @param template
     */
    @Override
    public void apply(RequestTemplate template) {
        //解决不传递请求头中的token
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null){
            HttpServletRequest request = attributes.getRequest();
            Enumeration<String> headerNames = request.getHeaderNames();
            //可以在这里将自定义请求头传递进去, key 请求, value 值
            //处理上游请求头信息,传递时继续携带
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);
                template.header(name, values);
            }
        }

        // 解决seata的xid未传递
        String xid = RootContext.getXID();
        template.header(RootContext.KEY_XID, xid);
    }

}

2. Add the above request interceptor (configuration = MultipartSupportConfig.class) configuration to the fein annotation that calls the A/B service.

@FeignClient(contextId="LogDataService",value = "LogDataService",configuration = MultipartSupportConfig.class)
public interface ServieFeignA {

    @PostMapping(value = "/api/logdata/v1/log/save",consumes = MediaType.APPLICATION_JSON_VALUE)
    public Result<String> save(@RequestBody SaveLogDataReq req);

}

This solution is more cumbersome and is not a priority.

Solution 2, use spring-cloud-starter-alibaba-seata automatic configuration (recommended)

The seata-spring-boot-starter configuration was used before, and the seata automatic configuration here will not process feign, so the fein call will not pass the xid, resulting in the global transaction not taking effect.

		<dependency>
			<groupId>io.seata</groupId>
			<artifactId>seata-spring-boot-starter</artifactId>
			<version>1.4.2</version>
		</dependency>

So remove this maven configuration and use spring-cloud-starter-alibaba-seata instead.

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

Here, fein will be configured related to seata.

 

Notice:

1. The exception needs to be thrown up layer by layer. If you handle the exception in the sub-service (such as GlobalExceptionHandler), seata will think that you have manually handled the exception.

2. In the case of transaction failure, check RootContext.getXID() first, whether the xid is passed and consistent.

3. The main service can be annotated with @GlobalTransactional, and the called service does not need to add @GlobalTransactional and @Transactional.

4. @GlobalTransactional(rollbackFor = Exception.class) is best to add rollbackFor = Exception.class, which means that Exception will be rolled back, otherwise some exceptions (such as custom exceptions) will not be rolled back.

Guess you like

Origin blog.csdn.net/zdb1314/article/details/124211553