Go micro集成DTM分布式事务的方法

背景

我有一个go micro开发的微服务要引入分布式事务,用的是Saga模式,结果遇到DTM访问gRPC接口报错,返回Unsupported Content-Type: application/grpc+dtm_raw,网上搜了一圈没有任何资料,AI也给不出答案,分布式事务是必不可少的,很明显框架默认不支持DTM但还是要解决。

分析

我们可以通过错误分析得知是不支持application/grpc+dtm_raw这种格式,因为go micro的GRPC Server有个newGRPCCodec方法,源码如下:

go 复制代码
func (g *grpcServer) newGRPCCodec(contentType string) (encoding.Codec, error) {
	codecs := make(map[string]encoding.Codec)
	if g.opts.Context != nil {
		if v, ok := g.opts.Context.Value(codecsKey{}).(map[string]encoding.Codec); ok && v != nil {
			codecs = v
		}
	}
	if c, ok := codecs[contentType]; ok {
		return c, nil
	}
	if c, ok := defaultGRPCCodecs[contentType]; ok {
		return c, nil
	}
	return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
}

Content-Type对应的Codec来实现,先检查Context里面有没有对应的编解码器,如果没有就从defaultGRPCCodecs选择一个来解码,还是找不到就报上文提到的错误。这个格式是DTM特有的,defaultGRPCCodecs找不到没有相应的编解码器所以会报错,默认编解码器映射表源码如下:

go 复制代码
	defaultGRPCCodecs = map[string]encoding.Codec{
		"application/json":         jsonCodec{},
		"application/proto":        protoCodec{},
		"application/protobuf":     protoCodec{},
		"application/octet-stream": protoCodec{},
		"application/grpc":         protoCodec{},
		"application/grpc+json":    jsonCodec{},
		"application/grpc+proto":   protoCodec{},
		"application/grpc+bytes":   bytesCodec{},
	}

解决方法

由于采用的是GRPC通信,application/grpc+dtm_raw和application/grpc是相通的,所以我们找到protoCodec{}结构体把它的所有方法复制出来放到自己的项目目录下,修改该编解码器的结构体名称,Name方法改成其他名字比如"grpc+dtm_raw",然后在创建GRPC Server的时候注入自定义编解码器,部分代码如下:(重点看grpcserver.Codec)这一行。

go 复制代码
import (
	grpcserver "github.com/go-micro/plugins/v4/server/grpc"
	"github.com/go-micro/plugins/v4/transport/grpc"
	"go-micro.dev/v4"
	"go-micro.dev/v4/server"
	"time"
)

func newServer(conf *config.SysConfig) micro.Option {
	// 注册到Consul
	consulRegistry := infrastructure.ConsulRegister(conf.Consul)
	return micro.Server(
		grpcserver.NewServer(
			server.Name(conf.Service.Name),
			server.Version(conf.Service.Version),
			server.Address(conf.Service.Listen),
			server.Transport(grpc.NewTransport()),
			server.Registry(consulRegistry),
			server.RegisterTTL(time.Duration(conf.Consul.RegisterTtl)*time.Second),
			server.RegisterInterval(time.Duration(conf.Consul.RegisterInterval)*time.Second),
			grpcserver.Codec("application/grpc+dtm_raw", codec.NewDtmCodec()),  // 在这里注入你的Codec,这样框架底层会先从Context里取出这个编解码器来解析DTM发出的消息格式
			grpcserver.MaxConn(conf.Service.MaxConn),
			grpcserver.MaxMsgSize(conf.Service.MaxMsgSize),
		))
}

这样我们就可以在DTM里使用自定义的编解码器正常解析DTM发来的消息,完成分布式事务的所有流程。

相关推荐
Turnip12021 小时前
深度解析:为什么简单的数据库"写操作"会在 MySQL 中卡住?
后端·mysql
神奇小汤圆2 小时前
终于找到一个好用的 Nginx 日志分析工具了!
后端
Java编程爱好者2 小时前
Condition底层机制剖析:多线程等待与通知机制
后端
Java编程爱好者2 小时前
面试官:你知道 MCP、Skill、Function Call 这三个的区别吗?
后端
狂炫冰美式2 小时前
把手从键盘上抬起来:AI 编程的 3 个不可逆阶段
前端·后端·ai编程
王中阳Go3 小时前
Go 协程池满了怎么办?面试官问我“兜底策略”,我差点挂了……
后端
蝎子莱莱爱打怪4 小时前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes
茶杯梦轩4 小时前
CompletableFuture 在 项目实战 中 创建异步任务 的核心优势及使用场景
服务器·后端·面试
埃博拉酱5 小时前
SMB服务器无法访问?一次PowerShell故障排查演练
后端