go语言grpc框架实战-02-grpc语法速学
作者:互联网
repeated使用返回商品数组
repeated是一个修饰符,返回字段可以重复任意多次(包括0次),可以认为是一个数组(切片)
- proto文件定义
syntax = "proto3"; // 使用的proto 3的版本
package services; // 生成go文件的包名
option go_package = "../services"; // 指定生成go文件所在当前文件的路径
import "google/api/annotations.proto";
message ProdRequest{
int32 prod_id = 1; // 传入的商品id
}
message ProdResponse{
int32 prod_stock = 1; // 商品库存
}
message QuerySize {
int32 size = 1; // 页尺寸
}
message ProdResponseList { // 返回一堆商品库存,使用了修饰符repeated
repeated ProdResponse prodRes = 1;
}
service ProdService{// 获取商品库存服务
rpc GetProdStock(ProdRequest) returns (ProdResponse) {
option (google.api.http) = {
get : "/v1/prod/{prod_id}"
};
}
rpc GetProdStocks(QuerySize) returns (ProdResponseList) {
option (google.api.http) = {
get : "/v1/prod/list/{size}"
};
}
}
- 使用脚本生成Prod.pb.go和Prod.pb.gw.go两个文件,一个提供grpc服务,另一个提供Http服务
protoc --go_out=plugins=grpc:services ./pbfiles/Prod.proto -I ./pbfiles && ^
protoc --grpc-gateway_out=logtostderr=true:services ./pbfiles/Prod.proto -I ./pbfiles
- server.go代码
func main() {
creds := helper.GetServerCreds()
// NewServer 创建一个未注册服务且尚未开始接受请求的 gRPC 服务器。
rpcServer := grpc.NewServer(grpc.Creds(creds))
// 将当前产品服务注册到grpc服务当中去
services.RegisterProdServiceServer(rpcServer, new(services.ProdService))
// 监听套接字
listener, _ := net.Listen("tcp", ":8080")
//// grpc服务器接收请求,开始提供服务
rpcServer.Serve(listener)
}
- httpserver.go代码
func main() {
gwmux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithTransportCredentials(helper.GetClientCreds())}
// endpoint: 是grpc服务器的地址
err := services.RegisterProdServiceHandlerFromEndpoint(context.Background(), gwmux, "127.0.0.1:8080", opts)
if err != nil {
log.Fatal(err.Error())
}
httpServer := http.Server{Addr: "127.0.0.1:8081", Handler: gwmux}
httpServer.ListenAndServe()
}
启动两个服务
7. grpc客户端代码
func main() {
creds := helper.GetClientCreds()
conn, err := grpc.Dial(":8080", grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatalln(err.Error())
}
//defer conn.Close()
prodClient := services.NewProdServiceClient(conn)
// 获取指定数量的商品库存
prodResList, err := prodClient.GetProdStocks(context.Background(), &services.QuerySize{Size: 5})
if err != nil {
log.Fatalln(err.Error())
}
fmt.Println("客户端接收到服务端的响应", prodResList.ProdRes)
}
-
http访问服务的地址:http://127.0.0.1:8081/v1/prod/list/5
-
使用第三方库进行字段验证
安装
go get -u github.com/envoyproxy/protoc-gen-validate
go install github.com/envoyproxy/protoc-gen-validate
千万注意:通过protoc-gen-validate插件生成代码时,起订要把proto文件中的option go_package = "../services";
注释掉,否则会一直报错。
生成.pb.validate.go的代码:
protoc --validate_out=lang=go:services ./pbfiles/Models.proto -I ./pbfiles
服务接口中对模型进行验证:
// validate验证器进行验证
err := orderRequest.OrderMain.Validate()
if err != nil {
return &OrderResponse{
Status: err.Error(),
}, nil
}
流模式的应用
- 服务端流模式,场景:分批发送查询结果
User.proto定义
// 服务端流模式
rpc GetUserScoreByServerStream(UserScoreRequest) returns(stream UserScoreResponse){
option (google.api.http) = {
post : "/v1/stream/user"
body : "*"
};
}
从新生成*.pb.go文件
gen.bat
流模式代码
// 服务端代码:
func (u *UserService) GetUserScoreByServerStream(request *UserScoreRequest, stream UserService_GetUserScoreByServerStreamServer) error {
// 服务端流模式
fmt.Println(request)
var (
i = 1 // 记录向客户端发送第几次
score int32 = 100 // 积分开始值,随意设置的,因为没有查询数据库
k = 3 // 一次发送多少个用户信息
)
userInfo := make([]*UserInfo, 0, k)
for index, user := range request.UserInfo{
user.UserScore = score
score++
userInfo = append(userInfo, user)
if (index+1) % k == 0 && index > 0{ // 服务端流模式,分批发送
err := stream.Send(&UserScoreResponse{UserInfo: userInfo})
if err != nil {
return err
}
fmt.Printf("服务端向客户端发送了%d次了\n", i)
i++
userInfo = userInfo[:0] // 发送完成后,将切片长度清零,容量不变,重新保存要发送的数据
time.Sleep(time.Second * 1) // 测试时可以把它打开看效果
}
}
// 注意:如果request.Userinfo不是k的整数倍,最后还会有没有发送的,需要再次发送过去
if len(userInfo) > 0 {
if err := stream.Send(&UserScoreResponse{UserInfo: userInfo}); err != nil{
return err
}
fmt.Printf("服务端向客户端发送了%d次了\n", i)
}
return nil
}
// 客户端代码
var i int32
var req = new(UserScoreRequest)
req.UserInfo = make([]*UserInfo, 0)
for i = 1; i <= 10; i++{
req.UserInfo = append(req.UserInfo, &UserInfo{UserId: i})
}
streamClient, _ := userClient.GetUserScoreByServerStream(context.Background(), req)
var n = 1 // 客户端接收数据的次数
for {
// 因为服务端采用的是流模式,所以客户端需要循环接收
userScoreRes, err := streamClient.Recv()
if err == io.EOF {
fmt.Println("数据接收完毕")
break
}
if err != nil {
log.Fatalln(err.Error())
}
fmt.Printf("客户端接收了%d次", n)
n++
// 注意:此处可以开启goroutine去处理接收到的数据,不需要处理完了再接收服务端数据
// 主协程接收数据,子协程处理数据,提高接收处理的效率
go func() {
fmt.Println(userScoreRes)
}()
}
- 客户端流模式
- 双向流模式
由于篇幅比较长,源码详见github
如果链接访问失败,请加小编微信:Python_Django_Flask,备注:要grpc_test_server_client源码
标签:02,err,grpc,go,userInfo,services,服务端 来源: https://www.cnblogs.com/mayanan/p/16246261.html