【Golang | gRPC】使用gRPC实现简单远程调用

环境:
Golang: go1.18.2 windows/amd64
grpc: v1.47.0
protobuf: v1.28.0

完整代码:
https://github.com/WanshanTian/GolangLearning
cd GolangLearning/RPC/gRPC

1. 简介

gRPC是一个基于C/S架构,使用protobuf作为传输协议进行远程过程调用的高性能框架,前文【Golang | gRPC】protocol buffer compiler\protoc的安装【Golang | gRPC】使用protoc编译.proto文件分别就protoc编译工具的安装和使用进行了详细的说明,下面通过一个demo具体说明gRPC的简单使用

2. 实践

现有下面一种场景:服务端保存着用户的年龄信息,客户端输入姓名,经RPC后获得对应的年龄

2.1 proto文件

2.1.1 新建gRPC文件夹,使用go mod init初始化,创建pb文件夹,新建query.proto文件

syntax = "proto3";
package pb;
option go_package= ".;pb";

// 定义查询服务包含的方法
service Query {
  rpc GetAge (userInfo) returns (ageInfo) {}
}

// 请求用的结构体,包含一个name字段
message userInfo {
  string name = 1;
}

// 响应用的结构体,包含一个age字段
message ageInfo {
  int32 age = 1;
}

服务端实现一个查询(Query)服务,包含一个方法GetAge;从Golang层面理解,其实就是一个Query接口,实现了一个GetAge方法

2.1.2 在.\gRPC\pb目录下使用protoc工具进行编译,在pb文件夹下直接生成.pb.go_grpc.pb.go文件

protoc --go_out=./ --go-grpc_out=./ *.proto

在这里插入图片描述

2.2 pb.go和grpc.pb.go文件

2.2.1 查看query_grpc.pb.go中生成的关于QueryServer的定义

type QueryServer interface {
	GetAge(context.Context, *UserInfo) (*AgeInfo, error)
	mustEmbedUnimplementedQueryServer()
}

// UnimplementedQueryServer must be embedded to have forward compatible implementations.
type UnimplementedQueryServer struct {
}

func (UnimplementedQueryServer) GetAge(context.Context, *UserInfo) (*AgeInfo, error) {
	return nil, status.Errorf(codes.Unimplemented, "method GetAge not implemented")
}
func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}

QueryServer就是根据proto文件中定义的service Query{}生成的接口,GetAge是我们定义的方法,mustEmbedUnimplementedQueryServer()这个方法是protoc自行编译出的(https://github.com/grpc/grpc-go/issues/3669这里有详细的讨论,在通过protoc编译时,也可以加参数取消这个方法,protoc --go_out=./ --go-grpc_out=require_unimplemented_servers=false:./ *.proto)

2.2.2 查看pb.go中关于UserInfoAgeInfo的定义

type UserInfo struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *UserInfo) GetName() string {
	if x != nil {
		return x.Name
	}
	return ""
}
type AgeInfo struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Age int32 `protobuf:"varint,1,opt,name=age,proto3" json:"age,omitempty"`
}
func (x *AgeInfo) GetAge() int32 {
	if x != nil {
		return x.Age
	}
	return 0
}

UserInfoAgeInfo结构体的前三个字段暂且用不到,通过方法Get...可以得到自定义字段的值

2.3 服务端

在gRPC目录下新建Server文件夹,新建main.go文件
2.3.1 下面我们通过Query这个结构体具体实现QueryServer接口

var userinfo = map[string]int32{
	"foo": 18,
	"bar": 20,
}
// Query 实现了QueryServer接口
type Query struct {
	pb.UnimplementedQueryServer // 通过结构体嵌套的方式默认实现mustEmbedUnimplementedQueryServer()这个方法,查看2.2.1
}

func (q *Query) GetAge(ctx context.Context, info *pb.UserInfo) (*pb.AgeInfo, error) {
	age := userinfo[info.GetName()]
	var res = new(pb.AgeInfo)
	res.Age = age
	return res, nil
}

2.3.2 服务注册并启动

func main() {
	// 创建socket监听器
	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		log.Panic(err)
	}
	// new一个gRPC服务器,用来注册服务
	grpcserver := grpc.NewServer()
	// 注册服务方法
	pb.RegisterQueryServer(grpcserver, new(Query))
	// 开启gRPC服务
	err = grpcserver.Serve(listener)
	if err != nil {
		log.Panic(err)
	}
}

使用RegisterQueryServer这个方法向gRPC服务器里注册服务。这里有两个服务,很容易概念搞混,一个是gRPC服务(用来监听,完成一些网络、路由等工作),一个是业务层面的服务(这些服务可以理解为包含一定方法的接口,用来给客户端进行调用)

2.4 客户端

在gRPC目录下新建Client文件夹,新建main.go文件
2.4.1 先建立无认证的连接,生成Client,然后进行方法调用

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
	"wanshantian/grpc/pb"
)

func main() {
	//建立无认证的连接
	conn, err := grpc.Dial(":1234", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Panic(err)
	}
	defer conn.Close()
	client := pb.NewQueryClient(conn)
	//RPC方法调用
	age, _ := client.GetAge(context.Background(), &pb.UserInfo{Name: "foo"})
	fmt.Println(age)
}

注:

  • 使用grpc.WithTransportCredentials(insecure.NewCredentials())建立无认证的连接
  • 使用client := pb.NewQueryClient(conn)生成gRPC客户端

运行结果如下:

age:18

3 总结

  • 先创建proto文件,定义messageservice
  • 根据编译生成的pb.gogrpc.pb.go文件具体实现接口
  • 服务端注册服务并启动,客户端建立连接进行方法调用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

田土豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值