ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

go语言grpc框架实战-02-grpc语法速学

2022-05-15 19:33:42  阅读:202  来源: 互联网

标签:02 err grpc go userInfo services 服务端


repeated使用返回商品数组

repeated是一个修饰符,返回字段可以重复任意多次(包括0次),可以认为是一个数组(切片)

  1. 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}"
    };
  }
}

  1. 使用脚本生成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
  1. 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)
}

  1. 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)
}

  1. http访问服务的地址:http://127.0.0.1:8081/v1/prod/list/5

  2. 使用第三方库进行字段验证
    安装

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
  	}

protoc-gen-validate github文档

流模式的应用

  1. 服务端流模式,场景:分批发送查询结果
    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)
		}()
	}

  1. 客户端流模式
  2. 双向流模式
    由于篇幅比较长,源码详见github

github 服务端代码

github 客户端代码

如果链接访问失败,请加小编微信:Python_Django_Flask,备注:要grpc_test_server_client源码

标签:02,err,grpc,go,userInfo,services,服务端
来源: https://www.cnblogs.com/mayanan/p/16246261.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有