以下文章来源于阿里巴巴云原生 ,作者刘晓敏
发布云原生技术资讯、汇集云原生技术详细内容,定期举办云原生活动、直播,阿里产品及用户实战发布。与你并肩探索云原生技术点滴,分享你需要的云原生内容。
seata 的 AT 模式将全局锁放在 transaction coordinator 也就是事务协调器上,依赖于具体锁接口的存储实现方式可以是 file/db/redis 等,而不是数据库锁,每个分支事务提交时立即释放数据库锁,这样对数据库的压力也就减小了,变相得提升了数据库的性能。seata AT 模式和 TCC 模式的原理见:[Seata 是什么?]
下面以seats-golang samples 为例,就 AT 模式和 TCC 模式如何接入到业务中做一个说明。
AT 模式接入
product_svc 负责创建订单时扣减库存。
order_svc 负责创建订单时写入订单主表和订单明细表。
aggregation_svc 通过 http 请求调用 order_svc 和 product _svc 的接口。
type GlobalTransactionProxyService interface {
GetProxyService() interface{}
GetMethodTransactionInfo(methodName string) *TransactionInfo
}
CreateSo
,该方法通过对 order_svc 和 product_svc 的调用实现了创建订单和扣减库存。seata-golang 要代理该 *Svc 对象,需要创建一个代理对象,被代理的方法要在代理对象中作为一个空方法成员,等待 seata-golang 去动态实现。type ProxyService struct {
*Svc
CreateSo func(ctx context.Context, rollback bool) error
}
tm.Implement(svc.ProxySvc)
方法后,seata-golang 会通过 Svc 实现的 GlobalTransactionProxyService 接口获取动态创建 CreateSo 方法所需要的事务信息,然后根据这些事务信息去动态创建 CreateSo 方法:开启事务 -> 执行被代理 *Svc 对象的 CreateSo 方法逻辑 -> 根据被代理的 CreateSo 方法的返回错误信息决定提交还是回滚。req.Header.Set("XID", rootContext.GetXID())
)将 XID (全局事务 ID)传递到了 order_svc 和 product_svc,order_svc 和 product_svc 则从 Request Header 取出 XID (c.Request.Header.Get("XID")
)用于分支事务处理。context.WithValue(ctx, "attachment", map[string]string{
"XID": rootContext.GetXID(),
}
// SeataFilter ...
type SeataFilter struct {
}
// Invoke ...
func (sf *SeataFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
xid := invocation.AttachmentsByKey("XID", "")
if xid != "" {
return invoker.Invoke(context.WithValue(ctx, "XID", xid), invocation)
}
return invoker.Invoke(ctx, invocation)
}
// OnResponse ...
func (sf *SeataFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
return result
}
// GetSeataFilter ...
func getSeataFilter() filter.Filter {
return &SeataFilter{}
}
extension.SetFilter("SEATA", getSeataFilter)
方法可将其注入到 dubbo-go 的 filter 链。md := metadata.Pairs("XID", rootContext.GetXID())
,再将 metadata 传入 context:metadata.NewOutgoingContext(context.Background(), md)
。md, ok := metadata.FromIncomingContext(ctx)
获取到 metadata,再从中取出 XID。db, err := exec.NewDB(config.GetATConfig(), {你的 sql driver 实例})
rootContext := &context.RootContext{Context: ctx}
rootContext.Bind("{上游获取到的 XID}")
tx, err := dao.Begin(ctx)
。func (tx *Tx) Exec(query string, args ...interface{}) (sql.Result, error)
。tx.Commit()
或 tx.Rollback()
来提交或回滚分支操作。TCC 模式接入
type TccService interface {
Try(ctx *context.BusinessActionContext) (bool, error)
Confirm(ctx *context.BusinessActionContext) bool
Cancel(ctx *context.BusinessActionContext) bool
}
type TccProxyService interface {
GetTccService() TccService
}
通过调用 tcc.ImplementTCC({代理类实例})
方法,框架会为代理类实现上述逻辑。开发者可在samples/tcc 目录查看 tcc 模式的示例。
总结陈述
除了项目结构目录内的 samples,还有一个 dubbo-go 的例子dubbo-go-seata。对于上文讲述的接入方法,还希望读者结合代码多多理解,融汇贯通。
长按扫描二维码,添加管理员微信
加入容器时代交流群吧~
记得备注“加群”字样哦