gRPC之gRPC开发流程与入门案例

1、gRPC开发流程

gRPC大致的开发流程如下:

1、定义proto文件,即定义Request和Response结构,以及包含多个方法的服务Service。

2、通过protoc工具生成对应语言的Stub。

3、server实现proto中定义的接口,编写逻辑代码。

4、client通过生成的Stub调用server。

1.1 编写.proto描述文件

Go编写一个获取服务器时间的rpc接口,time.proto文件的内容如下:

syntax = "proto3";
package proto;
option go_package = "./base;base";

service BaseService {
    rpc GetTime (TimeRequest) returns (TimeResponse) {}
}

message TimeRequest {}

message TimeResponse {
    string time = 1;
}

1.2 编译生成.pb.go文件

通过protoc生成服务端以及客户端的Stub代码:

$ protoc --go_out=plugins=grpc:. time.proto

1.3 服务端实现约定的接口并提供服务

编写server.go文件:

package main

import (
	"context"
	pb "demo/base"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
	"net"
	"time"
)

type service struct {
	// time.pb.go中的结构体
	pb.UnimplementedBaseServiceServer
}

func main() {
	listen, err := net.Listen("tcp", ":50051")
	fmt.Println("Listen 50051!")
	if err != nil {
		fmt.Println(err)
	}
	s := grpc.NewServer()
	reflection.Register(s)
	pb.RegisterBaseServiceServer(s, &service{})
	s.Serve(listen)
}

// 实现接口
func (s *service) GetTime(ctx context.Context, in *pb.TimeRequest) (*pb.TimeResponse, error) {
	now := time.Now().Format("2006-01-02 15:04:05")
	return &pb.TimeResponse{Time: now}, nil
}
# 启动server
$ go run server.go
Listen 50051!

1.4 客户端按照约定调用.pb.go文件中的方法请求服务

编写client.go文件:

package main

import (
	"context"
	pb "demo/base"
	"fmt"
	"google.golang.org/grpc"
	"time"
)

func main() {
	conn, err := grpc.Dial(":50051", grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		fmt.Println(err)
	}
	defer conn.Close()
	c := pb.NewBaseServiceClient(conn)
	getTime(c)
}

func getTime(client pb.BaseServiceClient) error {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, _ := client.GetTime(ctx, &pb.TimeRequest{})
	fmt.Println("Time:", r.Time)
	return nil
}
# 启动client
$ go run client.go
Time: 2023-02-10 16:14:01

最终的项目结构:

$ tree demo
demo
├── base
│   └── time.pb.go
├── client.go
├── go.mod
├── go.sum
├── server.go
└── time.proto

1 directory, 6 files

2、gRPC入门案例

按照惯例,这里也从一个Hello项目开始,本项目定义了一个Hello Service,客户端发送包含字符串名字的请求,

服务端返回Hello消息。

2.1 Step1:编写描述文件hello.proto

// 指定proto版本
// 表明使用proto3语法,如果你没有指定这个,编译器会使用proto2语法
syntax = "proto3"; 
// 指定默认包名
package hello;     
// 指定golang包名
option go_package = "./hello";
// 定义Hello服务
service Hello {
    // 定义SayHello方法
    rpc SayHello(HelloRequest) returns (HelloResponse) {}
}
// HelloRequest 请求结构
message HelloRequest {
    string name = 1;
}
// HelloResponse 响应结构
message HelloResponse {
    string message = 1;
}

hello.proto文件中定义了一个Hello Service,该服务包含一个SayHello方法,同时声明了HelloRequest

HelloResponse消息结构用于请求和响应。客户端使用HelloRequest参数调用SayHello方法请求服务端,服

务端响应HelloResponse消息。一个最简单的服务就定义好了。

2.2 Step2:编译生成.pb.go文件

# 编译hello.proto
$ protoc -I . --go_out=plugins=grpc:. ./hello.proto

在当前目录内生成的hello.pb.go文件,按照.proto文件中的说明,包含服务端接口HelloServer描述,客户

端接口及实现HelloClient,及HelloRequestHelloResponse结构体。

2.3 Step3:实现服务端接口server.go

package main

import (
	"context"
	"fmt"
	"log"
	"net"
	// 引入编译生成的包
	pb "demo/hello"
	"google.golang.org/grpc"
)

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

type server struct {
	pb.UnimplementedHelloServer
}

// SayHello 实现Hello服务接口
func (s* server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
	resp := new(pb.HelloResponse)
	resp.Message = fmt.Sprintf("Hello %s.", in.Name)
	return resp, nil
}

func main() {
	listen, err := net.Listen("tcp", Address)
	log.Println("Listen ", Address)
	if err != nil {
		log.Fatalf("Failed to listen: %v", err)
	}
	// 实例化 grpc Server
	s := grpc.NewServer()
	// 注册HelloService
	pb.RegisterHelloServer(s, &server{})
	if err := s.Serve(listen); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

实例化grpc Server并注册HelloService,开始提供服务。

# 启动服务
$ go run server.go
2023/02/10 18:26:39 Listen  :50052

2.4 Step4:实现客户端调用client.go

package main

import (
	pb "demo/hello" // 引入proto包
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"log"
)

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

func main() {
	// 连接
	conn, err := grpc.Dial(Address, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalln(err)
	}
	defer conn.Close()
	// 初始化客户端
	c := pb.NewHelloClient(conn)
	// 调用方法
	req := &pb.HelloRequest{Name: "gRPC"}
	res, err := c.SayHello(context.Background(), req)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(res.Message)
}

客户端初始化连接后直接调用hello.pb.go中实现的SayHello方法,即可向服务端发起请求,使用姿势就像调

用本地方法一样。

# 启动客户端
$ go run client.go
2023/02/10 18:30:08 Hello gRPC.

如果你收到了Hello gRPC的回复,证明该demo成功运行。

# 项目结构
$ tree demo
demo
├── client.go
├── go.mod
├── go.sum
├── hello
│   └── hello.pb.go
├── hello.proto
└── server.go

1 directory, 6 files

你可能感兴趣的:(gRPC,golang)