golang protobuf unknown字段透传

需求背景:

云端在线架构有上游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。

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章