• 【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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

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

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

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

    在这里插入图片描述

    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() {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    注:

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

    运行结果如下:

    age:18
    
    • 1

    3 总结

    • 先创建proto文件,定义messageservice
    • 根据编译生成的pb.gogrpc.pb.go文件具体实现接口
    • 服务端注册服务并启动,客户端建立连接进行方法调用
  • 相关阅读:
    一文让你彻底搞懂js正则表达式
    NR PDSCH(一)时域资源
    【C++游戏引擎Easy2D】Random随机数,不同于Rand,做游戏必备
    关于写文章怎样才能制作出优质封面?看完这篇博客就够了(数千字手把手教学)
    vLLM-prefix浅析(System Prompt,大模型推理加速)
    Direct3D的初始化
    HTTP协议。(HTTP-概述和特点、HTTP-请求协议、HTTP-请求数据格式、浏览器访问服务器的几种方式)
    【Spring】Spring的JdbcTemplate
    5个写自定义函数小练习
    动态规划 --- 状态压缩DP 详细解释
  • 原文地址:https://blog.csdn.net/weixin_42216109/article/details/125470176