go-grpc实践指南-01grpc+protobuf介绍

目录

安装

  1. 第一步安装protoc编译器:
    linux下载地址
    windows64下载地址
  2. 安装protoc-gen-go和protoc-gen-go-grpc两个插件
go get google.golang.org/protobuf
go get google.golang.org/grpc
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

grpc简介

参考文档

这里着重说一下grpc采用Http/2的优势:
grpc采用http2标准设计,所以相对于其它rpc框架,grpc带来了更多强大的功能,如:双向流、头部压缩、多复用请求等。这些功能给移动设备带来重大溢出,如节省带宽、降低tcp链接次数、节省CPU和延长电池使用寿命等,同时grpc还能够提高云端服务和web应用的性能,grpc既能够在客户端应用,也能够在服务端应用,从而以透明的方式实现客户端和服务端的通信和简化通信系统的构建。

protobuf--->go

这里使用一个测试文件对照说明常用结构的protobuf到golang的转换。

  1. package
    在proto文件中使用package关键字声明包名,默认转换成go中的包名与此一致,如果需要指定不一样的包名,可以使用go_package选项:
package test;
option go_package="test";
  1. Message
    proto中的message对应go中的struct,全部使用驼峰命名规则,嵌套定义的message、enum转换为go之后,名称变为Parent_Child结构
    示例proto:
syntax = "proto3";

package proto;
option go_package = "my_grpc/proto";

message Test {
  int32 age = 1;
  int64 count = 2;
  double money = 3;
  float score = 4;
  string name = 5;
  bool fat = 6;
  bytes char = 7;
  // Status:枚举状态
  enum Status {
    Ok = 0;
    Fail = 1;
  }
  Status status = 8;
  // Child子结构
  message Child {
    string sex = 1;
  }
  Child child = 9;
  map<string, string> dict = 10;
}

转换结果:

// Status:枚举状态
type Test_Status int32

const (
	Test_Ok   Test_Status = 0
	Test_Fail Test_Status = 1
)

type Test struct {
	Age    int32             `protobuf:"varint,1,opt,name=age,proto3" json:"age,omitempty"`
	Count  int64             `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
	Money  float64           `protobuf:"fixed64,3,opt,name=money,proto3" json:"money,omitempty"`
	Score  float32           `protobuf:"fixed32,4,opt,name=score,proto3" json:"score,omitempty"`
	Name   string            `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"`
	Fat    bool              `protobuf:"varint,6,opt,name=fat,proto3" json:"fat,omitempty"`
	Char   []byte            `protobuf:"bytes,7,opt,name=char,proto3" json:"char,omitempty"`
	Status Test_Status       `protobuf:"varint,8,opt,name=status,proto3,enum=pb.Test_Status" json:"status,omitempty"`
	Child  *Test_Child       `protobuf:"bytes,9,opt,name=child,proto3" json:"child,omitempty"`
	Dict   map[string]string `protobuf:"bytes,10,rep,name=dict,proto3" json:"dict,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}

// Child子结构
type Test_Child struct {
	Sex string `protobuf:"bytes,1,opt,name=sex,proto3" json:"sex,omitempty"`
}

除了会生成对应的结构外,还会有些工具方法,如字段的getter:

func (x *Test) GetAge() int32 {
	if x != nil {
		return x.Age
	}
	return 0
}

枚举类型会生成对应名称的常量,同时会有两个map方法使用:

// Enum value maps for Test_Status.
var (
	Test_Status_name = map[int32]string{
		0: "Ok",
		1: "Fail",
	}
	Test_Status_value = map[string]int32{
		"Ok":   0,
		"Fail": 1,
	}
)
  1. service
    定义一个简单的Service,TestService有一个方法Test,接收一个Request参数,返回Response:
service TestService {
  rpc Test(Request) returns(Response){}
}
message Request {
  string name = 1;
}
message Response {
  string message = 1;
}

转换结果:

# 客户端接口
type TestServiceClient interface {
	Test(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}

# 服务端接口
type TestServiceServer interface {
	Test(context.Context, *Request) (*Response, error)
	mustEmbedUnimplementedTestServiceServer()
}

生成的go代码中包含该service定义的接口,客户端接口已经自动实现了,直接供客户端调用者调用,服务端接口需要由服务提供方实现。

服务端代码

package main

import (
	"context"
	"google.golang.org/grpc"
	"my_grpc/pb"
	"net"
)

type TestService struct{
	pb.UnimplementedTestServiceServer
}

func (t *TestService) Test(ctx context.Context, in *pb.Request) (*pb.Response, error) {
	return &pb.Response{Message: "hello " + in.Name}, nil
}
func main() {
	listen, _ := net.Listen("tcp", ":9000")
	gServer := grpc.NewServer()
	pb.RegisterTestServiceServer(gServer, &TestService{})
	_ = gServer.Serve(listen)
}

客户端代码

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"my_grpc/pb"
)

func main() {
	clientConn, _ := grpc.Dial(":9000", grpc.WithTransportCredentials(insecure.NewCredentials()))
	testServiceClient := pb.NewTestServiceClient(clientConn)
	reply, _ := testServiceClient.Test(context.Background(), &pb.Request{Name: "哈哈"})
	fmt.Println(reply)
}

protobuf语法

proto中的数据结构对应不同语言的数据类型

.proto C++ Java Python Go Ruby C#
double double double float float64 Float double
float float float float float32 Float float
int32 int32 int int int32 Fixnum or Bignum int
int64 int64 long ing/long[3] int64 Bignum long
uint32 uint32 int[1] int/long[3] uint32 Fixnum or Bignum uint
uint64 uint64 long[1] int/long[3] uint64 Bignum ulong
sint32 int32 int intj int32 Fixnum or Bignum int
sint64 int64 long int/long[3] int64 Bignum long
fixed32 uint32 int[1] int uint32 Fixnum or Bignum uint
fixed64 uint64 long[1] int/long[3] uint64 Bignum ulong
sfixed32 int32 int int int32 Fixnum or Bignum int
sfixed64 int64 long int/long[3] int64 Bignum long
bool bool boolean boolean bool TrueClass/FalseClass bool
string string String str/unicode[4] string String(UTF-8) string
bytes string ByteString str []byte String(ASCII-8BIT) ByteString

更多protobuf详细信息参考

定义服务

如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol编译器会根据所选择的不同语言生成服务接口代码。例如,想要定义一个RPC服务并具有一个方法,该方法接收SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义:

service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse) {}
}

生成的接口代码作为客户端与服务端的约定,服务端必须实现定义的所有接口方法,客户端直接调用同名方法向服务端发起请求。

posted @ 2022-09-23 14:39  专职  阅读(207)  评论(0编辑  收藏  举报