上一个项目被疫情打断,无限期延后,公司也解散了。于是自己在网上又找了一个类似的电商项目,试图按之前二开的需求改造成spring cloud微服务架构。今天遇到事务的思考,因此记录一下想法。

由于运营商的后台系统、商家的后台系统、订单系统甚至是搜索系统都会不同程度地需要对某些实体的操作,于是先将商品相关的增删改查都抽取到一个工程,通过rest对外提供微服务。至于微服务的粒度问题,考虑到通信的开销和上线之后的实际承受能力,开始还是不宜拆的太细,能尽量放一起的就尽量放一起。其他工程谁要调这个微服务,按照provider的controller写一个接口(一般叫XxxClient),注入到consumer的controller中,通过Feign来调用即可。一般consumer接到请求,可以直接调这个XxxClient完成,使用起来感觉很类似本地的service bean,于是有几个单实体的操作我干脆没有写service层。

但如果遇到分表或者是关联查询的情况,不写service层可能就会有问题,同时也粗略的感受到分布式架构中的事务难题。在我的provider的工程中,为了提供最大的可重用性,开始我的想法是所有的实体都提供一套独立的crud服务,但这样一来,如果consumer需要用关联查询的结果来响应给页面,那关联的代码就被写到了consumer的controller方法中,因为consumer它自己没有service和dao,服务全部来自提供者。这里首先就冒出来多个事务的问题,由于这些事务是平行的,因此Spring的事务传播行为在这里不生效。

初始解决的想法有三个: ①让远端的微服务provider去做好关联,然后提供rest api给consumer。但这样一来,不同的consumer需求千奇百怪,会让provider编码的压力很大。②把事务注解加在consumer的控制器上或者目标方法上,虽然spring手册对此说明是可行,但还是感觉怪怪的,有点破坏整体代码的层次感。③为consumer写service层,让XxxClient只担任原来的Dao的角色,为service加事务控制。但转念一想,后两种办法根本就是错误的,因为本地的事务控制是需要借助于关系型数据库的事务管理,而作为consumer可能都没有连接数据库,并不是用一个@transaction注解就自动生成了一个大事务包住了这些小事务,保证了原子性。事实上这些来自不同provider的事务还是各玩各的,无法保证ACID。于是就牵扯出分布式事务的世界难题。

目前解决分布式事务的方案主要有:

  • 两阶段提交协议2PC,Two Phase Commitment Protocol。
  • 事务补偿TCC,Try+Confirm+Cancel。
  • 使用消息队列实现事务最终一致性。

每种方案都各有利弊,需要根据实际业务需求权衡选择,具体的介绍可以参看网上的资料。
放一个介绍比较全面的链接,https://blog.csdn.net/pbrlovejava/article/details/105317145

 posted on 2020-06-10 19:12  银色甲壳虫  阅读(266)  评论(0编辑  收藏  举报