Golang Go语言中 没有泛型,业务逻辑相似的 grpc 调用代码如何能更优雅一些?

发布于 1周前 作者 bupafengyu 来自 Go语言

Golang Go语言中 没有泛型,业务逻辑相似的 grpc 调用代码如何能更优雅一些?

有个 gate 网关,包装一层 grpc 调用 logic 上的接口,协议使用的 protobuf 。大致业务流程都差不多: 1.反序列化成对象 2.调用 grpc 接口 3.根据结果,发送到客户端。

具体代码如下:

// 创建群
func (tcp *TcpConn) onHandleCreateGroup(header *cim.ImHeader, buff []byte) {
	req := &cim.CIMGroupCreateReq{}
	err := proto.Unmarshal(buff, req)
	if err != nil {
		logger.Sugar.Warnf("onHandleCreateGroup error")
		return
	}
logger.Sugar.Info("onHandleCreateGroup")

conn := GetMessageConn()
ctx, cancelFun := context.WithTimeout(context.Background(), time.Second*3)
defer cancelFun()

rsp, err := conn.CreateGroup(ctx, req)
if err != nil {
	logger.Sugar.Warnf("CreateGroup(gRPC) err:", err.Error())
} else {
	_, err = tcp.Send(header.SeqNum, uint16(GROUP_CREATE_DEFAULT_REQ), rsp)
	logger.Sugar.Infof("onHandleCreateGroup CreateGroup(gRPC) res")
}

}

// 解散群 func (tcp *TcpConn) onHandleDisbandingGroup(header *cim.ImHeader, buff []byte) { req := &cim.CIMGroupDisbandingReq{} err := proto.Unmarshal(buff, req) if err != nil { logger.Sugar.Warnf(“onHandleDisbandingGroup error”) return }

logger.Sugar.Info("onHandleDisbandingGroup")

conn := GetMessageConn()
ctx, cancelFun := context.WithTimeout(context.Background(), time.Second*3)
defer cancelFun()

rsp, err := conn.DisbandingGroup(ctx, req)
if err != nil {
	logger.Sugar.Warnf("DisbandingGroup(gRPC) err:")
} else {
	_, err = tcp.Send(header.SeqNum, uint16(GROUP_DISBINGDING_RSP), rsp)
	logger.Sugar.Infof("onHandleDisbandingGroup res")
}

}


更多关于Golang Go语言中 没有泛型,业务逻辑相似的 grpc 调用代码如何能更优雅一些?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

18 回复

一般都是 interface{}代替泛型,不过会写很多用反射或者断言去判断 interface 持有类型的代码,用得少的话没必要搞

更多关于Golang Go语言中 没有泛型,业务逻辑相似的 grpc 调用代码如何能更优雅一些?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我当时用的运行时和反射。当时也考虑过 go generate template…

随手写了一段,大概看个意思。基本思路就是把重复的地方抠出来做成 func,然后用另一个 helper func 去调用它们。参数尽量看准原来就使用 interface{} 的地方,这样不用写很多 type assert

go<br>func (tcp *TcpConn) onHandleCreateGroup(header *cim.ImHeader, buff []byte) {<br> req := &amp;cim.CIMGroupCreateReq{}<br> call(header, buff, req, func() {<br> return conn.CreateGroup(ctx, req)<br> }, func(rsp Response) {<br> _, err = tcp.Send(...)<br> })<br>}<br><br>func (tcp *TcpConn) onHandleDisbandingGroup(header *cim.ImHeader, buff []byte) {<br> req := &amp;cim.CIMGroupDisbandingReq{}<br> call(header, buff, req, func () {<br> return conn.DisbandingGroup(ctx, req)<br> }, func(rsp Response) {<br> _, err = tcp.Send(...)<br> })<br>}<br><br>func (t *TcpConn) call(<br> header *cim.ImHeader,<br> buff []byte,<br> req interface{},<br> method string,<br> rpcFn func(context.Context, interface{}) (Response, error),<br> callback func(Response) error) func() {<br> return func() {<br> err := proto.Unmarshal(buff, req)<br> if err != nil {<br> logger.Sugar.Warnf("%s error", method)<br> return<br> }<br><br> <a target="_blank" href="http://logger.Sugar.Info" rel="nofollow noopener">logger.Sugar.Info</a>(method)<br><br> conn := GetMessageConn()<br> ctx, cancelFun := context.WithTimeout(context.Background(), time.Second*3)<br> defer cancelFun()<br><br> rsp, err := rpcFn(ctx, req)<br> if err != nil {<br> logger.Sugar.Warnf("DisbandingGroup(gRPC) err:")<br> } else {<br> _, err := callback(rsp)<br> logger.Sugar.Infof("onHandleDisbandingGroup res")<br> }<br> }<br>}<br><br>

反射会影响程序性能

别整反射 去看看 go proverb
泛型 去用回 JAVA 吧
没泛型 更优雅

在写了在写了,别催

用不着泛型,log 的内容可以参数拼接,发送的内容可以先序列化再作为参数传入进行发送。你需要的只是分拆几个静态方法而已。

不是有 pb 插件自动生成的调用代码? 你还自己手写个啥

我在自己的 infra 里面写了个代码生成的脚本

已经在做了,这个语法。

自己写个 protoc-plugin 进去, 生成 go 代码?

传个闭包进来,用户使用时,在 func 里自己 cast 到想要的类型,稍微啰嗦一两行。

感谢,vinterface{}是正解。同时感谢 go 之禅第一次听说,我这里补个链接,大家有空可以研究一下,类似于编码习惯和修养的东西。https://senseis.xmp.net/?GoProverbs

抱歉,链接上是个啥玩意。 能贴个链接吗?

第一个结果就是 你干嘛贴第二个? https://go-proverbs.github.io/

非常感谢,拜读。

在Golang中,虽然早期版本确实没有泛型支持,但在Go 1.18及以后的版本中,泛型已经被引入,这为编写更通用和优雅的代码提供了可能。不过,即使在没有泛型支持的早期版本中,也有多种方法可以使业务逻辑相似的grpc调用代码更加优雅。

一种常见的方法是使用接口和抽象。你可以定义一个接口来描述grpc调用的共同行为,然后为每个具体的grpc服务实现这个接口。这样,你就可以使用接口类型的变量来调用不同的grpc服务,而无需关心具体的实现细节。

此外,你还可以使用设计模式来优化代码结构,例如模板方法模式或策略模式。这些模式允许你定义算法的骨架,并将算法中可变的部分抽象出来,以便在运行时进行选择。

如果你正在使用Go 1.18或更高版本,那么泛型将为你提供更强大的工具来编写更通用的代码。你可以使用泛型来定义函数或类型,这些函数或类型可以处理多种数据类型,而无需为每种数据类型编写单独的实现。

最后,不要忽视代码重构的重要性。随着业务的发展,代码可能会变得复杂和难以维护。定期进行代码审查和重构,可以帮助你发现并解决这些问题,使代码更加清晰和易于维护。

总之,通过利用接口、设计模式、泛型以及定期的代码重构,你可以使业务逻辑相似的grpc调用代码更加优雅和易于维护。

回到顶部