创建一套完整的 gRPC 应用 ( 基于 goLang )

本节将带您体验基于 go 语言创建一套完整的 gRPC 应用。
本节以一个文章服务为例,为您演示 grpc 的完整实现流程。

环境准备

请提前安装相关工具包

go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

1. 创建项目文件夹

如 : D:\grpcDemo,然后初始化 go mod :

go mod init grpcdemo

2. 创建 proto 文件

文件位置 : /项目根目录/pbfiles/article.proto

编辑如下代码

syntax = "proto3";

option go_package = "../services";

// 定义Article服务
service Article {
  // 定义方法
  rpc GetInfo (ArticleRequest) returns (ArticleReply) {}
  // 可以定义多个方法
}

// 请求数据结构
message ArticleRequest {
  int32 Id = 1;
}

// 响应数据结构
message ArticleReply {
    int32 Id = 1;
    string Title = 2;
    string Content = 3;
}

3. 生成 grpc 服务相关代码

执行如下命令 :

cd .\pbfiles
protoc --go_out=../services --go_opt=paths=source_relative --go-grpc_out=../services --go-grpc_opt=paths=source_relative ./article.proto

4. 安装依赖

cd ../
go mod tidy

5. 创建服务

在项目根目录下创建  service.go , 编写如下代码 :

package main

import (
	"context"
	"flag"
	"fmt"
	"grpcdemo/services"
	"log"
	"net"

	"google.golang.org/grpc"
)

var (
	port = flag.Int("port", 8008, "The server port")
)

// 定义服务主体
type server struct {
	services.UnimplementedArticleServer
}

// 实现获取文章接口
func (s *server) GetInfo(ctx context.Context, in *services.ArticleRequest) (*services.ArticleReply, error) {
	log.Printf("Received: %v", in.GetId())
	return &services.ArticleReply{
		Id:      in.GetId(),
		Title:   "grpc",
		Content: "grpc is a good framework",
	}, nil
}

func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	services.RegisterArticleServer(s, &server{})
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

6. 启动服务

go run service.go

## ... server listening at [::]:8008

7. 编写客户端测试代码

文件 :./client_test.go

package main

import (
	"context"
	"grpcdemo/services"
	"testing"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func TestGetInfo(t *testing.T) {
	conn, err := grpc.Dial("localhost:8008", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()

	// 调用服务
	client := services.NewArticleClient(conn)
	res, err := client.GetInfo(context.Background(), &services.ArticleRequest{Id: 1})
	if err != nil {
		t.Fatal(err)
	}
	t.Log(res)
}

8. 测试

go test -v -run TestGetInfo
=== RUN   TestGetInfo
    client_test.go:25: Id:1  Title:"grpc"  Content:"grpc is a good framework"
--- PASS: TestGetInfo (0.01s)
PASS