• 使用springcloud-seata解决分布式事务问题-2PC模式


    目录

    一、建立undo_log表

    二、安装事务协调器:seata-server

    三、整合


    可以查看官网:快速启动 | Seata 

    一、建立undo_log表

    1. -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
    2. CREATE TABLE `undo_log` (
    3.  `id` bigint(20) NOT NULL AUTO_INCREMENT,
    4.  `branch_id` bigint(20) NOT NULL,
    5.  `xid` varchar(100) NOT NULL,
    6.  `context` varchar(128) NOT NULL,
    7.  `rollback_info` longblob NOT NULL,
    8.  `log_status` int(11) NOT NULL,
    9.  `log_created` datetime NOT NULL,
    10.  `log_modified` datetime NOT NULL,
    11.  `ext` varchar(100) DEFAULT NULL,
    12.  PRIMARY KEY (`id`),
    13.  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
    14. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

    二、安装事务协调器:seata-server

    三、整合

    (一) 导入依赖 spring-cloud-starter-alibaba-seata seata-all 0.7.1

    (二) 启动seata-server(TC)

    修改配置 registry.conf 修改seata的注册中心为nacos,(文件配置放在file.conf中,不修改)

    registry.conf文件修改后如下:

    1. registry {
    2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa seata的注册中心
    3. type = "nacos"  //修改的地方
    4. nacos {
    5. serverAddr = "localhost:8848"
    6. namespace = "public"
    7. cluster = "default"
    8. }
    9. eureka {
    10. serviceUrl = "http://localhost:1001/eureka"
    11. application = "default"
    12. weight = "1"
    13. }
    14. redis {
    15. serverAddr = "localhost:6379"
    16. db = "0"
    17. }
    18. zk {
    19. cluster = "default"
    20. serverAddr = "127.0.0.1:2181"
    21. session.timeout = 6000
    22. connect.timeout = 2000
    23. }
    24. consul {
    25. cluster = "default"
    26. serverAddr = "127.0.0.1:8500"
    27. }
    28. etcd3 {
    29. cluster = "default"
    30. serverAddr = "http://localhost:2379"
    31. }
    32. sofa {
    33. serverAddr = "127.0.0.1:9603"
    34. application = "default"
    35. region = "DEFAULT_ZONE"
    36. datacenter = "DefaultDataCenter"
    37. cluster = "default"
    38. group = "SEATA_GROUP"
    39. addressWaitTime = "3000"
    40. }
    41. file {
    42. name = "file.conf"
    43. }
    44. }
    45. config {
    46. # file、nacos 、apollo、zk、consul、etcd3
    47. type = "file"
    48. nacos {
    49. serverAddr = "localhost"
    50. namespace = "public"
    51. cluster = "default"
    52. }
    53. consul {
    54. serverAddr = "127.0.0.1:8500"
    55. }
    56. apollo {
    57. app.id = "seata-server"
    58. apollo.meta = "http://192.168.1.204:8801"
    59. }
    60. zk {
    61. serverAddr = "127.0.0.1:2181"
    62. session.timeout = 6000
    63. connect.timeout = 2000
    64. }
    65. etcd3 {
    66. serverAddr = "http://localhost:2379"
    67. }
    68. file {
    69. name = "file.conf"
    70. }
    71. }

     

    (三) 所有想要用到分布式事务的微服务使用seata DatasourceProxy代理自己的数据源

    1. @Autowired
    2. DataSourceProperties dataSourceProperties;
    3. @Bean
    4. public DataSource dataSource(DataSourceProperties properties) {
    5.    HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    6.    if (StringUtils.hasText(properties.getName())) {
    7.        dataSource.setPoolName(properties.getName());
    8.   }
    9.    return new DataSourceProxy(dataSource);
    10. }

    (四) 每个微服务,都必须导入(放resource下)

    register.conf和 file.conf(修改vgroup_mapping.{application.name}-fescar-service-group = "default")

    (五) 在分布式事务的入口方法上设置@GlobalTransactional(TM),在每一小事务上设置@Transactional(RM)

    远程调用的方法上面也要添加@Transactional

    1. @GlobalTransactional //seata分布式事务控制
    2. @Transactional
    3. @Override
    4. public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
    5. submitVoThreadLocal.set(vo);
    6. MemberResVo memberResVo = LoginUserInterceptor.loginUser.get();
    7. SubmitOrderResponseVo response = new SubmitOrderResponseVo();
    8. response.setCode(0);
    9. String redisToken = redisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResVo.getId());
    10. String orderToken = vo.getOrderToken();
    11. // 成功返回1 失败返回0
    12. String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    13. // 保证原子性
    14. Long result = redisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResVo.getId()), orderToken);
    15. if(result == 0L) {
    16. // 验证失败
    17. response.setCode(1);
    18. return response;
    19. } else {
    20. // 下单,创建订单,校验令牌,检验价格,锁库存
    21. // TODO 1、创建订单,订单项等信息
    22. OrderCreateTo order = createOrder();
    23. // TODO 2、验价
    24. BigDecimal payAmount = order.getOrder().getPayAmount();
    25. if(Math.abs(payAmount.subtract(vo.getPayPrice()).doubleValue()) < 0.01) {
    26. // 金额对比成功后保存订单
    27. // TODO 3、保存订单
    28. saveOrder(order);
    29. WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
    30. wareSkuLockVo.setOrderSn(order.getOrder().getOrderSn());
    31. List collect = order.getOrderItems().stream().map(item -> {
    32. OrderItemVo orderItemVo = new OrderItemVo();
    33. orderItemVo.setCount(item.getSkuQuantity());
    34. orderItemVo.setSkuId(item.getSkuId());
    35. orderItemVo.setTitle(item.getSkuName());
    36. return orderItemVo;
    37. }).collect(Collectors.toList());
    38. wareSkuLockVo.setLocks(collect);
    39. // TODO 4、锁库存
    40. // 出异常后,因为远程锁库存成功,但是忘了原因超时了,订单回滚,库存不回滚
    41. R r = wareFeignService.orderLockStock(wareSkuLockVo);
    42. if(r.getCode() == 0) {
    43. // 锁成功
    44. response.setOrder(order.getOrder());
    45. // TODO 5 出异常
    46. int i = 10/0;
    47. return response;
    48. } else {
    49. // 锁定失败
    50. // 抛异常才能使事务回滚
    51. response.setCode(3);
    52. throw new NoStockException((String)r.get("msg"));
    53. // return response;
    54. }
    55. } else {
    56. response.setCode(2); // 金额对比失败
    57. return response;
    58. }
    59. }

    注意:

    这种方法其实并不适用于下单等高并发场景。seata的AT模式适用于并发量不大的场景,比如说后台商家添加商品什么的,而不适用于高并发场景。因为锁太多导致串行,没法并发。

  • 相关阅读:
    多要素气象站:推动气象监测进入智能化新时代
    m4a怎么转换mp3?4个方法包教包会
    UE4_材质_湿度着色器及Desaturation算法_ben材质教程
    【毕业设计】基于SSM的进存销管理系统 - spring mvc java web
    力扣:166. 分数到小数(Python3)
    Linux之管道符
    【SwiftUI模块】0024、SwiftUI创建一个时尚的3D轮播滑块
    尼莫地平纳米脂质体包载小干扰RNA(siRNA)|低分子肝素纳米脂质体包载信使RNA(mRNA)|齐岳生物
    物料凭证过账时有用的BAdI和User exit
    虹科动态 | cippe2022即将举办,报名火热进行中
  • 原文地址:https://blog.csdn.net/m0_62946761/article/details/132941401