【gRPC】Protobuf中间文件介绍、使用、Go新版本TLS证书认证问题

【gRPC】Protobuf中间文件介绍、使用、Go新版本TLS证书认证问题,第1张

文章目录
    • 前言
    • 一、gRPC
      • SAN
      • CA根证书生成
      • 服务端证书签发
    • 二、总结
    • 三、参考

前言

初学gRPC,跟着B站go语言grpc框架实战Up主进行学习,其中视频中的坑还是比较多的,比如版本方法废弃,获取库地址废弃等,本文基于该视频整理了前三讲的实战内容。

一、gRPC

gRPC是Google开发的高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。本身它不是分布式的,所以需要进一步的开发。Go版gRPC文档地址:https://grpc.io/docs/languages/go/

gRPC支持Java、C++、Golang、php等多个语言版本,golang版本的官方地址:https://github.com/grpc/grpc-go,在go中使用也是比较简单的,基本上go的项目开发都采用了go module模式的开发,因此,我们在项目目录的控制台下利用go get命令下载下来即可(确保设置了代理,否则可能下载失败):

go get -u google.golang.org/grpc


代理配置(在setting中找到Go Modules添加代理):GONOPROXY=https://goproxy.cn,direct

grpc始终只是一个框架,要支持多种语言的编译就需要一种中间文件,介绍中间文件例子之前需要知道,grpc之间传输使用的是Protobuf协议,是基于二进制的(网络传输中还有其它的协议,比如xml、json是基于文本的),Protobuf协议是一种轻便高校的序列化数据结构协议,可用于网络通信和数据存储,特点是效率高,传输快、维护方便。很多的第三方rpc库都会支持Protobuf,github下载地址为:https://github.com/protocolbuffers/protobuf/releases

我们可以下载Protobuf的编译器,将我们编写的满足protobuf协议的中间文件转为go、java等其他语言。下载完成之后解压缩文件随便放到哪个目录都行,但是要记得为该目录配置环境变量:

配置完成之后在cmd中可以使用protoc --version命令进行验证:

快速学习使用的地址为:https://developers.google.com/protocol-buffers/docs/tutorials,看了下是google的域名,因此可以找找国内的翻译版本或者其他大佬的整理,这里找了一份地址供参考:Protocol Buffer 官网文档整理

在go中可以使用命令go get google.golang.org/protobuf下载相应的库支持。我们使用prococ编译为go文件需要一个插件的支持,在go中使用go get google.golang.org/protobuf/protoc-gen-go命令下载即可,下载完成之后会在自己配制的GoPath的bin目录下出现一个protoc-gen-go.exe文件:

当执行protoc文件的时候就会调用该插件生成相应的go文件,在goland中我们可以下载一个插件检查我们编写的protoc文件是否有错误,大多都下载Protobuf Support,我是下载了一个Protocol Buffer Editor


然后我们可以在工程中创建一个proto的中间文件,并利用插件将其编译为go文件了,如下是我的目录结构:

现在我们编写一个protocbuf的中间文件,也就是上图中的Prod.proto,代码如下:

syntax = "proto3"; //proto3的语法,不写会默认为proto2

//包名,通过protoc生成go文件时使用
package services;
option go_package = "../services"; //添加生成go文件的路径

message ProdRequest {
  int32 prod_id = 1; //传入的商品id
}
message ProdResponse {
  int32 prod_stock =1; //商品库存
}
service ProdService {
  rpc GetProdStock(ProdRequest) returns(ProdResponse);
}

现在,我们在pbfiles目录下利用protoc编译器和go插件(protoc-gen-go,编译时自动调用)将上面的中间文件编译为go文件:

protoc --go_out=plugins=grpc:../services Prod.proto


编译完成之后就会在services目录下多出一个Prod.pb.go的go文件,为了篇幅,本次将其放置在文末,在该文件中,可以找到包含了ProdServiceServerProdServiceClient两个接口,接下来在服务端实现该接口,services/ProdService.go中包含了服务端的实现:

package services

import (
	context "context"

	_ "google.golang.org/grpc"
)

type ProdService struct {
}

func (this *ProdService) GetProdStock(context.Context, *ProdRequest) (*ProdResponse, error) {
	return &ProdResponse{ProdStock:20}, nil
}

在项目根目录下创建server.go并启动服务:

package main

import (
	"google.golang.org/grpc"
	"grpcpro/services"
	"net"
)

func main() {
	rpcServer := grpc.NewServer() //new一个grpc服务
	services.RegisterProdServiceServer(rpcServer,new(services.ProdService))  //进行服务注册
	listen, _ := net.Listen("tcp",":8888") //建立tcp的监听服务
	rpcServer.Serve(listen) 
}

然后新建一个工程grpcClient创建一个客户端,在客户端并不需要服务端的ProdService.go服务文件,只需要将生成的go文件放置到客户端的目录下即可,为了统一,和服务端保持一致,这里也将其放置在services目录下:

然后在项目根目录下创建main.go编写客户端服务:

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"grpcClient/services"
	"log"
)

func main() {
	conn, err := grpc.Dial(":8888",grpc.WithTransportCredentials(insecure.NewCredentials())) //第二个参数是表示不使用证书,老版本的grpc第二个参数为grpc.WithInsecure()
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	prodClient := services.NewProdServiceClient(conn)
	prodRes, err := prodClient.GetProdStock(context.Background(),&services.ProdRequest{ProdId: 12})
	if err != nil{
		log.Fatal(err)
	}
	fmt.Printf("grpcClient getProdStock is %d\n",prodRes.ProdStock)
}

启动服务端再启动客户端,客户端就能够从服务端获取相应的结果,也就是说客户端能直接使用服务端中提供的服务了。
上面我们使用的服务端和客户端都没有证书,下面我们利用openSSL来生成CA证书进行使用,openSSL下载安装地址:http://slproweb.com/products/Win32OpenSSL.html

下载exe执行安装一路通行,安装完成之后进入到openssl的bin目录下,在上方输入cmd即可进入当前目录,如果不想这么做可以直接给该目录配置环境变量,任意位置打开cmd都能使用openssl。

CA证书生成的几个命令如下:

# 生成CA私钥(ca.key)
openssl genrsa -des3 -out ca.key 2048 
# 生成CA证书签名请求(ca.csr)
openssl req -new -key ca.key -out ca.csr
# 生成自签名CA证书(ca.cert)
openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt

首先生成CA的私钥(ca.key):

这步完成之后会在当前目录下生成一个ca.key的私钥文件:

然后生成CA证书的签名请求(ca.csr):

这步完成之后会在当前目录下生成一个ca.csr文件

为了简单使用,我们上面的ca.key生成一个不带密码的ca_no_pwd.key

openssl rsa -in ca.key -out ca_no_pwd.key


最后生成自签名的CA证书ca.crt:

openssl x509 -req -days 3650 -in ca.csr -signkey ca_no_pwd.key -out ca.crt

C = countryName=国家
S = stateOrProvinceName=省
L = localityName=城市
O = organizationName=公司
OU = organizationalUnitName=部门
CN = commonName=域名或者你的名字

此时生成的ca_no_pwd.key就是不带秘密的了,然后我们就可以对客户端和服务端进行改造了,在服务端的keys文件夹下拷贝刚才生成的两个文件ca.crtca_no_pwd.key

然后改造服务端的server.go,加入CA证书和CA私钥:

package main

import (
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"grpcpro/services"
	"log"
	"net"
)

func main() {
	creds, err := credentials.NewServerTLSFromFile("keys/ca.crt","keys/ca_no_pwd.key")
	if err != nil {
		log.Fatal(err)
	}
	rpcServer := grpc.NewServer(grpc.Creds(creds)) //创建带ca证书验证的服务端
	services.RegisterProdServiceServer(rpcServer,new(services.ProdService))
	listen, _ := net.Listen("tcp",":8888")
	rpcServer.Serve(listen)
}

对于客户端,这里也是简单的认证,而不是双向的认证,因此客户端和服务端使用同一个证书,将CA证书ca.crt也拷贝到客户端的keys目录下,改写客户端代码:

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"grpcClient/services"
	"log"
)

func main() {
	creds, err := credentials.NewClientTLSFromFile("keys/ca.crt","tonghua")
	if err != nil {
		log.Fatal(err)
	}
	conn, err := grpc.Dial(":8888",grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	prodClient := services.NewProdServiceClient(conn)
	prodRes, err := prodClient.GetProdStock(context.Background(),&services.ProdRequest{ProdId: 12})
	if err != nil{
		log.Fatal(err)
	}
	fmt.Printf("grpcClient getProdStock is %d\n",prodRes.ProdStock)
}

此时报错:

查了下资料,golang 1.15+版本上,用 gRPC通过TLS实现数据传输加密时,会报错证书的问题
因为证书并没有开启SAN扩展(默认是没有开启SAN扩展)所造成的,导致客户端和服务端无法建立连接,此时两个解决办法,一个是设置环境变量set GODEBUG="x509ignoreCN=0",这种方法在go 1.17及以后已经没用了。第二种则是开启SAN的扩展。

SAN

SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。接下来我们重新利用配置文件生成CA证书,再利用ca相关去生成服务端的证书。

CA根证书生成

在openssl的这个目录下新建一个配置文件ca.conf,文件内容如下:

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = SiChuan
localityName                = Locality Name (eg, city)
localityName_default        = Chengdu
organizationName            = Organization Name (eg, company)
organizationName_default    = Step
commonName                  = CommonName (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = tonghua

生成私钥、证书等命令依次如下执行:

openssl genrsa -out ca.key 2048
openssl req -new -sha256 -out ca.csr -key ca.key -config ca.conf
openssl x509 -req -days 3650 -in ca.csr signkey ca.key out ca.crt
服务端证书签发

接下来我们还需要在openssl的这个目录下新建一个配置文件server.conf,用于生成服务端的证书和私钥等,文件内容如下:

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = SiChuan
localityName                = Locality Name (eg, city)
localityName_default        = Chengdu
organizationName            = Organization Name (eg, company)
organizationName_default    = Step
commonName                  = CommonName (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = tonghua
[ req_ext ]
# 添加subjectAltName 
subjectAltName = @alt_names
# 文件末尾添加. www.p-pp.cn代表允许的ServerName,自己随便写
[alt_names]
DNS.1   = www.p-pp.cn
IP      = 127.0.0.1

然后我们依次执行下面命令(中途遇到填写国家啥的直接回车跳过,):

// 服务端私钥
openssl genrsa -out server.key 2048
//服务端签名请求
openssl req -new -sha256 -out server.csr -key server.key -config server.conf
//用根证书签发服务端证书server.pem
openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.pem -extensions req_ext -extfile server.conf

然后在服务端,将server.keyserver.pem拷贝到keys目录下,改写服务端代码:

package main

import (
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"grpcpro/services"
	"log"
	"net"
)

func main() {
	creds, err := credentials.NewServerTLSFromFile("keys/server.pem","keys/server.key")
	if err != nil {
		log.Fatal(err)
	}
	Address := "127.0.0.1:8888"
	rpcServer := grpc.NewServer(grpc.Creds(creds)) //创建带ca证书验证的服务端
	services.RegisterProdServiceServer(rpcServer,new(services.ProdService))
	listen, _ := net.Listen("tcp",Address)
	log.Println("Listen on " + Address + " with TLS")
	rpcServer.Serve(listen)
}

server.pem拷贝到客户端,改写下客户端代码:

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"grpcClient/services"
	"log"
)

func main() {
	creds, err := credentials.NewClientTLSFromFile("keys/server.pem","www.p-pp.cn")
	if err != nil {
		log.Fatal(err)
	}
	conn, err := grpc.Dial("127.0.0.1:8888",grpc.WithTransportCredentials(creds))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	prodClient := services.NewProdServiceClient(conn)
	prodRes, err := prodClient.GetProdStock(context.Background(),&services.ProdRequest{ProdId: 12})
	if err != nil{
		log.Fatal(err)
	}
	fmt.Printf("grpcClient getProdStock is %d\n",prodRes.ProdStock)
}

重启之后进行测试,发现客户端能正常使用服务端的服务了。

二、总结

在本篇文章中,我们主要简单介绍了gRPC的概念及其跨语言、高效等特性,介绍了Protobuf中间文件来支持跨语言的特性,并编写了一个简单的Protobuf中间文件,利用Protoc编译器和go插件将其转换为go语言,并据此编写了gRPC服务端和客户端实现了客户端利用服务端的服务。这中间遇到了证书生成的坑,这在视频中是没有提到的,但这并不能阻碍我们的脚步,因为go在发展阶段,版本迭代更新的问题还是比较多的,在解决问题的过程中,确实也能学到不少东西。

三、参考

1、grpc中TLS认证证书问题
2、golang grpc 证书报错


Prod.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.28.0
// 	protoc        v3.20.0
// source: Prod.proto

//包名,通过protoc生成go文件时使用

package services

import (
	context "context"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type ProdRequest struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	ProdId int32 `protobuf:"varint,1,opt,name=prod_id,json=prodId,proto3" json:"prod_id,omitempty"` //传入的商品id
}

func (x *ProdRequest) Reset() {
	*x = ProdRequest{}
	if protoimpl.UnsafeEnabled {
		mi := &file_Prod_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *ProdRequest) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*ProdRequest) ProtoMessage() {}

func (x *ProdRequest) ProtoReflect() protoreflect.Message {
	mi := &file_Prod_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use ProdRequest.ProtoReflect.Descriptor instead.
func (*ProdRequest) Descriptor() ([]byte, []int) {
	return file_Prod_proto_rawDescGZIP(), []int{0}
}

func (x *ProdRequest) GetProdId() int32 {
	if x != nil {
		return x.ProdId
	}
	return 0
}

type ProdResponse struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	ProdStock int32 `protobuf:"varint,1,opt,name=prod_stock,json=prodStock,proto3" json:"prod_stock,omitempty"` //商品库存
}

func (x *ProdResponse) Reset() {
	*x = ProdResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_Prod_proto_msgTypes[1]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *ProdResponse) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*ProdResponse) ProtoMessage() {}

func (x *ProdResponse) ProtoReflect() protoreflect.Message {
	mi := &file_Prod_proto_msgTypes[1]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use ProdResponse.ProtoReflect.Descriptor instead.
func (*ProdResponse) Descriptor() ([]byte, []int) {
	return file_Prod_proto_rawDescGZIP(), []int{1}
}

func (x *ProdResponse) GetProdStock() int32 {
	if x != nil {
		return x.ProdStock
	}
	return 0
}

var File_Prod_proto protoreflect.FileDescriptor

var file_Prod_proto_rawDesc = []byte{
	0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x73, 0x65,
	0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x26, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x64, 0x52, 0x65,
	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x5f, 0x69, 0x64,
	0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x64, 0x49, 0x64, 0x22, 0x2d,
	0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d,
	0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01,
	0x28, 0x05, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x53, 0x74, 0x6f, 0x63, 0x6b, 0x32, 0x4c, 0x0a,
	0x0b, 0x50, 0x72, 0x6f, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0c,
	0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x64, 0x53, 0x74, 0x6f, 0x63, 0x6b, 0x12, 0x15, 0x2e, 0x73,
	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x75,
	0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x50,
	0x72, 0x6f, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0d, 0x5a, 0x0b, 0x2e,
	0x2e, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
	0x6f, 0x33,
}

var (
	file_Prod_proto_rawDescOnce sync.Once
	file_Prod_proto_rawDescData = file_Prod_proto_rawDesc
)

func file_Prod_proto_rawDescGZIP() []byte {
	file_Prod_proto_rawDescOnce.Do(func() {
		file_Prod_proto_rawDescData = protoimpl.X.CompressGZIP(file_Prod_proto_rawDescData)
	})
	return file_Prod_proto_rawDescData
}

var file_Prod_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_Prod_proto_goTypes = []interface{}{
	(*ProdRequest)(nil),  // 0: services.ProdRequest
	(*ProdResponse)(nil), // 1: services.ProdResponse
}
var file_Prod_proto_depIdxs = []int32{
	0, // 0: services.ProdService.GetProdStock:input_type -> services.ProdRequest
	1, // 1: services.ProdService.GetProdStock:output_type -> services.ProdResponse
	1, // [1:2] is the sub-list for method output_type
	0, // [0:1] is the sub-list for method input_type
	0, // [0:0] is the sub-list for extension type_name
	0, // [0:0] is the sub-list for extension extendee
	0, // [0:0] is the sub-list for field type_name
}

func init() { file_Prod_proto_init() }
func file_Prod_proto_init() {
	if File_Prod_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_Prod_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*ProdRequest); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
		file_Prod_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*ProdResponse); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_Prod_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   2,
			NumExtensions: 0,
			NumServices:   1,
		},
		GoTypes:           file_Prod_proto_goTypes,
		DependencyIndexes: file_Prod_proto_depIdxs,
		MessageInfos:      file_Prod_proto_msgTypes,
	}.Build()
	File_Prod_proto = out.File
	file_Prod_proto_rawDesc = nil
	file_Prod_proto_goTypes = nil
	file_Prod_proto_depIdxs = nil
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6

// ProdServiceClient is the client API for ProdService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ProdServiceClient interface {
	GetProdStock(ctx context.Context, in *ProdRequest, opts ...grpc.CallOption) (*ProdResponse, error)
}

type prodServiceClient struct {
	cc grpc.ClientConnInterface
}

func NewProdServiceClient(cc grpc.ClientConnInterface) ProdServiceClient {
	return &prodServiceClient{cc}
}

func (c *prodServiceClient) GetProdStock(ctx context.Context, in *ProdRequest, opts ...grpc.CallOption) (*ProdResponse, error) {
	out := new(ProdResponse)
	err := c.cc.Invoke(ctx, "/services.ProdService/GetProdStock", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// ProdServiceServer is the server API for ProdService service.
type ProdServiceServer interface {
	GetProdStock(context.Context, *ProdRequest) (*ProdResponse, error)
}

// UnimplementedProdServiceServer can be embedded to have forward compatible implementations.
type UnimplementedProdServiceServer struct {
}

func (*UnimplementedProdServiceServer) GetProdStock(context.Context, *ProdRequest) (*ProdResponse, error) {
	return nil, status.Errorf(codes.Unimplemented, "method GetProdStock not implemented")
}

func RegisterProdServiceServer(s *grpc.Server, srv ProdServiceServer) {
	s.RegisterService(&_ProdService_serviceDesc, srv)
}

func _ProdService_GetProdStock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(ProdRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(ProdServiceServer).GetProdStock(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/services.ProdService/GetProdStock",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(ProdServiceServer).GetProdStock(ctx, req.(*ProdRequest))
	}
	return interceptor(ctx, in, info, handler)
}

var _ProdService_serviceDesc = grpc.ServiceDesc{
	ServiceName: "services.ProdService",
	HandlerType: (*ProdServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "GetProdStock",
			Handler:    _ProdService_GetProdStock_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "Prod.proto",
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/719918.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-25
下一篇 2022-04-25

发表评论

登录后才能评论

评论列表(0条)

保存