分布式事务解决方案

释放双眼,带上耳机,听听看~!

分布式一致性协议

  • XA接口

XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或JMS队列)

  • JTA规范

作为java平台上事务规范JTA(Java Transaction API)也定义了对XA事务的支持. 目前JTA的实现主要由以下几种:

  1. J2EE容器所提供的JTA实现, 如JBoss
  2. 独立的JTA实现, 如JOTM,Atomikos.

二阶段提交协议

  • 含义
  1. 表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;
  2. 执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚。

分布式事务解决方案

  • 优点

尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于 MySQL 是从 5.5 开始支持。

  • 缺点
  1. 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机, 资源管理器就会一直阻塞,导致数据库无法使用。
  2. 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
  3. 数据不一致:比如在第二阶段中,假设协调者发出了事务 Commit 的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了 Commit 操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
  • 总结

二阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。

三阶段提交协议

  1. TCC
  • 含义

TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。

  • 特点

该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
该模式对有无本地事务控制都可以支持使用面广。
数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。

  1. 异步回调模式
  2. 最终一致性模式
  3. 可靠消息模式

代码实现(基于LCN框架4.1.2)

  • 架构图

分布式事务解决方案

  • 步骤
  1. 由于lcn暂时不支持SpringCloud2.0,因此我们需要把某位大牛改的lcn安装到本地库,打包时将maven-javadoc-plugin插件注掉, 调整redis配置,不然会报错。

  2. 在订单服务中集成lcn。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
1// 引入依赖
2<!-- https://mvnrepository.com/artifact/com.codingapi/transaction-springcloud -->
3       <dependency>
4           <groupId>com.codingapi</groupId>
5           <artifactId>transaction-springcloud</artifactId>
6           <version>4.1.2</version>
7           <exclusions>
8               <exclusion>
9                   <groupId>org.slf4j</groupId>
10                  <artifactId>*</artifactId>
11              </exclusion>
12          </exclusions>
13      </dependency>
14
15      <!-- https://mvnrepository.com/artifact/com.codingapi/tx-plugins-db -->
16      <dependency>
17          <groupId>com.codingapi</groupId>
18          <artifactId>tx-plugins-db</artifactId>
19          <version>4.1.2</version>
20          <exclusions>
21              <exclusion>
22                  <groupId>org.slf4j</groupId>
23                  <artifactId>*</artifactId>
24              </exclusion>
25          </exclusions>
26      </dependency>
27// 配置lcn
28tm:
29  manager:
30     url: http://lcn.zxkj.com/tx/manager/
31// 添加两个service
32@Service
33public class TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService{
34
35    @Override
36    public String httpGet(String url) {
37        System.out.println("httpGet-start");
38        String res = HttpUtils.get(url);
39        System.out.println("httpGet-end");
40        return res;
41    }
42
43    @Override
44    public String httpPost(String url, String params) {
45        System.out.println("httpPost-start");
46        String res = HttpUtils.post(url,params);
47        System.out.println("httpPost-end");
48        return res;
49    }
50}
51
52@Service
53public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService {
54
55  @Value("${tm.manager.url}")
56  private String url;
57
58  @Override
59  public String getTxUrl() {
60      System.out.println("load tm.manager.url ");
61      return url;
62  }
63}
64// 测试用例
65@RestController
66public class IOrderServiceImpl extends BaseApiService implements IOrderService {
67 
68  @Autowired
69  private OrderMapper orderMapper;
70 
71  @Autowired
72  private StockFeign stockFeign;
73
74  @TxTransaction(isStart = true)
75  @Transactional
76  @GetMapping(value = "/addOrderAndStock")
77  public ResponseBase addOrderAndStock(int i) throws Exception {
78      OrderEntity orderEntity = new OrderEntity();
79      orderEntity.setName("订单1");
80      orderEntity.setOrderCreatetime(new Date());
81      orderEntity.setOrderMoney(300d);
82      orderEntity.setOrderState(0);
83      Long commodityId = 30l;
84      orderEntity.setCommodityId(30l);
85      int orderResult = orderMapper.addOrder(orderEntity);
86      System.out.println("orderResult:" + orderResult);
87      if (orderResult <= 0) {
88          return setResultError("下单失败!");
89      }
90     
91      ResponseBase inventoryReduction = stockFeign.inventoryReduction(commodityId);
92      if (inventoryReduction.getRtnCode() != 200) {
93          throw new Exception("调用库存服务接口失败,开始回退订单事务代码");
94      }
95      int reuslt = 1 / i;
96      System.out.println("reuslt:" + reuslt);
97      return setResultSuccess("下单成功!");
98  }
99
100}
101
102
  1. 在库存服务中集成lcn, 同上。

  2. 配置nginx


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1    upstream  backServer{
2       server 127.0.0.1:8899;
3       server 127.0.0.1:8898;
4   }
5     server {
6        listen       80;
7        server_name  lcn.zxkj.com;
8        location / {
9            ### 指定上游服务器负载均衡服务器
10          proxy_pass http://backServer/;
11          ###nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间
12          proxy_connect_timeout 5s;
13          ###nginx发送给上游服务器(真实访问的服务器)超时时间
14            proxy_send_timeout 5s;
15          ### nginx接受上游服务器(真实访问的服务器)超时时间
16            proxy_read_timeout 5s;
17            index  index.html index.htm;
18        }
19  }
20
21
  1. 初始化测试脚本


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1CREATE TABLE `stock`  (
2  `id` int(11) NOT NULL AUTO_INCREMENT,
3  `commodity_id` int(11) DEFAULT NULL COMMENT '商品ID',
4  `stock` int(11) DEFAULT NULL COMMENT '库存余额',
5  PRIMARY KEY (`id`) USING BTREE
6) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
7
8CREATE TABLE `order`  (
9  `id` int(11) NOT NULL AUTO_INCREMENT,
10  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '订单名称',
11  `order_createtime` datetime(0) DEFAULT NULL COMMENT '下单时间',
12  `order_state` int(11) DEFAULT NULL COMMENT '订单状态 0 已经未支付 1已经支付 2已退单',
13  `order_money` double(10, 0) DEFAULT NULL COMMENT '订单价格',
14  `commodity_id` int(10) DEFAULT NULL COMMENT '商品ID',
15  PRIMARY KEY (`id`) USING BTREE
16) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
17
18
  1. 依次启动EurekaServer、两个TxManager、库存服务、订单服务.
  • 测试结果

分布式事务解决方案
分布式事务解决方案
分布式事务解决方案
分布式事务解决方案
分布式事务解决方案
分布式事务解决方案
分布式事务解决方案
分布式事务解决方案
分布式事务解决方案
分布式事务解决方案

给TA打赏
共{{data.count}}人
人已打赏
安全经验

英文站如何做Google Adsense

2021-10-11 16:36:11

安全经验

安全咨询服务

2022-1-12 14:11:49

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索