V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
xmcy0011
V2EX  ›  Go 编程语言

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

  •  
  •   xmcy0011 · 2020-10-22 19:58:17 +08:00 · 4518 次点击
    这是一个创建于 1526 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有个 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")
    	}
    }
    
    17 条回复    2020-11-10 14:24:26 +08:00
    BoarBoar
        1
    BoarBoar  
       2020-10-22 20:40:04 +08:00
    一般都是 interface{}代替泛型,不过会写很多用反射或者断言去判断 interface 持有类型的代码,用得少的话没必要搞
    lance6716
        2
    lance6716  
       2020-10-22 23:00:18 +08:00 via Android
    我当时用的运行时和反射。当时也考虑过 go generate template…
    fuis
        3
    fuis  
       2020-10-22 23:39:40 +08:00
    @xmcy0011 随手写了一段,大概看个意思。基本思路就是把重复的地方抠出来做成 func,然后用另一个 helper func 去调用它们。参数尽量看准原来就使用 interface{} 的地方,这样不用写很多 type assert

    ```go
    func (tcp *TcpConn) onHandleCreateGroup(header *cim.ImHeader, buff []byte) {
    req := &cim.CIMGroupCreateReq{}
    call(header, buff, req, func() {
    return conn.CreateGroup(ctx, req)
    }, func(rsp Response) {
    _, err = tcp.Send(...)
    })
    }

    func (tcp *TcpConn) onHandleDisbandingGroup(header *cim.ImHeader, buff []byte) {
    req := &cim.CIMGroupDisbandingReq{}
    call(header, buff, req, func () {
    return conn.DisbandingGroup(ctx, req)
    }, func(rsp Response) {
    _, err = tcp.Send(...)
    })
    }

    func (t *TcpConn) call(
    header *cim.ImHeader,
    buff []byte,
    req interface{},
    method string,
    rpcFn func(context.Context, interface{}) (Response, error),
    callback func(Response) error) func() {
    return func() {
    err := proto.Unmarshal(buff, req)
    if err != nil {
    logger.Sugar.Warnf("%s error", method)
    return
    }

    logger.Sugar.Info(method)

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

    rsp, err := rpcFn(ctx, req)
    if err != nil {
    logger.Sugar.Warnf("DisbandingGroup(gRPC) err:")
    } else {
    _, err := callback(rsp)
    logger.Sugar.Infof("onHandleDisbandingGroup res")
    }
    }
    }

    ```
    hakono
        4
    hakono  
       2020-10-22 23:44:14 +08:00 via Android
    反射会影响程序性能
    beidounanxizi
        5
    beidounanxizi  
       2020-10-23 01:44:11 +08:00
    别整反射 去看看 go proverb
    泛型 去用回 JAVA 吧
    没泛型 更优雅
    deepreader
        6
    deepreader  
       2020-10-23 04:21:39 +08:00
    等 go 1.15 支持泛型吧 https://blog.golang.org/why-generics
    raaaaaar
        7
    raaaaaar  
       2020-10-23 07:08:59 +08:00 via Android
    在写了在写了,别催
    xuanbg
        8
    xuanbg  
       2020-10-23 07:37:06 +08:00
    用不着泛型,log 的内容可以参数拼接,发送的内容可以先序列化再作为参数传入进行发送。你需要的只是分拆几个静态方法而已。
    eudore
        9
    eudore  
       2020-10-23 08:56:53 +08:00
    不是有 pb 插件自动生成的调用代码? 你还自己手写个啥
    zunceng
        10
    zunceng  
       2020-10-23 09:04:20 +08:00
    我在自己的 infra 里面写了个代码生成的脚本
    mikulch
        11
    mikulch  
       2020-10-23 10:43:31 +08:00 via iPhone
    @beidounanxizi 已经在做了,这个语法。
    joesonw
        12
    joesonw  
       2020-10-23 11:44:46 +08:00
    自己写个 protoc-plugin 进去, 生成 go 代码?
    troywinter
        13
    troywinter  
       2020-10-23 13:55:37 +08:00
    传个闭包进来,用户使用时,在 func 里自己 cast 到想要的类型,稍微啰嗦一两行。
    xmcy0011
        14
    xmcy0011  
    OP
       2020-11-05 10:58:34 +08:00
    @fuis 感谢,vinterface{}是正解。同时感谢 @beidounanxizi go 之禅第一次听说,我这里补个链接,大家有空可以研究一下,类似于编码习惯和修养的东西。https://senseis.xmp.net/?GoProverbs
    xmcy0011
        15
    xmcy0011  
    OP
       2020-11-05 11:23:19 +08:00
    抱歉,链接上是个啥玩意。 @beidounanxizi 能贴个链接吗?
    beidounanxizi
        16
    beidounanxizi  
       2020-11-05 18:07:59 +08:00
    @xmcy0011 第一个结果就是 你干嘛贴第二个? https://go-proverbs.github.io/
    xmcy0011
        17
    xmcy0011  
    OP
       2020-11-10 14:24:26 +08:00
    @beidounanxizi 非常感谢,拜读。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3026 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:54 · PVG 21:54 · LAX 05:54 · JFK 08:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.