go grpc(protobuf) 简单使用教程

go gprc 使用 教程

技术栈

grpc
go
protobuff

1.环境

1.1. 安装protoc

项目地址 https://github.com/protocolbuffers/protobuf

下载protobuff, 有条件的github上直接下载,github下载地址,或者可以从maven仓库下载

在maven仓库中找到对应的版本,进行下载,linux ,windows 都有

1589089027209.png

这里我下载 windows 64 位的这个

下载下来的是可执行文件,直接放到环境变量里面,这个文件名太长了,把后缀都去掉

我放的路径 %GOPATH%\bin\protoc.exe

1.2. 安装 protoc-gen-go

直接go get -u github.com/golang/protobuf/protoc-gen-go

go get 的比较慢的话可以用代理。需要配置下代理。

看下 %GOPATH%\bin\ 有没有protoc-gen-go.exe ,没有的话需要找到下载的包进行安装。

下载目录在:%GOPATH%\pkg\mod\github.com\golang\protobuf@xxx\protoc-gen-go

进入目录然后 go install ,然后再去看bin 目录就会生成protoc-gen-go.exe

1589090845985.png

2. 建项目实践

1. 建目录 grpcprj

go mod init grpcprj

这个是我创建后的目录

|   go.mod #工程文件
|   go.sum #工程文件
+---client #客户端代码存放目录
|       main.go 
+---proto #proto 文件存放目录,包括通过protoc 编译后的文件
|   \---hello
|           hello.pb.go #protoc 根据proto文件生成的go项目文件
|           hello.proto #proto 文件
\---server #服务端代码
    |   main.go
    \---handler #服务端接口实现
            hello.go
2. 编写proto文件
syntax="proto3"; //版本

package go.rpc.srv.hello; //作用域

service Hello{ //service 表示服务,在这里面定义接口
 //定义了一个接口SayHello 接收了一个Say消息,返回一个Say消息
 rpc SayHello(Say) returns (Say);  
}

// 定义一个结构体,Say
message Say {
string name = 1; // 定义格式如下 :<修饰符> 类型 字段名 = 唯一编号

//由于一些历史原因,基本数值类型的repeated的字段并没有被尽可能地高效编码。
//在新的代码中,用户应该使用特殊选项[packed=true]来保证更高效的编码。
//注意[packed=true]只能用在 repeated修饰的数字类型中
//repeated用来定义数组
repeated  int32 list =2; //repeated 

//定义map
map maps = 3;

//有时候你需要保留一些你以后要用到的编号或者变量名,使用reserved关键字
reserved 3, 15, 9 to 11; //保留 3,15,9到11
reserved "foo", "bar";

//string var2 = 3;//编译会报错,因为3被保留了
//string var3 = 10;//编译会报错,因为10被保留了
//string foo = 12;//编译会报错,因为foo被保留了
}

消息体可以嵌套,也可以引入其他的proto文件

import "google/protobuf/any.proto"; //引入其他文件

//消息嵌套
message ParentBean{
    ChildA child = 1;
    message ChildB{
        string name = 1;
    } 
    ChildB child2 = 2 ;
}
message ChildA{
    string name = 1;
}

字段类型对比

.proto Type Go Type 说明
double float64
float float32
int32 int32 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代
uint32 uint32 使用变长编码
uint64 uint64 使用变长编码
sint32 int32 使用变长编码,这些编码在负值时比int32高效的多
sint64 int64 使用变长编码,有符号的整型值。编码时比通常的int64高效。
fixed32 uint32 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。
fixed64 uint64 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。
sfixed32 int32 总是4个字节
sfixed64 int64 总是8个字节
bool bool 默认 false
string string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本,默认值为空。
bytes []byte 默认是空数组
3. 编译proto文件

​ proto 文件编写完毕以后,使用protoc 进行编译,编译成成 xxx.pb.go,编译是干什么呢?

protoc --go_out=plugins=grpc:. proto/hello/hello.proto

​ 运行命令后会在 proto 文件同目录生成 xxx.pb.go,对应的service 会成生成一个空的实现(指的是只做了实现,并没有具体业务功能),这里需要注意的是,默认go环境变量已经配置正确,gopath\bin 在环境变量里面。

hello.proto(最简的)

syntax="proto3";

service Hello{
    rpc SyaHello(Say) returns (Say);
}

message Say {
  string name = 1;
}

编译后的文件

...省略了其他代码
// HelloServer is the server API for Hello service.
// helloServer 接口
type HelloServer interface { 
    SyaHello(context.Context, *Say) (*Say, error)
}

// UnimplementedHelloServer can be embedded to have forward compatible implementations.

type UnimplementedHelloServer struct {
}

// 以下是一个空的实现,没有实现任何业务逻辑
func (*UnimplementedHelloServer) SyaHello(context.Context, *Say) (*Say, error) {
    return nil, status.Errorf(codes.Unimplemented, "method SyaHello not implemented")
}
4. 实现服务端接口逻辑

​ 服务端可以实现,也可以不实现,也可以改下上面的默认生成的这个实现类,这里重新实现了下,里面写自己的业务逻辑。

server/handler/hello.go(最简实现)

package handler

import (
    "context"
    "fmt"
    h "grpcprj/proto/hello"
    )

type HelloImpl struct {}

func (hi *HelloImpl)SyaHello(ctx context.Context,in *h.Say) (*h.Say, error){
    msg:=fmt.Sprintf("echo say:%s",in.GetName())
    return &h.Say{Name:msg},nil
}
5. 服务端代码
package main

import (
 "context"
 "google.golang.org/grpc"
 h "grpcprj/proto/hello"
 "log"
 "time"
)

var(
 addr  ="localhost:8081"
 defmsg = "hello boger"
)

func main()  {

 var(
     conn *grpc.ClientConn
     ctx context.Context
     cancel context.CancelFunc
     err error
     r *h.Say
 )

 if conn,err=grpc.Dial(addr,grpc.WithInsecure(),grpc.WithBlock());err!=nil{
     log.Fatalf("conn server err :%v",err)
 }
 defer conn.Close()
 c := h.NewHelloClient(conn)

 ctx,cancel=context.WithTimeout(context.Background(),time.Second)
 defer cancel()
 if r,err=c.SyaHello(ctx,&h.Say{Name:defmsg});err!=nil{
     log.Fatalf("get error :%v",err)
 }
 log.Printf("get msg :%s",r.Name)
}
6. 客户端代码

​ protoc 生成的go 代码里面包括服务端的定义,和客户端的定义(协议的打包,解包都包括了)

​ grpcprj/client/hello.go

package main

import (
    "context"
    "google.golang.org/grpc"
    h "grpcprj/proto/hello"
    "log"
    "time"
)

var(
    addr  ="localhost:8081"
    defmsg = "hello boger"
)

func main()  {

    var(
        conn *grpc.ClientConn
        ctx context.Context
        cancel context.CancelFunc
        err error
        r *h.Say
    )

    if conn,err=grpc.Dial(addr,grpc.WithInsecure(),grpc.WithBlock());err!=nil{
        log.Fatalf("conn server err :%v",err)
    }
    defer conn.Close()
    c := h.NewHelloClient(conn)

    ctx,cancel=context.WithTimeout(context.Background(),time.Second)
    defer cancel()
    if r,err=c.SyaHello(ctx,&h.Say{Name:defmsg});err!=nil{
        log.Fatalf("get error :%v",err)
    }
    log.Printf("get msg :%s",r.Name)
}

你可能感兴趣的:(go grpc(protobuf) 简单使用教程)