仓库源文站点原文


title: 使用Protobuf3和grpc实现RPC调用 cover: https://img.paulzzh.com/touhou/random?38 date: 2020-05-14 21:16:31 categories: [Golang] toc: true tags: [Golang, Protobuf, grpc]

description: 本文展示了通过Protobuf3定义服务接口并通过grpc实现简单的RPC调用

本文展示了通过Protobuf3定义服务接口并通过grpc实现简单的RPC调用

源代码:

https://github.com/JasonkayZK/Go_Learn/tree/protobuf_grpc_demo

<br/>

<!--more--> <!-- **目录:** --> <!-- toc --> <!-- <br/> -->

使用Protobuf3和grpc实现RPC调用

使用Protobuf是还需要安装protoc和protoc-gen-go以支持通过.proto生成对应的接口;

下面以在windows下为例介绍protoc和protoc-gen-go的安装(protoc底层调用的是protoc-gen-go)

安装protoc和protoc-gen-go

protoc

Github的Release下载:

https://github.com/protocolbuffers/protobuf/releases

下载对应平台的压缩包(win/Linux);

解压之后将bin目录下的protoc复制到系统环境变量的路径下:

等等;

然后测试:

protoc --version
libprotoc 3.12.0

protoc-gen-go

Github的Release下载:

https://github.com/golang/protobuf/releases

下载压缩包并解压;

解压后在protoc-gen-go目录下编译:

go build

然后将生成的二进制文件protoc-gen-go复制到系统环境变量的路径下

全部安装完成!

<br/>

目录结构

tree /F                                       
.                                             
│  go.mod                                       
│  go.sum                                       
│                                               
├─hello_client                                  
│      main.go                                  
│                                               
├─hello_server                                  
│      main.go                                  
│                                               
└─proto                                         
        hello.pb.go                             
        hello.proto

<br/>

编写proto

本例子使用proto3语法;

hello.proto

// 指定proto版本
syntax = "proto3";

package proto;

// 定义请求结构
message HelloRequest{
    string name = 1;
}

// 定义响应结构
message HelloResponse{
    string message = 1;
}

// 定义Hello服务
service Hello{
    // 定义服务中的方法
    rpc SayHello (HelloRequest) returns (HelloResponse);
}

编写完成之后使用protoc生成.pb.gp文件:

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

即可生成hello.pb.go文件

<br/>

初始化gomod项目

使用go mod init protobuf_grpc_demo初始化项目;

生成go.mod文件;

然后使用go mod tidy初始化;

<br/>

服务端代码

hello_server/main.go

package main

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

const (
   // gRPC服务地址
   Address = "127.0.0.1:50052"
)

type helloService struct{}

// 定义服务接口的SayHello的实现方法
func (s helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
   resp := new(pb.HelloResponse)
   fmt.Printf("Get remote call from client, the context is: %s\n\n", ctx)

   resp.Message = "hello " + in.Name + "."
   fmt.Printf("Response msg: " + resp.Message)

   return resp, nil
}

var HelloService = helloService{}

func main() {
   listen, err := net.Listen("tcp", Address)
   if err != nil {
      fmt.Printf("Failed to listen: %v", err)
   }

   // 实现gRPC Server
   s := grpc.NewServer()

   // 注册helloServer为客户端提供服务
   // 内部调用了s.RegisterServer()
   pb.RegisterHelloServer(s, HelloService)

   println("Listen on: " + Address)

   _ = s.Serve(listen)
}

<br/>

客户端代码

package main

import (
   "context"
   "fmt"
   "google.golang.org/grpc"
   pb "protobuf_grpc_demo/proto"
)

const (
   Address = "127.0.0.1:50052"
)

func main() {
   // 连接gRPC服务器
   conn, err := grpc.Dial(Address, grpc.WithInsecure())
   if err != nil {
      fmt.Printf("Failed to dial to: " + Address)
   }
   if conn == nil {
      panic("Failed to get connection from the server")
   }
   defer conn.Close()

   // 初始化客户端
   c := pb.NewHelloClient(conn)

   // 调用方法
   reqBody := new(pb.HelloRequest)
   reqBody.Name = "gRPC"
   r, err := c.SayHello(context.Background(), reqBody)
   if err != nil {
      fmt.Printf("Fail to call method, err: %v", err)
   }

   fmt.Println(r.Message)
}

<br/>

运行测试

# 服务端运行:
D:\workspace\go_learn>go run hello_server/main.go
Listen on: 127.0.0.1:50052

# 客户端运行:
D:\workspace\go_learn>go run hello_client/main.go
hello gRPC.

# 并且在服务器返回
Get remote call from client, the context is: context.Background.WithCancel.WithValue(type peer.peerKey, val <not Stri
nger>).WithValue(type metadata.mdIncomingKey, val <not Stringer>).WithValue(type grpc.streamKey, val <not Stringer>)

Response msg: hello gRPC.

<br/>

附录

源代码:

https://github.com/JasonkayZK/Go_Learn/tree/protobuf_grpc_demo

如果觉得文章写的不错, 可以关注微信公众号: Coder张小凯

内容和博客同步更新~

<br/>