范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

GO编程DTM(二)

  二阶段消息例子
  本文将介绍一个完整的二阶段消息例子,让读者对二阶段消息型事务有一个准确的了解 业务场景
  跨行转账是典型的分布式事务场景,在这里,A需要跨行转账给B,假设需求场景是:只有转出A可能失败,转入B是能够最终成功的 二阶段消息
  二阶段消息是dtm首创的事务模式,用于替换本地事务表和事务消息这两种现有的方案。它能够保证本地事务的提交和全局事务提交是"原子的",适合解决不需要回滚的分布式事务场景。下面我们来看看二阶段消息,如何解决这个业务场景的问题。 核心业务
  首先我们创建账户余额表: CREATE TABLE dtm_busi.`user_account` (   `id` int(11) AUTO_INCREMENT PRIMARY KEY,   `user_id` int(11) not NULL UNIQUE ,   `balance` decimal(10,2) NOT NULL DEFAULT "0.00",   `trading_balance` decimal(10,2) NOT NULL DEFAULT "0.00",   `create_time` datetime DEFAULT now(),   `update_time` datetime DEFAULT now() );
  然后编写核心业务代码,调整用户的账户余额 func SagaAdjustBalance(db dtmcli.DB, uid int, amount int, result string) error {     _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)     return err }
  再来编写具体的处理函数 app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   barrier := MustBarrierFromGin(c)   return barrier.Call(txGet(), func(tx *sql.Tx) error {     return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, "")   }) }))
  这些处理函数的核心逻辑都是是调整余额。这里面的 barrier.Call  主要是用于处理幂等,保证重复调用不会多次调整余额,详情参见异常与子事务屏障二阶段消息事务
  到此各个子事务的处理函数已经OK了,然后是开启二阶段消息事务,进行分支调用         msg := dtmcli.NewMsg(DtmServer, shortuuid.New()).             Add(busi.Busi+"/SagaBTransIn", &TransReq{ Amount: 30 })         err := msg.DoAndSubmitDB(busi.Busi+"/QueryPreparedB", dbGet(), func(tx *sql.Tx) error {             return busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount)         })
  这段代码中,会保证 DoAndSubmitDB 中的业务提交和全局事务提交是"原子的",保证了TransOut和TransIn的同时成功,或同时失败。其中 DoAndSubmitDB 中的第一个参数为回查URL,他的代码如下: app.GET(BusiAPI+"/QueryPreparedB", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   return bb.QueryPrepared(dbGet()) }))
  至此,一个完整的二阶段消息分布式事务编写完成。 运行
  如果您想要完整运行一个成功的示例,步骤如下: 运行dtm git clone https://github.com/dtm-labs/dtm && cd dtm go run main.go运行例子 git clone https://github.com/dtm-labs/dtm-examples && cd dtm-examples go run main.go http_msg_doAndCommit如何保证原子性
  二阶段消息如何保证本地事务和全局事务要么都成功,要么都失败呢?假定本地事务提交完成后,提交全局事务前,进程crash会如何?下面时序图很好的讲解了二阶段消息是如何处理这个问题的:
  图中的回查处理逻辑,dtm已经做了自动处理,用户只需要粘贴上述的代码即可 SAGA 例子
  本文将介绍一个完整的 SAGA 例子,让读者对 SAGA 型事务有一个准确的了解 业务场景
  跨行转账是典型的分布式事务场景,在这里,A需要跨行转账给B,假设需求场景是:转出A和转入B都有可能成功和失败,需要最终转入转出都成功,或者都失败 SAGA
  Saga是这一篇数据库论文SAGAS提到的一个分布式事务方案。其核心思想是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果各个本地事务成功完成那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。 核心业务
  对于我们要进行的银行转账的例子,我们将在正向操作中,进行转入转出,在补偿操作中,做相反的调整。
  首先我们创建账户余额表: CREATE TABLE dtm_busi.`user_account` (   `id` int(11) AUTO_INCREMENT PRIMARY KEY,   `user_id` int(11) not NULL UNIQUE ,   `balance` decimal(10,2) NOT NULL DEFAULT "0.00",   `trading_balance` decimal(10,2) NOT NULL DEFAULT "0.00",   `create_time` datetime DEFAULT now(),   `update_time` datetime DEFAULT now() );
  然后编写核心业务代码,调整用户的账户余额 func SagaAdjustBalance(db dtmcli.DB, uid int, amount int, result string) error {     _, err := dtmimp.DBExec(db, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)     return err }
  再来编写具体的正向操作/补偿操作的处理函数 app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   barrier := MustBarrierFromGin(c)   return barrier.Call(txGet(), func(tx *sql.Tx) error {     return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, "")   }) })) app.POST(BusiAPI+"/SagaBTransInCom", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   barrier := MustBarrierFromGin(c)   return barrier.Call(txGet(), func(tx *sql.Tx) error {     return SagaAdjustBalance(tx, TransInUID, -reqFrom(c).Amount, "")   }) })) app.POST(BusiAPI+"/SagaBTransOut", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   barrier := MustBarrierFromGin(c)   return barrier.Call(txGet(), func(tx *sql.Tx) error {     return SagaAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount, "")   }) })) app.POST(BusiAPI+"/SagaBTransOutCom", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   barrier := MustBarrierFromGin(c)   return barrier.Call(txGet(), func(tx *sql.Tx) error {     return SagaAdjustBalance(tx, TransOutUID, reqFrom(c).Amount, "")   }) }))
  这些处理函数的核心逻辑都是是调整余额,对于这里面的 barrier.Call  作用,后面会详细解释SAGA 事务
  到此各个子事务的处理函数已经OK了,然后是开启SAGA事务,进行分支调用     req := &gin.H{"amount": 30} // 微服务的载荷     // DtmServer为DTM服务的地址     saga := dtmcli.NewSaga(DtmServer, shortuuid.New()).         // 添加一个TransOut的子事务,正向操作为url: qsBusi+"/TransOut", 逆向操作为url: qsBusi+"/TransOutCom"         Add(qsBusi+"/SagaBTransOut", qsBusi+"/SagaBTransOutCom", req).         // 添加一个TransIn的子事务,正向操作为url: qsBusi+"/TransOut", 逆向操作为url: qsBusi+"/TransInCom"         Add(qsBusi+"/SagaBTransIn", qsBusi+"/SagaBTransInCom", req)     // 提交saga事务,dtm会完成所有的子事务/回滚所有的子事务     err := saga.Submit()
  至此,一个完整的SAGA分布式事务编写完成。 运行
  如果您想要完整运行一个成功的示例,步骤如下: 运行dtm git clone https://github.com/dtm-labs/dtm && cd dtm go run main.go运行例子 git clone https://github.com/dtm-labs/dtm-examples && cd dtm-examples go run main.go http_saga_barrier
  时序图如下:
  处理网络异常
  假设提交给dtm的事务中,调用转入操作时,出现短暂的故障怎么办?dtm 会重试未完成的操作,此时就会要求全局事务中的各个子事务是幂等的。dtm 框架首创子事务屏障技术,提供 BranchBarrier 工具类,可以帮助用户简单的处理幂等。它提供了一个函数 Call ,保证这个函数内部的业务,会被最多调用一次: func (bb *BranchBarrier) Call(tx *sql.Tx, busiCall BarrierBusiFunc) error
  该 BranchBarrier 不仅能够自动处理幂等,还能够自动处理空补偿、悬挂的问题,详情可以参考异常与子事务屏障 处理回滚
  假如银行将金额准备转入用户2时,发现用户2的账户异常,返回失败,会怎么样?我们调整处理函数,让转入操作返回失败 app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   return dtmcli.ErrFailure }))
  我们给出事务失败交互的时序图
  这里有一点,TransIn的正向操作什么都没有做,就返回了失败,此时调用TransIn的补偿操作,会不会导致反向调整出错了呢?
  不用担心,前面的子事务屏障技术,能够保证TransIn的错误如果发生在提交之前,则补偿为空操作;TransIn的错误如果发生在提交之后,则补偿操作会将数据提交一次。
  您可以将返回错误的TransIn改成: app.POST(BusiAPI+"/SagaBTransIn", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   barrier := MustBarrierFromGin(c)   barrier.Call(txGet(), func(tx *sql.Tx) error {     return SagaAdjustBalance(tx, TransInUID, reqFrom(c).Amount, "")   })   return dtmcli.ErrFailure }))
  最后的结果余额依旧会是对的,详情可以参考异常与子事务屏障 TCC 例子
  本文将介绍一个完整的 TCC 例子,让读者对 TCC 型事务有一个准确的了解 业务场景
  跨行转账是典型的分布式事务场景,在这里,A需要跨行转账给B,假设需求场景是:转出A和转入B都有可能成功和失败,需要最终转入转出都成功,或者都失败。
  同时这里还有一个要求,假如发生回滚,SAGA 模式下会发生A发现自己的余额被扣减了,但是收款方B迟迟没有收到余额,那么会对A造成很大的困扰。业务上面希望不要出现这种情况 TCC组成
  TCC分为3个阶段 Try 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性) Confirm 阶段:如果所有分支的Try都成功了,则走到Confirm阶段。Confirm真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源 Cancel 阶段:如果所有分支的Try有一个失败了,则走到Cancel阶段。Cancel释放 Try 阶段预留的业务资源。
  如果我们要进行一个类似于银行跨行转账的业务,转出(TransOut)和转入(TransIn)分别在不同的微服务里,一个成功完成的TCC事务典型的时序图如下:
  核心业务
  首先我们创建账户余额表,其中 trading_balance 表示被冻结的金额: create table if not exists dtm_busi.user_account(   id int(11) PRIMARY KEY AUTO_INCREMENT,   user_id int(11) UNIQUE,   balance DECIMAL(10, 2) not null default "0",   trading_balance DECIMAL(10, 2) not null default "0",   create_time datetime DEFAULT now(),   update_time datetime DEFAULT now(),   key(create_time),   key(update_time) );
  我们先编写核心代码,冻结/解冻资金操作,会检查约束balance+trading_balance >= 0,如果约束不成立,执行失败 func tccAdjustTrading(db dtmcli.DB, uid int, amount int) error {     affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account         set trading_balance=trading_balance+?         where user_id=? and trading_balance + ? + balance >= 0`, amount, uid, amount)     if err == nil && affected == 0 {         return fmt.Errorf("update error, maybe balance not enough")     }     return err }  func tccAdjustBalance(db dtmcli.DB, uid int, amount int) error {     affected, err := dtmimp.DBExec(db, `update dtm_busi.user_account         set trading_balance=trading_balance-?,         balance=balance+? where user_id=?`, amount, amount, uid)     if err == nil && affected == 0 {         return fmt.Errorf("update user_account 0 rows")     }     return err }
  下面我们来编写具体的Try/Confirm/Cancel的处理函数 app.POST(BusiAPI+"/TccBTransOutTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   return bb.Call(txGet(), func(tx *sql.Tx) error {     return tccAdjustTrading(tx, TransOutUID, -req.Amount)   }) })) app.POST(BusiAPI+"/TccBTransOutConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   return bb.Call(txGet(), func(tx *sql.Tx) error {     return tccAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount)   }) })) app.POST(BusiAPI+"/TccBTransOutCancel", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   return bb.Call(txGet(), func(tx *sql.Tx) error {     return tccAdjustTrading(tx, TransOutUID, req.Amount)   }) })) app.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   return bb.Call(txGet(), func(tx *sql.Tx) error {     return tccAdjustTrading(tx, TransInUID, req.Amount)   }) })) app.POST(BusiAPI+"/TccBTransOutConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   return bb.Call(txGet(), func(tx *sql.Tx) error {     return tccAdjustBalance(tx, TransInUID, reqFrom(c).Amount)   }) })) app.POST(BusiAPI+"/TccBTransInCancel", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   return bb.Call(txGet(), func(tx *sql.Tx) error {     return tccAdjustTrading(tx, TransInUID, -req.Amount)   }) }))
  到此各个子事务的处理函数已经OK了,这些处理函数的核心逻辑都是冻结和调整余额,对于这里面的 bb.Call  作用,后面会详细解释TCC 事务
  然后是开启TCC事务,进行分支调用 // TccGlobalTransaction 会开启一个全局事务 _, err := dtmcli.TccGlobalTransaction(DtmServer, func(tcc *dtmcli.Tcc) (rerr error) {   // CallBranch 会将事务分支的Confirm/Cancel注册到全局事务上,然后直接调用Try   res1, rerr := tcc.CallBranch(&TransReq{Amount: 30}, host+"/api/TccBTransOutTry", host+"/api/TccBTransOutConfirm", host+"/api/TccBTransOutCancel"   if err != nil {     return resp, err   }   return tcc.CallBranch(&TransReq{Amount: 30}, host+"/api/TccBTransInTry", host+"/api/TccBTransInConfirm", host+"/api/TccBTransInCancel") })
  至此,一个完整的TCC分布式事务编写完成。 运行
  如果您想要完整运行一个成功的示例,步骤如下: 运行dtm git clone https://github.com/dtm-labs/dtm && cd dtm go run main.go运行例子 git clone https://github.com/dtm-labs/dtm-examples && cd dtm-examples go run main.go http_tcc_barrier处理网络异常
  假设提交给dtm的事务中,这些步骤中,出现短暂的故障怎么办?dtm 会重试未完成的操作,此时就会要求全局事务中的各个子事务是幂等的。dtm 框架首创子事务屏障技术,提供 BranchBarrier 工具类,可以帮助用户简单的处理幂等。它提供了一个函数 Call ,保证这个函数内部的业务,会被最多调用一次: func (bb *BranchBarrier) Call(tx *sql.Tx, busiCall BarrierBusiFunc) error
  该 BranchBarrier 不仅能够自动处理幂等,还能够自动处理空补偿、悬挂的问题,详情可以参考异常与子事务屏障 TCC的回滚
  假如银行将金额准备转入用户2时,发现用户2的账户异常,返回失败,会怎么样?我们修改代码,模拟这种情况: app.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   return dtmcli.ErrFailure }))
  这是事务失败交互的时序图
  这个跟成功的TCC差别就在于,当某个子事务返回失败后,后续就回滚全局事务,调用各个子事务的Cancel操作,保证全局事务全部回滚。
  这里有一点,TransInTry的正向操作什么都没有做,就返回了失败,此时调用TransInCancel补偿操作,会不会导致反向调整出错了呢?
  不用担心,前面的子事务屏障技术,能够保证TransInTry的错误如果发生在提交之前,则补偿为空操作;TransInTry的错误如果发生在提交之后,则补偿操作会将数据提交一次。
  您可以将  TccBTransInTry  改成app.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {   bb := MustBarrierFromGin(c)   bb.Call(txGet(), func(tx *sql.Tx) error {     return tccAdjustTrading(tx, TransInUID, req.Amount)   })   return dtmcli.ErrFailure }))
  最后的结果余额依旧会是对的,详情可以参考异常与子事务屏障

李敏和李讷都姓李,原来是跟着李云鹤姓的?江青这是个误会1949年,娇娇终于回到了爸爸的身边。虽然留在爸爸身边很是高兴,但是娇娇毕竟刚从苏联回来不久,北京对于她来说,实在陌生又冷漠。但,好在娇娇是个喜欢探索的孩子,所以对这一切都很好奇。历史45年毛主席远赴重庆,中央派出一虎二龙三鼠,戴笠谁是三叔1945年9月末,此时已距毛主席亲赴重庆进行谈判一月之久,在谈判桌上没有占到任何便宜的蒋介石,终究还是按捺不住内心的想法,想要暗箱操作,神不知鬼不觉地让毛主席消失。然而,他的险恶想抗美援朝胜利后,世界各国有什么反应?日本无法接受这个事实1953年,二战结束的第八个年头,祖国刚满周岁,100多万中国志愿军先后跨过鸭绿江,将美国逼到了只能签订停战协议的份上。中国战胜美国一事在各国都引起了热议,在日本更是掀起了惊涛骇浪伊朗加入上合组织,世界格局迎来大变化,未来的国际社会谁说了算如果俄罗斯,中国,伊朗这三个国家结成大联盟,对于这个世界将会产生什么样的影响?大家好啊,我是陪你看世界的通晓君。1997年,美国的大战略家布热津斯基在大棋局一书中写道未来20年内,阿森纳女足队长公开性取向,女足世界里,同性恋很常见?近日,阿森纳女足队长,帮助英格兰女足夺得2022女足欧洲杯冠军并拿到赛事金靴的前锋贝丝米德公开了自己的同性恋身份,并表示自己正在与阿森纳队友薇薇安米德玛正在交往。这位27岁的女足球浙江理想汽车申请注销!总裁套现超9000万,美团王兴也跑了浙江理想汽车有限公司申请注销天眼查App显示,9月5日,浙江理想汽车有限公司新增简易注销公告,公告期从9月5日至9月24日。该公司成立于2020年11月,注册资本10亿人民币,经营中国游戏遍及世界?中国游戏在海外崛起已成大势,2022年中国游戏已经涵盖欧美,日韩,东南亚和中东等地。但就手机游戏而言,目前中国的游戏制作已经处于世界领先地位。根据统计,2021年的中国游戏在海外销你玩过如此真实的武侠养成吗?种田玩家的福音来了相信有不少的玩家都特别喜欢经营养成的游戏,但是你玩过有关于古代的一些养成游戏吗?市面上绝大多数的一些有关古代的武侠类游戏中,其绝大多数都是把游戏的重心放在了游戏剧情里,以及在武侠的原神须弥铁匠铺武器原木剑,5个单手剑角色适配分析哈喽,大家好,我是拾柒。导读原神3。0版本更新之后,铁匠铺又上架了5把具有须弥特色的锻造武器。分别为单手剑原木刀双手剑森林王器弓箭王下近侍法器盈满之实长柄武器贯月矢这套武器上线之后代入感十足,门槛比较高,四款高拟真度的硬核射击游戏推荐头条创作挑战赛射击玩法一直都深受广大玩家们的喜爱,射击游戏算是市场上的主流产品。而在这类游戏中,一般又细分成两个大类,一种是简化爽快类型的,代表性的游戏包括CSGO使命召唤系列Ap美国铁路工人欲罢工提出了涨薪水的要求为了避免大罢工,美国铁路公司和铁路工人协会达成了暂时性的协议,避免了一场全国性的铁路大罢工。美国的铁路罢工最早发生在今天的7月份,铁路工人协会发言称,如果他们的要求没有得到满足时,
北齐的皇帝很昏庸,那皇后有多奇葩?过度放纵自己,后人辱骂千年北齐的皇帝很昏庸,那皇后有多奇葩?过度放纵自己,后人辱骂千年魏晋之后的南北朝,是一段极为混乱的时代。几乎在每一个时间段里,中国大地上都会有两到三个政权存在,可谓是你方唱罢我登场,各嗨起来!2022中国柳州国际水上狂欢节开幕,现场超燃9月30日上午9时30分,柳钢集团2022中国柳州国际水上狂欢节暨水上休闲运动会在柳州金沙角开幕。开幕仪式现场。本届水上狂欢节共设第十届柳州螺蛳粉美食节东方梦工场狂欢艺术节绿色水上澎湃海港升入中超十周年,跨越十年的两场比赛武磊都取得进球直播吧9月30日讯昨晚海港30战胜蓉城的比赛,正好也是球队冲超十周年的纪念日。赛后,澎湃新闻网以上海海港升入中超十年了,进球的还是崇明子弟武磊吕文君为题,称赞了崇明子弟们的坚守。确散文系列报道之九我想睡大觉散文系列报道之九我想睡大觉作者丁康权(逗号)世纪之夜,我想来想去还是想睡大觉,可以从20世纪睡到21世纪。千年等一回的夜,为什么一定要去干什么呢?这个世界变得越来越现实和越来越物质丰田汽车公布8月全球产销数据产量创同期新高达766683辆中国质量新闻网讯(张阳)9月29日,丰田汽车公布了其8月全球范围内的产销数据。数据显示,8月丰田汽车全球销量同比增长3。8至777047辆,全球产量同比增长44。3至766683辆挺进决赛!韩旭5数据全场第1统治攻防,睢然表态奖励两功臣各20万中国女篮时隔28年再度冲进世界杯决赛!本场对阵东道主澳大利亚的比赛实在惊心动魄,球队得分王李梦因发高烧缺阵,球队缺少了这么一位能攻坚的好手而替代李梦首发的小将张茹早早陷入犯规麻烦,50。1!9月制造业PMI超预期回升至扩张区间,三季度以来数据逐月向好每经记者李可愚每经编辑陈旭9月PMI出现超预期回升。9月30日,国家统计局发布2022年9月中国采购经理指数运行情况。数据显示,9月份,制造业采购经理指数(PMI)为50。1,比上(经济)综述美联储加息全球负面外溢效应不断扩大新华社北京10月1日电综述美联储加息全球负面外溢效应不断扩大新华社记者受美国联邦储备委员会持续激进加息影响,在通胀加剧本币贬值等压力下,不少国家近日不得不跟随美联储步伐上调利率,美威海银行副行长张文斌年仅39岁去年上任薪酬85万不如副行长陶遵建运营商财经网实习生杨雪利文近来,威海银行发布公告称,中国银行保险监督管理委员会山东监管局已核准该行副行长张文斌任执行董事的任职资格。接下来运营商财经网将揭秘威海银行副行长张文斌的履一加11R规格全面曝光搭载骁龙8最早今年12月推出手机中国新闻据手机中国了解,一加最早将于今年12月推出其下一代智能手机一加11系列。根据外媒的报道,一加将在推出一加11和11Pro的时候,一同推出一加11R。该报道还详细介绍了一科教兴国浅谈机器人教育的利与弊科教兴国这看似简单却不普通的四个字最初是在1995年全国科技大会中由邓小平提出,它最开始的解释为发展科技教育事业以振兴国家,并用科技和教育使国家强大振兴起来。直到现在,这四个字还在