写点什么

从 1 开始,扩展 Go 语言后端业务系统的 RPC 功能

作者:Barry Yan
  • 2022-11-03
    北京
  • 本文字数:2824 字

    阅读完需:约 9 分钟

从1开始,扩展Go语言后端业务系统的RPC功能

这次我们接上一篇文章《从0开始,用Go语言搭建一个简单的后端业务系统》,利用 Google 开源的 RPC 框架—gRPC 来进行对接口的 RPC 功能横向扩展,也就是用 RPC 协议将 restful 的 HTTP 协议重写一遍。


那么对于 gRPC 不是很了解的同学可以尝试着学一下我之前写过的这篇文章:《Go使用grpc协议通信实战》,以及《一文带你搞懂HTTP和RPC协议的异同》,相信有了这些知识作为铺垫之后,了解 gRPC 的使用和原理就能更加的顺理成章。


下面我们开始正文:

1 扩展后的项目结构

画红圈圈的为新增加的文件


2 RPC 相关主要代码和编译流程

2.1 下载依赖

go get google.golang.org/grpcgo get google.golang.org/protobuf
复制代码

2.2 代码

proto 文件:


syntax = "proto3";  //指定语法格式package proto;  //指定生成的 xx.pb.go 的包名字;
option go_package = "../proto"; //go语言下指定包的名称
//service接口service NumInfoService { //rpc接口中的方法 rpc GetNumInfoById(InfoRequest) returns (InfoResponse){} rpc AddNumByKey(InfoRequest) returns (InfoResponse){} rpc FindNumInfoByKey(InfoRequest) returns (InfoResponse){} rpc SaveNumInfo(InfoRequest) returns (InfoResponse){} rpc DeleteById(InfoRequest) returns (InfoResponse){} rpc FindAll(InfoRequest) returns (InfoResponse){}}
//请求的数据格式 message 对应生成的代码中的struct,[修饰符] 类型 字段名 = 标识符message InfoRequest{ int64 id = 1; string name = 2 ; string info_key = 3 ; int64 info_num = 4;}
message InfoResponse{ int64 code = 1; string msg = 2; int64 count = 3; string data = 4;}
复制代码


接口实现:


package impl
import ( "context" "count_num/pkg/dao/impl" "count_num/pkg/entity" "count_num/proto" "encoding/json")
type NumInfoRPCImpl struct { dao *impl.CountNumDAOImpl}
func NewNumInfoControllerImpl() *NumInfoRPCImpl { return &NumInfoRPCImpl{dao: impl.NewCountNumDAOImpl()}}
func (impl *NumInfoRPCImpl) AddNumByKey(ctx context.Context, request *proto.InfoRequest) (*proto.InfoResponse, error) { key := request.GetInfoKey() id := request.GetId() name := request.GetName() num := request.GetInfoNum() impl.dao.UpdateNumInfoByKey(ctx, entity.NumInfo{ id, name, key, num, }) return &proto.InfoResponse{Code: 0, Msg: "", Count: 1, Data: "true"}, nil}
func (impl *NumInfoRPCImpl) FindNumInfoByKey(ctx context.Context, request *proto.InfoRequest) (*proto.InfoResponse, error) { key := request.GetInfoKey() numInfo := impl.dao.GetNumInfoByKey(ctx, key) info, _ := json.Marshal(numInfo) return &proto.InfoResponse{Code: 0, Msg: "", Count: 1, Data: string(info)}, nil}
func (impl *NumInfoRPCImpl) SaveNumInfo(ctx context.Context, request *proto.InfoRequest) (*proto.InfoResponse, error) { key := request.GetInfoKey() id := request.GetId() name := request.GetName() num := request.GetInfoNum() impl.dao.AddNumInfo(ctx, entity.NumInfo{ id, name, key, num, }) return &proto.InfoResponse{Code: 0, Msg: "", Count: 1, Data: "true"}, nil}
func (impl *NumInfoRPCImpl) DeleteById(ctx context.Context, request *proto.InfoRequest) (*proto.InfoResponse, error) { id := request.GetId() impl.dao.DeleteNumInfoById(ctx, id) return &proto.InfoResponse{Code: 0, Msg: "", Count: 1, Data: "true"}, nil}
func (impl *NumInfoRPCImpl) FindAll(ctx context.Context, request *proto.InfoRequest) (*proto.InfoResponse, error) { numInfos := impl.dao.FindAllNumInfo(ctx) infos, _ := json.Marshal(numInfos) return &proto.InfoResponse{Code: 0, Msg: "", Count: 1, Data: string(infos)}, nil}
func (impl *NumInfoRPCImpl) GetNumInfoById(ctx context.Context, req *proto.InfoRequest) (resp *proto.InfoResponse, err error) { id := req.GetId() numInfo := impl.dao.GetNumInfoById(ctx, id) info, _ := json.Marshal(numInfo) return &proto.InfoResponse{Code: 0, Msg: "", Count: 1, Data: string(info)}, nil}
复制代码


启动函数:


package rpc
import ( "count_num/pkg/rpc/impl" "count_num/proto" "fmt" "google.golang.org/grpc" "log" "net")
func RunGRPC() { //1 添加监听的端口 port := ":6666" l, err := net.Listen("tcp", port) if err != nil { log.Fatalf("端口监听错误 : %v\n", err) } fmt.Printf("正在监听: %s 端口\n", port) //2 启动grpc服务 s := grpc.NewServer() //3 将服务注册到gRPC中 ,注意第二个参数是接口类型的变量,需要取地址传参 proto.RegisterNumInfoServiceServer(s, impl.NewNumInfoControllerImpl()) s.Serve(l)}
复制代码


cmd:


package main
import "count_num/pkg/rpc"
func main() { rpc.RunGRPC()}
复制代码

2.3 编译生成 gRPC 相关代码

protoc -I . --go_out=plugins=grpc:. count_num.proto
复制代码

3 RPC 测试代码

package main
import ( "context" "count_num/proto" "fmt" "google.golang.org/grpc" "log")
func main() { //1 配置grpc服务端的端口作为客户端的监听 conn, err := grpc.Dial(":6666", grpc.WithInsecure()) if err != nil { log.Fatalf("正在监听服务端 : %v\n", err) } defer conn.Close() //2 实例化 UserInfoService 服务的客户端 client := proto.NewNumInfoServiceClient(conn) //3 调用grpc服务 req := new(proto.InfoRequest) req.Id = 20 resp, err := client.FindAll(context.Background(), req) if err != nil { log.Fatalf("请求错误 : %v\n", err) } fmt.Printf("响应内容 : %v\n", resp)}
复制代码

4 遇见的问题和排查方式以及注意点

4.1 编译 proto 文件出错 1


解决:


4.2 编译 proto 文件出错 2


解决:


5 小总结

在系统开发中,一般需要对外提供接口时,因为普适性,HTTP 是首选,而在同一个组织或公司内部进行不同系统间服务的提供时,面向服务封装的 RPC 更具有竞争力,可以针对服务进行可用性和效率的优化,因此 HTTP 和 RPC 不同的网络通信协议各自具有更擅长的领域。


当然在本项目中,gRPC 的使用在返回值处理上针对结构体类型时使用的是 json 格式字符串,在真实的项目中可以使用字节数组进行代替可更加高效,但是规范也会随之更加严格。


好了,今天的分享就到这里~


源码获取方式:关注公众号[ 扯编程的淡 ],回复:0615

发布于: 刚刚阅读数: 4
用户头像

Barry Yan

关注

做兴趣使然的Hero 2021-01-14 加入

Just do it.

评论

发布
暂无评论
从1开始,扩展Go语言后端业务系统的RPC功能_后端_Barry Yan_InfoQ写作社区