前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >protoc语法详解及结合grpc定义服务

protoc语法详解及结合grpc定义服务

作者头像
陌无崖
发布2019-08-16 17:26:59
2.7K0
发布2019-08-16 17:26:59
举报

导语

说到JSON可能大家很熟悉,是目前应用最广泛的一种序列化格式,它使用起来简单方便,而且拥有超高的可读性。但是在越来越多的应用场景里,JSON冗长的缺点导致它并不是一种最优的选择。而今天总结的Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。本文主要以Golang语言进行介绍。

应用领域

数据通信、grpc通信、微服务。

准备环境

首先需要在下载protoc编译器【会很慢,建议使用V**】

将下载好的编译器移动到$GOPATH/bin目录

使用命令安装插件

代码语言:javascript
复制
go get -u github.com/golang/protobuf/protoc-gen-go

简单的例子

我们创建一个p.proto文件这个例子中message代表一个消息类型,在消息类型中有三个字段,这里不在多说,大家都明白。

代码语言:javascript
复制
syntax = "proto3";

message RequestParm {
    string query = 1;
    int32 pages = 2;
    int32 article_page = 3;
}

现在我们运行一下,目录切换到这个文件的目录执行一下代码

代码语言:javascript
复制
protoc -I. --go_out=plugins=grpc:. p.proto

可以看到编译后出现了p.pb.go的文件,打开这个文件可以看到,有下面部分代码,我们的消息类型变成了一个结构体

代码语言:javascript
复制
type RequestParm struct {
    Query                string   `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
    Pages                int32    `protobuf:"varint,2,opt,name=pages,proto3" json:"pages,omitempty"`
    ArticlePage          int32    `protobuf:"varint,3,opt,name=article_page,json=articlePage,proto3" json:"article_page,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

在这个结构体中出现了一些基础的方法,如下

语法介绍【对应Golang】

数据类型的对应

.proto Type

Notes

Go Type

double

float64

float

float32

int32

使用可变长度编码。无效编码负数 - 如果您的字段可能具有负值, 请改用sint32。

int32

int64

使用可变长度编码。无效编码负数 - 如果您的字段可能具有负值,请改用sint64。

int64

uint32

使用可变长度编码。

uint32

uint64

使用可变长度编码。

uint64

sint32

使用可变长度编码。带符号的int值。这些比常规的int32更有效地编码负数。

int32

sint64

使用可变长度编码。带符号的int值。这些比常规的int64更有效地编码负数。

int64

fixed32

总是四个字节。如果值通常大于228,则比uint32效率更高。

uint32

fixed64

总是八个字节。如果值通常大于256,则会比uint64更高效。

uint64

sfixed32

总是四个字节。

int32

sfixed64

总是八个字节。

int64

bool

bool

string

字符串必须始终包含UTF-8编码或7位ASCII文本。

string

bytes

可能包含任何字节序列。

[]byte

Repeated 字段【指针数组】

代码语言:javascript
复制
message ErrorStatus {
    string message = 1;
    repeated string details = 2;
}

枚举类型

代码语言:javascript
复制
message Bar {
    string a = 15;
    repeated RequestParm  r = 4;
    enum Data {
        FIRST = 0;
        SECOND = 1;
        THIRD = 2;
    }
    Data data = 5;
}

编译之后的部分结果

代码语言:javascript
复制
type Bar_Data int32

const (
    Bar_FIRST  Bar_Data = 0
    Bar_SECOND Bar_Data = 1
    Bar_THIRD  Bar_Data = 2
)
type Bar struct {
    A                    string         `protobuf:"bytes,15,opt,name=a,proto3" json:"a,omitempty"`
    R                    []*RequestParm `protobuf:"bytes,4,rep,name=r,proto3" json:"r,omitempty"`
    Data                 Bar_Data       `protobuf:"varint,5,opt,name=data,proto3,enum=Bar_Data" json:"data,omitempty"`
    XXX_NoUnkeyedLiteral struct{}       `json:"-"`
    XXX_unrecognized     []byte         `json:"-"`
    XXX_sizecache        int32          `json:"-"`
}

嵌套类型

代码语言:javascript
复制
message SearchResponse {
    message Result {
      string url = 1;
      string title = 2;
      repeated string snippets = 3;
    }
    repeated Result results = 1;
}

编译之后的结果

代码语言:javascript
复制
type SearchResponse_Result struct {
    Url                  string   `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
    Title                string   `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
    Snippets             []string `protobuf:"bytes,3,rep,name=snippets,proto3" json:"snippets,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}
type SearchResponse struct {
    Results              []*SearchResponse_Result `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"`
    XXX_NoUnkeyedLiteral struct{}                 `json:"-"`
    XXX_unrecognized     []byte                   `json:"-"`
    XXX_sizecache        int32                    `json:"-"`
}

map类型

代码语言:javascript
复制
message SubMessage {
    string sub = 1;
    map<string, string> m = 4;
}

编译之后的结果

代码语言:javascript
复制
type SubMessage struct {
    Sub                  string            `protobuf:"bytes,1,opt,name=sub,proto3" json:"sub,omitempty"`
    M                    map[string]string `protobuf:"bytes,4,rep,name=m,proto3" json:"m,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
    XXX_NoUnkeyedLiteral struct{}          `json:"-"`
    XXX_unrecognized     []byte            `json:"-"`
    XXX_sizecache        int32             `json:"-"`
}

Any类型

需要导入import "google/protobuf/any.proto";

代码语言:javascript
复制
message ErrorStatus {
    string message = 1;
    repeated google.protobuf.Any details = 2;
}

编译之后的结果

代码语言:javascript
复制
type ErrorStatus struct {
    Message              string     `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
    Details              []*any.Any `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"`
    XXX_NoUnkeyedLiteral struct{}   `json:"-"`
    XXX_unrecognized     []byte     `json:"-"`
    XXX_sizecache        int32      `json:"-"`
}

可以看到在编译之后的代码是一个*any.Any的指针数组,那么如何使用呢?按照Golang语言,这种类型代替的是interface{}类型 ,首先我们想看一下Any的源码

代码语言:javascript
复制
type Any struct {
    // A URL/resource name that uniquely identifies the type of the serialized
    // protocol buffer message. The last segment of the URL's path must represent
    // the fully qualified name of the type (as in
    // `path/google.protobuf.Duration`). The name should be in a canonical form
    // (e.g., leading "." is not accepted).
    //
    // In practice, teams usually precompile into the binary all types that they
    // expect it to use in the context of Any. However, for URLs which use the
    // scheme `http`, `https`, or no scheme, one can optionally set up a type
    // server that maps type URLs to message definitions as follows:
    //
    // * If no scheme is provided, `https` is assumed.
    // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
    //   value in binary format, or produce an error.
    // * Applications are allowed to cache lookup results based on the
    //   URL, or have them precompiled into a binary to avoid any
    //   lookup. Therefore, binary compatibility needs to be preserved
    //   on changes to types. (Use versioned type names to manage
    //   breaking changes.)
    //
    // Note: this functionality is not currently available in the official
    // protobuf release, and it is not used for type URLs beginning with
    // type.googleapis.com.
    //
    // Schemes other than `http`, `https` (or the empty scheme) might be
    // used with implementation specific semantics.
    //
    TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
    // Must be a valid serialized protocol buffer of the above specified type.
    Value                []byte   `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

在源码中有一个字节类型的数组,很明显,我们将用这个数组来存储我们的代码了,任何类型都可转换成字节,因此可以存储到该字段里

定义服务

代码语言:javascript
复制
syntax = "proto3";


import "any.proto";

message CallRequest {
  string greeting = 1;
  map<string, string> infos  = 2;
}

message CallResponse {
  string reply = 1;
  repeated google.protobuf.Any details = 2;
}

message Res {
    string reply = 4;
}
service CallService {
  rpc SayCall(CallRequest) returns (CallResponse){}
}

启动服务端

代码语言:javascript
复制
package main

import (
    "context"
    "net"

    "github.com/golang/protobuf/ptypes"
    "github.com/golang/protobuf/ptypes/any"
    pb "github.com/yuwe1/gopratice/proto-pratice/protodemo/protodemo1"
    "google.golang.org/grpc"
)

type CallServer struct {
}

func (c *CallServer) SayCall(ctx context.Context, re *pb.CallRequest) (res *pb.CallResponse, err error) {

    var an *any.Any
    if re.Infos["A"] == "B" {
        an, err = ptypes.MarshalAny(&pb.Res{Reply: "请求正确"})
    } else {
        an, err = ptypes.MarshalAny(&pb.Res{Reply: "请求出错"})
    }
    return &pb.CallResponse{
        Reply:   "Hello World !!",
        Details: []*any.Any{an},
    }, nil
}

func main() {
    lis, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }

    // 新建一个grpc服务器
    grpcServer := grpc.NewServer()
    // 向grpc服务器注册SayHelloServer
    pb.RegisterCallServiceServer(grpcServer, &CallServer{})
    // 启动服务
    grpcServer.Serve(lis)
}

启动客户端

代码语言:javascript
复制
package main

import (
    "context"
    "log"

    "google.golang.org/grpc"

    pb "github.com/yuwe1/gopratice/proto-pratice/protodemo/protodemo1"
)

func main() {
    // 创建一个 gRPC channel 和服务器交互
    conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Dial failed:%v", err)
    }
    defer conn.Close()

    // 创建客户端
    client := pb.NewCallServiceClient(conn)

    // 直接调用
    resp1, err := client.SayCall(context.Background(), &pb.CallRequest{
        Greeting: "Hello Server 1 !!",
        Infos:    map[string]string{"A": "B"},
    })

    log.Printf("Resp1:%+v", resp1)

    resp2, err := client.SayCall(context.Background(), &pb.CallRequest{
        Greeting: "Hello Server 2 !!",
    })

    log.Printf("Resp2:%+v", resp2)
}

现在自己运行看看会出现什么内容吧

推荐阅读

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang技术杂文 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导语
  • 应用领域
  • 准备环境
  • 简单的例子
  • 语法介绍【对应Golang】
    • 数据类型的对应
      • Repeated 字段【指针数组】
        • 枚举类型
          • 嵌套类型
            • map类型
              • Any类型
              • 定义服务
              • 启动服务端
              • 启动客户端
              • 推荐阅读
              相关产品与服务
              文件存储
              文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档