美文网首页
golang protobuf unknown字段透传

golang protobuf unknown字段透传

作者: 舒小贱 | 来源:发表于2019-12-05 21:02 被阅读0次

    需求背景:

    云端在线架构有上游A-中间模块B-下游C三个模块,都是proto格式数据传输交互。流量方向为从A->B->C,当A模块需要给proto增加某个字段,这个字段中间模块B并不关系,只需要透传给下游模块C来使用。
    那么A和C模块都升级proto的情况下,作为透传模块的B能不能不升级proto文件呢?答案是肯定的,这就要说到proto3的unknown字段支持了(proto2和大于proto 3.5版本的支持)

    proto定义:

    A模块和c模块的proto:

    syntax = "proto3";
    package dcs;
    option go_package = "dcs_type";
    
    
    message Directive{
        Header header = 1;
        Payload payload = 2;
    }
    
    message Header{
         string namespace = 1;
         string name = 2;
         string messageid = 3;
    }
    
    message Payload{
         string token = 1;
    }
    

    b模块的proto:

    syntax = "proto3";
    package ui;
    option go_package = "ui_type";
    
    
    message Directive{
        Header header = 1;
        Payload payload = 2;
    }
    
    message Header{
         string namespace = 1;
         string name = 2;
    }
    
    message Payload{
         string token = 1;
    }
    

    从两份proto文件可以看到,A模块和C模块相比较B模块的proto,就是header里面多了一个messageid字段。

    demo

    先将proto 生成go class文件,然后

    上游A模块代码:

    package main
    
    import (
        "bytes"
        "fmt"
        "net/http"
        dcs_type "through_proto_check/through_dcs/dcs_type"
    
        proto "github.com/golang/protobuf/proto"
    )
    
    func main() {
    
        direct := new(dcs_type.Directive)
        direct.Header = &dcs_type.Header{
            Namespace: "screen",
            Name:      "voiceinput",
            Messageid: "11111111",
        }
        direct.Payload = &dcs_type.Payload{
            Token: "vvvvvvv",
        }
    
        out, err := proto.Marshal(direct)
        if err != nil {
            panic(err)
        }
    
        url := "http://127.0.0.1:8702/saiya/ui"
        req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(out))
        client := &http.Client{}
        res, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        fmt.Println(res)
    
    }
    

    中间B模块代码:

    package main
    
    import (
        "bytes"
        "fmt"
        "net/http"
        "through_proto_check/through_ui/ui_type"
    
        proto "github.com/golang/protobuf/proto"
        "github.com/kataras/iris"
    )
    
    func dcsEvents(ctx iris.Context) {
    
        body, err := ctx.GetBody()
        if err != nil {
            panic(err)
        }
    
        uiReq := new(ui_type.Directive)
        secUIReq := new(ui_type.Directive)
        err = proto.Unmarshal(body, uiReq)
        if err != nil {
            panic(err)
        }
    
        secUIReq.Header = uiReq.Header
        secUIReq.Payload = uiReq.Payload
        fmt.Println(secUIReq)
    
        out, err := proto.Marshal(secUIReq)
        if err != nil {
            panic(err)
        }
        url := "http://127.0.0.1:8703/saiya/us"
        req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(out))
        client := &http.Client{}
        res, err := client.Do(req)
    
        if err != nil {
            panic(err)
        }
        fmt.Println(res)
    
    }
    
    func main() {
        app := iris.New()
        app.Post("/saiya/ui", dcsEvents)
        fmt.Println("service running...")
        app.Run(iris.Addr(":8702"))
    
    }
    
    

    下游C模块代码

    package main
    
    import (
        "fmt"
        us_type "through_proto_check/through_us/us_type"
    
        proto "github.com/golang/protobuf/proto"
        "github.com/kataras/iris"
    )
    
    func usEvents(ctx iris.Context) {
    
        body, err := ctx.GetBody()
        if err != nil {
            panic(err)
        }
    
        usReq := new(us_type.Directive)
    
        err = proto.Unmarshal(body, usReq)
        if err != nil {
            panic(err)
        }
        fmt.Println(usReq)
    
    }
    
    func main() {
        app := iris.New()
        app.Post("/saiya/us", usEvents)
        fmt.Println("service running...")
        app.Run(iris.Addr(":8703"))
    
    }
    
    

    结果

    分别依次启动C,B,A服务:

    C服务:

    [work@ through_us]#go run main.go
    service running...
    Now listening on: http://0.0.0.0:8703
    Application started. Press CTRL+C to shut down.
    header:<namespace:"screen" name:"voiceinput" messageid:"11111111" > payload:<token:"vvvvvvv" >
    

    B服务:

    [work@ through_ui]#go run main.go
    service running...
    Now listening on: http://0.0.0.0:8702
    Application started. Press CTRL+C to shut down.
    header:<namespace:"screen" name:"voiceinput" 3:"11111111" > payload:<token:"vvvvvvv" >
    &{200 OK 200 HTTP/1.1 1 1 map[Content-Length:[0] Date:[Thu, 05 Dec 2019 11:38:32 GMT]] {} 0 [] false false map[] 0xc00004ad00 <nil>}
    

    A服务:

    [work@ through_dcs]#go run main.go
    &{200 OK 200 HTTP/1.1 1 1 map[Content-Length:[0] Date:[Thu, 05 Dec 2019 11:20:41 GMT]] {} 0 [] false false map[] 0xc00002a100 <nil>}
    

    说明

    从C打印出来的数据可以看到成功拿到了mssageid,这个key在B模块的proto中是没有定义的。
    从b模块打印出来的信息可以看到:

    3:"11111111"
    

    经过A模块marshal传输过来的数据,在B模块unmarshal后,message这个不认识的字段的信息(unknown field)被编码成了pb_id:val这样的数据给暂存下来,这样的数据经过marshal后会保留,传给下游C模块经unmarshal后,会根据pb_id找到key为messageid。至此,C模块成功的拿到了messageid。

    相关文章

      网友评论

          本文标题:golang protobuf unknown字段透传

          本文链接:https://www.haomeiwen.com/subject/stejgctx.html