Seata entry series [2] Spring Cloud 2021.0.5 integrates seata 1.7.1

1 Lead to distributed transaction issues

1.1 seata-service-account is used to write interfaces for querying users and remotely calling orders.

@RestController
@RequestMapping("/accountTbl")
public class AccountTblController {
    
    

    @Autowired
    AccountTblMapper accountTblMapper;
    @Autowired
    OrderFeign orderFeign;


    @GetMapping("/insertOrder")
    public Object insertOrder() {
    
    
        // 查询用户
        AccountTbl accountTbl = accountTblMapper.selectById("11111111");
        // 下单
        Object order = orderFeign.insertOrder(accountTbl.getUserId(), "iphone11", 1, 1);
        // 修改余额
        accountTbl.setMoney(accountTbl.getMoney() - 1);
        accountTblMapper.updateById(accountTbl);
        return order;
    }
}

@FeignClient(name = "seata-service-order")
public interface OrderFeign {
    
    

    @GetMapping("orderTbl/insertOrder")
    Object insertOrder(@RequestParam String userId,@RequestParam  String commodityCode,@RequestParam  int count,@RequestParam  int money);
}

1.2 seata-service-order is written to place an order and remotely call the inventory reduction interface

@RestController
@RequestMapping("/orderTbl")
public class OrderTblController {
    
    
    @Autowired
    OrderTblMapper orderTblMapper;
    @Autowired
    StorageFeign storageFeign;

    @GetMapping("insertOrder")
    public Object insertOrder(String userId, String commodityCode, int count, int money) {
    
    
        // 下定单
        OrderTbl orderTbl = new OrderTbl();
        orderTbl.setUserId(userId);
        orderTbl.setCommodityCode(commodityCode);
        orderTbl.setCount(count);
        orderTbl.setMoney(1);
        // 下订单扣库存
        orderTblMapper.insert(orderTbl);
        Object storage = storageFeign.updateStorage(commodityCode, count);
        return orderTbl;
    }
}

@FeignClient(name = "seata-service-storage")
public interface StorageFeign {
    
    
    @GetMapping("/storageTbl/updateStorage")
    Object updateStorage(@RequestParam String commodityCode,@RequestParam  int count);
}

1.3 seata-service-storage writes inventory reduction interface

@RestController
@RequestMapping("/storageTbl")
public class StorageTblController {
    
    
    @Autowired
    StorageTblMapper storageTblMapper;

    @GetMapping("updateStorage")
    Object updateStorage(String commodityCode, int count){
    
    
        // 查询库存
        StorageTbl storageTbl = storageTblMapper.selectOne(new LambdaQueryWrapper<StorageTbl>().eq(StorageTbl::getCommodityCode, commodityCode));
        // 减去库存更新
        storageTbl.setCount(storageTbl.getCount()-count);
        int i = storageTblMapper.updateById(storageTbl);
        return storageTbl;
    }
}

1.4 Introducing distributed transactions

When we close the seata-service-storage service and access the /accountTbl/insertOrder interface, we will find that the order was successfully placed, but the inventory was not deducted, so we need to introduce distributed transactions to globally manage some cross-service database modification operations.

2 Integrate Seata to solve distributed transaction problems

2.1 Add undo_log table to each data data

CREATE TABLE `undo_log` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `branch_id` bigint NOT NULL,
  `xid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

2.2 Add seata dependency to parent pom

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.6.1</version>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba</groupId>
                    <artifactId>druid</artifactId>
                </exclusion>
            </exclusions>
        </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>

2.3 Add yml configuration to each module

seata:
  enabled: true
  application-id: seata-service-account # 添加为服务名
  tx-service-group: my_test_tx_group
  config:
    type: nacos
    nacos:
      namespace:
      serverAddr: 127.0.0.1:8848
      group: SEATA_GROUP
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace:

2.4 Add the @GlobalTransactional annotation to the AccountTblController interface at the beginning of the call chain

Insert image description here

2.5 Manually implement xid transfer

Manually implement xid transfer. In fact, cloud alibaba has implemented automatic transfer, but the new version of openfeign has removed many components. You need to implement it yourself, add configuration classes, and remove automatic configuration again.

/**
 * @author wuKeFan
 * @date 2020/11/27
 */
@Component
@ConditionalOnClass({
    
    RequestInterceptor.class, GlobalTransactional.class})
public class SeataRequestInterceptor implements RequestInterceptor {
    
    

    @Override
    public void apply(RequestTemplate template) {
    
    
        String currentXid = RootContext.getXID();
        if (StrUtil.isNotBlank(currentXid) && !template.url().startsWith(Auth.CHECK_TOKEN_URI) && !template.url().startsWith(Auth.CHECK_RBAC_URI)) {
    
    
            template.header(RootContext.KEY_XID, currentXid);
        }
    }
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(new SeataHandlerInterceptor()).addPathPatterns("/**");
    }
}
@SpringBootApplication(exclude = {
    
    SeataFeignClientAutoConfiguration.class})

2.6 Start each project, access the interface, and submit the global transaction successfully

Insert image description here

2.7 Simulating exceptions

Insert image description here
When the account is rolled back due to an exception, there are no order and inventory data inconsistencies, and the global transaction is rolled back.

Guess you like

Origin blog.csdn.net/qq_37284798/article/details/133339286