399 Star 1.7K Fork 495

闲.大赋(李家智)/BeetlSQL

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

在springboot 框架下,微服务解决方法

  • Saga-server 负责记录回滚sql语句(以任务形式),并在需要的时候回滚。也可以定时或者UI界面触发

  • database 负责保存事务数据以及回滚任务

  • client,任何微服务客户端,通过kafka 发送事务数据。客户端在调用start时候,标记自己的事务,在commit或者rollback的时候发送回滚sql到saga server

  • 当微服务调用链中最初的那个事务要求回滚(画重点),则表示真正需要回滚,Saga—Server会发送回滚任务到各自系,进行真正回滚

  • Kafka 消息机制保证了回滚任务一定能被执行。

注意,采用客户端异步消息方式同saga-server 交互,问题是回滚可能带来延迟。即提示用户出错, 但数据当时还可能没有回滚.这依赖于Saga—Server和消息服务器的性能,本示例是秒级回滚,不影响体验

如果不懂Saga,可以参考 https://docs.microsoft.com/en-us/azure/architecture/reference-architectures/saga/saga,我认为最好的一篇说明Saga的的文章

  • start,发送全局事务gid+纳秒时间戳 到事务管理服务器,其负责检测是否有同样的事务id,如果有,则加入,如果没有,则创建

  • commit,保存所有rollback语句到服务器

  • rollback, 保存所有rollback语句到服务,并设置rollback标记给服务器, 服务器接收到rollback标记,标记事务失败。但并不立即回滚。直到最外层的事务rollback标记发回才开始开始回滚,取出所同一个gid的rollback语句,然后再发回到各个客户端,客户端依次执行,并反馈结果到saga-server。 服务器会判断如果所有成功执行rollback,则标记事务成功回滚 则发回失败。 服务器接收到失败后,,标记下次回滚时间。成功的则不需要标记

  • 服务器提供API查询事务和其所有回滚任务,以通过界面再次执行事务回滚或者特定的任务

关键设计:

  • Saga-Server通过Kafka接受回滚任务,考虑到消息机制,因此Saga—Server可以是水平扩展,保证其工作可靠性
  • 服务器和客户端通过消息队列,同样的gid,将顺序发送给同样的Saga—Server,以保证顺序。这里需要保证各个客户端的时间是一致的
  • 回滚任务由各个客户端生成,beetlsql的saga mapper会自动生成操作的逆向操作(不需要解析sql来生成逆向sql)

Saga-Server提供Swagger API用于查询和管理回滚任任务,如下是一个查询gid为3的所有回滚任务, 可以看到有3个回滚任务,userSystem,orderSystem和demoSystem,这三个系统,demoSytem的status是Error 表示触发了回滚,3个系统的rollbackStatus都是Success,表示回滚执行成功

{
  "success": true,
  "msg": "成功",
  "data": [
    {
      "id": "a717d181-608b-4f99-ab8a-9c60e83cce12",
      "gid": "123",
      "appName": "userSystem",
      "status": "Success",
      "rollbackStatus": "Success",
      "time": 1138055785025836,
      "taskInfo": "{\"tasks\":[{\"@Clazz\":\"org.beetl.sql.saga.ms.client.SagaClientTransaction$KafkaSagaTaskTrace\",\"rollbackTask\":{\"@Clazz\":\"org.beetl.sql.saga.common.ami.SagaUpdateByIdAMI$UpdateSagaRollbackTask\",\"sqlManagerName\":\"mySqlManager\",\"obj\":{\"@Clazz\":\"org.beetl.sql.saga.demo.entity.UserEntity\",\"id\":\"xiandafu\",\"name\":\"闲大赋\",\"balance\":4}},\"success\":false}],\"success\":true}",
      "createTime": 1607864163823,
      "updateTime": 1607864164067
    },
    {
      "id": "c4765a46-cf2f-4d7a-a714-dc35bf723df2",
      "gid": "123",
      "appName": "orderSystem",
      "status": "Success",
      "rollbackStatus": "Success",
      "time": 1138055465293352,
      "taskInfo": "{\"tasks\":[{\"@Clazz\":\"org.beetl.sql.saga.ms.client.SagaClientTransaction$KafkaSagaTaskTrace\",\"rollbackTask\":{\"@Clazz\":\"org.beetl.sql.saga.common.ami.SagaInsertAMI$InsertSagaRollbackTask\",\"sqlManagerName\":\"mySqlManager\",\"entityClass\":\"org.beetl.sql.saga.demo.entity.OrderEntity\",\"pkId\":\"4a40f48b-4b29-4b62-8d47-5f3867b03afd\"},\"success\":false}],\"success\":true}",
      "createTime": 1607864163570,
      "updateTime": 1607864164058
    },
    {
      "id": "48eb2fbe-9ff8-4913-bfd2-63176b3646b4",
      "gid": "123",
      "appName": "demoSystem",
      "status": "Error",
      "rollbackStatus": "Success",
      "time": 1138055258883126,
      "taskInfo": "{\"tasks\":[],\"success\":true}",
      "createTime": 1607864163543,
      "updateTime": 1607864164052
    }
  ],
  "errorCode": 0
}

todo

  • 要考虑Saga-Server会记录大量事务数据,甚至比业务数据还多。要考虑以gid的分库分表。或者定时清理已经完成的回滚或者是全部成功的事务,避免数据库过大
  • 回滚失败后(比如数据库宕机或者主从替换中),Saga-Server不再处理,等待人工调用api再次回滚。可以考虑其它机制或者接入其它系统,专门处理回滚也失败的事务。
  • 在回滚的时候,saga-server通过kafka发送回滚任务,这里不保证执行顺序,因此客户端需要考虑。比如俩个客户端同时修改一条记录。正向执行是有序的,但回滚不能保证顺序 虽然saga-server能根据时间顺序发送回滚任务,并等待回滚成功再发送下一个,但代价太大。很容易因为一个客户端的回滚任务执行失败,导致其他任务不能执行
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Java
1
https://gitee.com/xiandafu/beetlsql.git
git@gitee.com:xiandafu/beetlsql.git
xiandafu
beetlsql
BeetlSQL
master

搜索帮助