【GO】protobuf在golang中的测试用例

上篇文章介绍了如何安装protobuf环境,文章链接如下

【Go】protobuf介绍及安装-CSDN博客

本节介绍protobuf在gRPC中具体如何使用,并编写测试用例

一、Protobuf是如何工作的

  .proto文件是protobuf一个重要的文件,它定义了需要序列化数据的结构,当protobuf编译器(protoc)来运行.proto文件时候,编译器将生成所选择的语言的代码,比如你选择go语言,那么就会将.proto转换成对应的go语言代码,对于go来说,编译器会为每个消息类型生成一个pd.go文件,而C++会生成一个.h文件和一个.cc文件。

  使用protobuf的3个步骤是:

    1. 在.proto文件中定义消息格式。

    2. 用protobuf编译器编译.proto文件。

    3. 用C++/Java/go等对应的protobuf API来写或者读消息。

二、Protobuf代码测试

在开始代码编写与测试之前,把官网的链接分享给大家,这个看完可以避坑,尤其是版本,示例代码,proto文件格式等。

工具安装及demo测试:Quick start | Go | gRPC

1.定义proto文件

syntax="proto3";
option go_package="./;student"; //关于最后生成的go文件是处在哪个目录哪个包中,.代表在当前目录生成,student代表了生成的go文件的包名是studentservice DemoService {rpc Sender(StudentRequest) returns (StudentResponse){}
}message StudentRequest {string Id = 1;
}message StudentResponse {string result =1;
}message Student {int64 Id = 1; //idstring Name =2; //姓名string No =3; //学号
}

2.生成代码

进入proto文件所在目录,cd ~/sourceCode/go/goproject01/src/day34/grpc/proto

<1>执行protoc --go_out=. student.proto

protoc --go_out=. student.proto

执行后发现proto目录生成了一个文件:student.pb.go

<2>执行protoc --go-grpc_out=. student.proto,发现命令执行报错如下

cd ~/sourceCode/go/goproject01/src/day34/grpc/proto
protoc --go-grpc_out=. student.proto 
protoc-gen-go-grpc: program not found or is not executable
Please specify a program using absolute path or make sure the program is available in your PATH system variable
--go-grpc_out: protoc-gen-go-grpc: Plugin failed with status code 1.

执行报错,发现没有安装protoc-gen-go-grpc,需要安装一下

先执行go get

go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
go: downloading google.golang.org/grpc v1.59.0
go: downloading google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
go: downloading google.golang.org/protobuf v1.28.1
go: added google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
go: added google.golang.org/protobuf v1.28.1

再执行go install

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

执行完在$GOBIN目录下生成protoc-gen-go-grpc,源码对应在pkg下

再次执行protoc --go-grpc_out=. student.proto

protoc --go-grpc_out=. student.proto

执行后会在当前目录生成一文件:student_grpc.pb.go

<3>执行go mod tidy

打开文件发现依赖的包没有导入,会报错,需要执行一下最小化导入包依赖

go mod tidy
go: finding module for package google.golang.org/grpc
go: finding module for package google.golang.org/grpc/status
go: finding module for package google.golang.org/grpc/codes
go: found google.golang.org/grpc in google.golang.org/grpc v1.59.0
go: found google.golang.org/grpc/codes in google.golang.org/grpc v1.59.0
go: found google.golang.org/grpc/status in google.golang.org/grpc v1.59.0
go: downloading google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d
go: downloading golang.org/x/text v0.12.0

执行后生成的代码编译通过,不再报错。

3.编写Server端程序

在server包下创建server.go文件

package mainimport ("context""encoding/json""errors""google.golang.org/grpc""google.golang.org/grpc/keepalive"student "goproject01/day34/grpc/proto""log""net""strconv""time"
)// grpc生成源码后多了一个方法mustEmbedUnimplementedDemoServiceServer
// 这个方法首字母小写不允许重载,自定义实现却没法实现该方法,解决方法如下
/**
1,生成代码时候使用选项:
protoc --go_out=. **--go-grpc_opt=require_unimplemented_servers=false** --go-grpc_out=. proto/*.proto
This works, but your binary will fail to compile if you add methods to your service(s) and regenerate/recompile.
That is why we have the embedding requirement by default. We recommend against using this option.
We recommend against using this option(不推荐使用此选项)2,使用内嵌的结构体定义
// server is used to implement helloworld.GreeterServer.
type server struct{
// Embed the unimplemented server
helloworld.UnimplementedGreeterServer
}*/
type MyDemeServiceImpl struct {student.UnimplementedDemoServiceServer
}func (ds *MyDemeServiceImpl) Sender(ctx context.Context, in *student.StudentRequest) (*student.StudentResponse, error) {return handSendMessage(ctx, in)
}func main() {//绑定9091端口listener, err := net.Listen("tcp", ":10005")if err != nil {log.Fatalf("bingding port:9091 error:%v", err)}//注册服务//这个连接最大的空闲时间,超过就释放,解决proxy等到网络问题(不通知grpc的client和server)/**func NewGrpcServer(opts ...grpc.ServerOption) *grpc.Server {var options []grpc.ServerOptionoptions = append(options,grpc.KeepaliveParams(keepalive.ServerParameters{Time:    10 * time.Second, // wait time before ping if no activityTimeout: 20 * time.Second, // ping timeout}),grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{MinTime:             60 * time.Second, // min time a client should wait before sending a pingPermitWithoutStream: true,}),grpc.MaxRecvMsgSize(Max_Message_Size),grpc.MaxSendMsgSize(Max_Message_Size),)for _, opt := range opts {if opt != nil {options = append(options, opt)}}return grpc.NewServer(options...)}*/option1 := grpc.KeepaliveParams(keepalive.ServerParameters{MaxConnectionIdle: 5 * time.Minute})option2 := grpc.MaxSendMsgSize(409600) //400kBoption3 := grpc.MaxRecvMsgSize(409600)grpcServer := grpc.NewServer(option1, option2, option3)//impliServer := student.UnimplementedDemoServiceServer{}var impliServer = &MyDemeServiceImpl{}student.RegisterDemoServiceServer(grpcServer, impliServer)log.Printf("server listening at %v", listener.Addr())/*错误的写成http了,导致排查半天err = http.Serve(listener, nil)if err != nil {log.Fatalf("http serve fail:%v", err)}*/if err := grpcServer.Serve(listener); err != nil {panic("error building server: " + err.Error())}
}func handSendMessage(ctx context.Context, req *student.StudentRequest) (*student.StudentResponse, error) {log.Println("receive param=", req.GetId())//模拟根据id查询student对象并构建一个student实例sid := req.GetId()if sid == "" {log.Println("request param id is null")return nil, errors.New("request param id is null")}resp := &student.StudentResponse{}sidInt64, err := strconv.ParseInt(sid, 10, 64)if err != nil {log.Printf("sid:%s covert to int64 error", sid)return nil, errors.New("sid covert to int64 error")}//通过proto进行序列化对象,和原始json以及easyJson使用方法类似s := &student.Student{Name: "xiaoliu", No: "10001", Id: sidInt64}//bytes, errs := proto.Marshal(s) //需要一个指针类型对象bytes, errs := json.Marshal(s)if errs != nil {log.Println("student obj convert to json error")return nil, errors.New("student obj convert to json error")}resp.Result = byteslog.Println("返回客户端序列化字符串:", string(bytes))return resp, nil
}

4.编写客户端程序

package mainimport ("context""flag""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"student "goproject01/day34/grpc/proto""log""time"
)const (defaultName = "world"defaultId   = "10001"
)var (address = flag.String("address", "localhost:10005", "the address connect to ")name    = flag.String("name", defaultName, " name to great")id      = flag.String("id", defaultId, "id send to server")
)func main() {flag.Parse()connection, err := grpc.Dial(*address, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf("connect localhost:9091 fail:%v\n", err)}defer connection.Close()client := student.NewDemoServiceClient(connection)ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)defer cancel()resp, errs := client.Sender(ctx, &student.StudentRequest{Id: *id})if errs != nil {log.Fatalf("client call server Sender method fail:%v\n", errs)}//获取StudentResponse result的内容rst := string(resp.GetResult())log.Println("rpc returns result:", rst)}

5.代码测试

<1>启动服务端程序

go run server.go
//启动后打开服务端端口,等待客户端连接日志
2023/12/04 18:24:17 server listening at [::]:10005//启动后接收客户端的参数打印
2023/12/04 18:24:25 receive param= 10001
2023/12/04 18:24:25 返回客户端序列化字符串: {"Id":10001,"Name":"xiaoliu","No":"10001"}

<2>运行客户端程序

go run client.go

首次执行发现报错如下:

rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: http2: frame too large"

错误解决:自己误把grpc协议写为http,修改代码即可:

/*错误的写成http了,导致排查半天err = http.Serve(listener, nil)if err != nil {log.Fatalf("http serve fail:%v", err)}*/if err := grpcServer.Serve(listener); err != nil {panic("error building server: " + err.Error())}

再次执行报错如下:

2023/12/04 18:09:55 client call server Sender method fail:rpc error: code = Internal desc = grpc: error while marshaling: string field contains invalid UTF-8
错误解决:需要修改student.proto文件中StudentResponse的result字段为bytes类型,用来支持utf-8字符。将student.proto文件修改如下:上面的server.go,client.go最终以这个proto文件为准。

syntax="proto3";
option go_package="./;student"; //关于最后生成的go文件是处在哪个目录哪个包中,.代表在当前目录生成,student代表了生成的go文件的包名是studentservice DemoService {rpc Sender(StudentRequest) returns (StudentResponse){}
}message StudentRequest {string Id = 1;
}message StudentResponse {bytes result =1; //涉及到utf-8编码的字符需要使用bytes类型
}message Student {int64 Id = 1; string Name =2;string No =3;
}

修改后运行客户端程序:

调用服务端获取序列化的结果如下

go run client.go
2023/12/04 18:24:25 rpc returns result: {"Id":10001,"Name":"xiaoliu","No":"10001"}

6. gRPC官网文档

这里go官网提供使用gRPC开发步骤

Quick start | Go | gRPC

Basics tutorial | Go | gRPC

<1>hellworld测试:examples/helloworld

<2>route_guide测试:examples/route_guide

以上两个测试程序官网都有对应的文档,按照步骤测试执行即可。

7. 补充protobuf定义的数据类型

####

参考资料

gRPC介绍:​​​​​​Basics tutorial | Go | gRPC

Server到Client数据发送过程解析:gRPC 源码分析(四): gRPC server 中 frame 的处理 - 掘金

使用go实现gRPC:墨滴社区

HTTP/2:RFC7540:RFC 7540/7541: Hypertext Transfer Protocol Version 2 (HTTP/2)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/198526.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业微信配置可信域名

首先去申请一个域名&#xff0c;然后将域名绑定到有公网ip的云服务器上&#xff0c;绑定到具体的网站&#xff1b;然后再企业微信&#xff0c;管理后台&#xff0c;点击具体的应用&#xff0c;进【网页授权及JS-SDK】&#xff1b;点击底部的【申请校验域名】点击下载文件&#…

postgresql pg_hba.conf 配置详解

配置文件之pg_hba.conf介绍 该文件用于控制访问安全性&#xff0c;管理客户端对于PostgreSQL服务器的访问权限&#xff0c;内容包括&#xff1a;允许哪些用户连接到哪个数据库&#xff0c;允许哪些IP或者哪个网段的IP连接到本服务器&#xff0c;以及指定连接时使用的身份验证模…

第73讲:深入理解MySQL数据库InnoDB存储引擎:内存结构、磁盘结构与后台线程全面解析

文章目录 1.InnoDB存储引擎的架构2.InnoDB存储引擎的内存结构2.1.Buffer Pool缓冲池2.2.Change Buffer更改缓冲区2.3.自适应Hash索引2.4.Log Buffer日志缓冲区 3.InnoDB存储引擎的磁盘结构3.1.System Tablespace系统表空间3.2.File-Per-Table Tablespaces每个表都有单独的表空间…

红警For Mac(RAM芯片可玩)

1、文件损坏解决版本&#xff01; 执行以下命令&#xff0c;&#xff08;注意&#xff1a;命令2应用路径根据实际情况修改&#xff09; sudo spctl --master-disable sudo xattr -r -d com.apple.quarantine /Applications/红警2尤里复仇M芯片.app2、新系统14&#xff0c;第一…

孩子都能学会的FPGA:第二十一课——用线性反馈移位寄存器实现伪随机序列

&#xff08;原创声明&#xff1a;该文是作者的原创&#xff0c;面向对象是FPGA入门者&#xff0c;后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门&#xff0c;作者不光让大家知其然&#xff0c;还要让大家知其所以然&#xff01;每个工程作者都搭建了全自动化的仿…

腾讯云轻量应用服务器怎么使用宝塔面板?

腾讯云轻量应用服务器宝塔面板怎么用&#xff1f;轻量应用服务器如何安装宝塔面板&#xff1f;在镜像中选择宝塔Linux面板腾讯云专享版&#xff0c;在轻量服务器防火墙中开启8888端口号&#xff0c;然后远程连接到轻量服务器执行宝塔面板账号密码查询命令&#xff0c;最后登录和…

采集伪原创洗稿,实现文章创作的方法

各位写手小伙伴们&#xff0c;今天要和大家分享一些关于伪原创的方法和经验&#xff0c;希望这些建议能够在你们的写作之旅中派上用场。 首先我们需要明确一下&#xff0c;伪原创并不是鼓励抄袭&#xff0c;而是一种在保留原文核心思想的同时&#xff0c;通过巧妙的方式改写&a…

Qt应用开发(Quick篇)——布局类与布局模块

一、前言 实际 应用中&#xff0c;布局是常用的功能&#xff0c;布局最直观的就是提供空间使用率&#xff0c;改善空间的流动和模块之间的重叠&#xff0c;让界面更加的美观。 二、布局类Layout 2.1 介绍 将Layout类型的对象附加到布局的子元素上&#xff0c;提供有关该项的特…

在AWS Lambda上部署标准FFmpeg工具——自定义层的方案

大纲 1 确定Lambda运行时环境1.1 Lambda系统、镜像、内核版本1.2 运行时1.2.1 Python1.2.2 Java 2 打包FFmpeg3 创建Lambda的Layer4 测试4.1 创建Lambda函数4.2 附加FFmpeg层4.3 添加测试代码4.4 运行测试 参考文献 FFmpeg被广泛应用于音/视频流处理领域。对于简单的需求&#…

阿里云Arthas使用——在日志没有输出异常情况下,如何进行线上bug定位 stack命令 和 trace命令

前言 Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff0c;监测方法执行耗时&#xff0c;类…

FreeRTOS-Plus-CLI移植

FreeRTOS-Plus-CLI移植 Fang XS.1452512966qq.com如果有错误&#xff0c;希望被指出&#xff0c;学习技术的路难免会磕磕绊绊量的积累引起质的变化 介绍 FreeRTOS-Plus-CLI是FreeRTOS的组件之一。FreeRTOS-Plus-CLI提供了一种简单、小巧、可扩展且RAM高效的启用方法方便Free…

分享67个节日PPT,总有一款适合您

分享67个节日PPT&#xff0c;总有一款适合您 67个节日PPT下载链接&#xff1a;https://pan.baidu.com/s/1oU-UUCV_69e8Gp5Y6zrzVA?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

uniapp-hubildx配置

1.配置浏览器 &#xff08;1&#xff09;运行》运行到浏览器配置》配置web服务器 &#xff08;2&#xff09;选择浏览器安装路径 &#xff08;3&#xff09;浏览器安装路径&#xff1a; &#xff08;3.1&#xff09; 右键点击图标》属性 &#xff08;3.2&#xff09;选择目标&…

FutureTask

1. 作用 异步操作获取执行结果取消任务执行&#xff0c;判断是否取消执行判断任务执行是否完毕 2. demo public static void main(String[] args) throws Exception {Callable<String> callable () -> search();FutureTask<String> futureTasknew FutureTask&…

Java多线程技术二:线程间通信——ThreadLocal的使用

1 概述 变量值的共享可以使用public static 的声明方式&#xff0c;所有的线程都是用同一个public static变量&#xff0c;那如果想实现每一个线程都有自己的变量该如何解决呢&#xff1f;JDK提供的ThreadLocal就派上用场了。 ThreadLocal类主要的作用就是将数据放入当前线程对…

web前端开发HTML/css用户登录界面

代码&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml"> <head> <meta http-equi…

神经网络常用归一化和正则化方法解析(一)

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

Win环境中安装Jenkins指南

目录 安装Java环境 下载并安装Jenkins Jenkins版本 启动Jenkins 如何删除Jenkins 安装Java环境 访问 Oracle官方网站 下载并安装JDK 安装完成后&#xff0c;设置系统环境变量 JAVA_HOME 到你的 JDK 安装路径&#xff0c;并将 %JAVA_HOME%\bin 添加到系统 PATH 中。 下载…

Apollo新版本Beta技术沙龙参会感受:未来的自动驾驶之旅

Apollo新版本Beta技术沙龙参会感受&#xff1a;未来的自动驾驶之旅 &#x1f697;&#x1f4a1; 文章目录 Apollo新版本Beta技术沙龙参会感受&#xff1a;未来的自动驾驶之旅 &#x1f697;&#x1f4a1;摘要引言正文&#x1f4cd; 参会流程介绍&#x1f31f; 参会收获&#x1…

「Verilog学习笔记」任意小数分频

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 timescale 1ns/1nsmodule div_M_N(input wire clk_in,input wire rst,output wire clk_out );parameter M_N 8d87; parameter c89 8d24; // 8/9时钟切换点parameter di…