go实现简单gRPC demo

环境配置

1.# 下载最新版本23.2的protoc,这个是protobuf代码生成工具,通过proto文件生成对应的代码,根据自己操作系统下载相应文件,这里以windows 64位系统为例
wget https://github.com/protocolbuffers/protobuf/releases/download/v23.2/protoc-23.2-win64.zip
# 解压并放在windows本地目录,并配置在Path路径下如D:\Program Files\protoc-23.2-win64\bin,在windows下命令行执行protoc --version检查是否安装配置正确

2.# 创建go项目grpc-demo,在GoLand IDE编写,并通过下面命令安装grpc核心库protoc,可以GoLand IDE安装protoc插件,实现语法高亮
go get google.golang.org/grpc

3.# 在实际开发中最好指定具体的版本,这里是演示使用就直接用latest,在命令行中执行下面两条命令
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

文件结构

1.创建user-service模块

service/users.proto内容:

syntax = "proto3";
option go_package = "../service";

// 服务和方法
service Users {
    rpc GetUser (UserGetRequest) returns (UserGetReply) {}
}

// 请求消息
message UserGetRequest {
    string email = 1;
    int32 id = 2;
}

// 响应消息
message User {
    string id = 1;
    string first_name = 2;
    string last_name = 3;
    int32 age = 4;
}

message UserGetReply {
    User user = 1;
}

2.生成客户端和服务端代码

在service目录下执行以下命令,自动生成pb.go文件

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative users.proto

3.编写服务器

server/server.go内容:

package main

import (
	"context"
	"log"
	"net"
	"os"

	users "testgo/service" // 导入之前生成的包

	"google.golang.org/grpc"
)

// userService类型是Users服务的服务处理程序
type userService struct {
	users.UnimplementedUsersServer // 这个字段对于gRPC中的任何服务实现都是强制性的
}

func (s *userService) GetUser(ctx context.Context, in *users.UserGetRequest) (*users.UserGetReply, error) {
	// 打印客户端传过来的数据
	log.Printf("已接收到邮件地址: %s, 还有ID: %d", in.Email, in.Id)

	// 自定义数据响应给客户端
	u := users.User{
		Id:        "user-782911",
		FirstName: "mike",
		LastName:  "li",
		Age:       22,
	}
	return &users.UserGetReply{User: &u}, nil
}

// 向gRPC服务器注册Users服务
func registerServices(s *grpc.Server) {
	users.RegisterUsersServer(s, &userService{})
}

// 启动gRPC服务器
func startServer(s *grpc.Server, l net.Listener) error {
	return s.Serve(l)
}

func main() {
	listenAddr := os.Getenv("LISTEN_ADDR")

	if len(listenAddr) == 0 {
		listenAddr = ":50051"
	}

	lis, err := net.Listen("tcp", listenAddr)

	if err != nil {
		log.Fatal(err)
	}

	s := grpc.NewServer()
	registerServices(s)

	log.Fatal(startServer(s, lis))
}
  •  在gRPC服务实现中,都需要嵌入一个users.UnimplementedUsersServer字段(在grpc.pb.go中存在该字段),将该字段嵌入到userService的struct中。
  • 实现proto中定义的GetUser接口,接收器为userService的指针类型,在方法内可以直接访问结构体的字段并修改。GetUser接口入参为context和proto定义的request指针,返回值是proto定义的response指针以及error。注意返回的response要按照proto定义的response格式实现。
  • 新建注册user服务方法,调用grpc.pb.go的RegisterUsersServer方法,该方法需要两个参数:一个grpc.Server类型的指针(grpc的服务对象)以及一个实现了UsersServer接口的对象(在这里userService结构体实现了UsersServer接口,它以指针方式传给RegisterUsersServer),RegisterUsersServer会将这个对象注册到gRPC服务中,这样客户端就可以通过这个对象调用定义在接口中的方法了。
  • 新建启动gRPC服务方法,其中入参有两个:s是已经初始化好的grpc.Server对象,l是通过net.Listen获取的监听套接字。Serve方法将grpc服务绑定到监听套接字l上,开始接收连接(阻塞)。
  • main的步骤十分清晰,先获取监听套接字,新建gRPC服务对象,注册服务,启动服务监听请求。

4.编写客户端

package main

import (
	"context"
	"log"
	"os"
	users "testgo/service" // 导入之前生成的包

	"google.golang.org/grpc"
)

// 建立与服务器的连接(通道)
func setupGrpcConnection(addr string) (*grpc.ClientConn, error) {
	return grpc.DialContext(
		context.Background(),
		addr,
		grpc.WithInsecure(),
		grpc.WithBlock(),
	)
}

// 创建客户端与Users服务通信
func getUserServiceClient(conn *grpc.ClientConn) users.UsersClient {
	return users.NewUsersClient(conn)
}

// 调用Users服务中的GetUser()方法
func getUser(client users.UsersClient, u *users.UserGetRequest) (*users.UserGetReply, error) {
	return client.GetUser(context.Background(), u)
}

func main() {
	if len(os.Args) != 2 {
		log.Fatal("缺少gRPC服务器地址")
	}
	conn, err := setupGrpcConnection(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	c := getUserServiceClient(conn)
	result, err := getUser(c, &users.UserGetRequest{
		Email: "mike_li@163.com",
		Id:    8082731,
	})
	if err != nil {
		log.Fatal(err)
	}

	// 打印响应
	log.Printf("收到响应: %s %s %s %d\n", result.User.Id, result.User.FirstName, result.User.LastName, result.User.Age)
}
  •  新建 建立与服务器连接(通道)的方法,这里的入参就是监听的端口号,返回一个*grpc.ClientConn对象。后续Client可以使用这个ClientConn对象来发送RPC请求。这里内部使用grpc.DialContext来创建连接,grpc.WithInsecure() 表示使用不安全连接,即不验证 TLS 证书,grpc.WithBlock() 设置连接是阻塞的。该方法的作用是封装了gRPC客户端连接的创建,提供了一个简单的接口给调用者使用。
  • 在getUserServiceClient方法中,使用返回的ClientConn对象来创建具体的服务客户端,NewUsersClient方法是pb.go文件自动生成的。
  • 在getUser方法中,使用创建好的具体的服务客户端和request调用Users服务中的GetUser()方法。注意此处的GetUser()是服务端实现的remote方法,该方法的定义在grpc.pb.go中。
  • main中步骤较清晰,先获取一个Client连接对象(注意用defer在最后return后关闭该连接),使用该连接对象创建具体的客户端,调用本地getUser()方法(实际调用remote服务端的GetUser()方法),传入client对象以及request信息,获取响应。

5.效果验证

# 1.服务端执行(在server文件夹下):
go run .\server.go

# 2.客户端执行(开启另外一个终端,在client文件夹下):
go run main.go localhost:50051

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值