0
点赞
收藏
分享

微信扫一扫

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

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

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

下面我们开始正文:

1 扩展后的项目结构

画红圈圈的为新增加的文件
从1开始,扩展Go语言后端业务系统的RPC功能_后端

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

2.1 下载依赖

go get google.golang.org/grpc
go 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

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

解决:

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

4.2 编译proto文件出错2

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

解决:

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

5 小总结

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

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

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


举报

相关推荐

0 条评论