其他分享
首页 > 其他分享> > go语言grpc框架实战-02-grpc语法速学

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

作者:互联网

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