跳到主要内容

第一个gRPC示例

使用gRPC开发的3个步骤

  1. 编写protobuf文件
  2. 生成代码【包括服务端和客户端】
  3. 编写业务逻辑,会使用第二步生成的代码

示例:第一步编写proto文件

我们新建一个hello_server项目,一般我们创建的protobuf文件存储在pb或者proto文件夹下,然后我们还需要使用mod来初始化一个项目,在pb文件下新建一个hello.proto

syntax = "proto3"; // 版本声明

// 生成的代码之后引入路径
option go_package = "hello_server/pb";

// proto 文件模块
package pb;

// 定义消息
message HelloRequest {
string name = 1; // 字段类型 字段名称 序号
}

message HelloResponse {
string replay = 1;
}

// 定义服务
service Greeter {
// 对外提供的方法
rpc SayHello(HelloRequest) returns (HelloResponse){}
}

示例:第二步,生成代码【服务端】

在项目根目录下执行以下命令,根据hello.proto生成go源码文件

protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pb/hello.proto
  • --go_out:指定生成的go源码文件到当前项目目录
  • --go_opt:使用的模式是使用的相对路径的模式,这里即相对于pb目录下
  • --go-grpc_out:生成的gRPC的源码文件目录
  • --go-grpc_opt:相对路径模式
注意

如果你的终端不支持\,你就写成一行然后再进行赋值。

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative pb/hello.proto
├── go.mod
└── pb
├── hello.pb.go
├── hello.proto
└── hello_grpc.pb.go

示例:第三步编写业务逻辑

需要使用到生产的代码的时候,我们得先下载一下依赖

go mod tidy

我们下面的步骤就是编写业务代码,我们需要创建一个main.go,我们通常需要自己再定义一个服务结构体,去实现第二步生成的代码中的方法,最后使用netgRPC来启动和注册服务。

package main

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

// 启动一个 gRPC 服务

type server struct {
pb.UnimplementedGreeterServer // 没有实现的服务的结构体
// ... 其他的字段
}

// SayHello 使用我们自己的结构体去实现 SayHello 方法
// 这个方法是我们对外提供的服务
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
reply := "hello " + in.Name
return &pb.HelloResponse{Replay: reply}, nil
}

func main() {
// 本地启动服务
l, err := net.Listen("tcp", ":8972")
if err != nil {
fmt.Printf("failed to listen, :err:%v\n", err)
return
}
// 启动rpc服务,创建gRPC服务
s := grpc.NewServer()
// 注册服务
pb.RegisterGreeterServer(s, &server{})
// 启动服务
err = s.Serve(l)
if err != nil {
fmt.Printf("failed to server, err:%v\n", err)
return
}
}

编写客户端

再新建一个hello_client项目,同样也需要使用mod进行初始化

go mod init hello_client

为了便携效果,直接把服务端的pb文件和生成的代码文件直接拷贝过来即可,因为你客户端需要调用服务端的代码,基本得一样,只是proto文件里的go_package可能需要小小变动一下,你可以自己再重新生成代码。

注意
  • proto文件中的package pb;必须和server端一致
  • option go_package = "hello_client/pb";这里的hello_client视你go mod init的时候的名称是什么,这里就是什么

继续生成客户端的代码

protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pb/hello.proto

编写客户端逻辑

package main

import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"hello_client/pb"
"log"
"time"
)

// grpc 客户端
// 调用 server 端的 SayHello 方法

func main() {
// 连接服务端
conn, err := grpc.Dial("127.0.0.1:8972", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("grpc.Dail failed, err: %v\n", err)
}
defer conn.Close()
// 创建客户端
client := pb.NewGreeterClient(conn)
// 调用rpc方法
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
name := "无解"
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Printf("client.SayHello faield, err:%v\n", err)
}
log.Printf("resp: %v\n", resp.Replay)
log.Printf("resp: %v\n", resp.GetReplay())
}


使用命令行参数解析来动态传参

package main

import (
"context"
"flag"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"hello_client/pb"
"log"
"time"
)

// grpc 客户端
// 调用 server 端的 SayHello 方法

var name = flag.String("name", "无解", "通过-name告诉服务端你是谁")

func main() {
// 解析命令行参数
flag.Parse()
// 连接服务端
conn, err := grpc.Dial("127.0.0.1:8972", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("grpc.Dail failed, err: %v\n", err)
}
defer conn.Close()
// 创建客户端
client := pb.NewGreeterClient(conn)
// 调用rpc方法
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Printf("client.SayHello faield, err:%v\n", err)
}
log.Printf("resp: %v\n", resp.Replay)
log.Printf("resp: %v\n", resp.GetReplay())
}

➜  hello_client ./hello_client -name 哈哈哈  
2022/10/26 21:46:55 resp: hello 哈哈哈
2022/10/26 21:46:55 resp: hello 哈哈哈