- 前言
- 一、gRPC
- SAN
- CA根证书生成
- 服务端证书签发
- 二、总结
- 三、参考
初学gRPC,跟着B站go语言grpc框架实战Up主进行学习,其中视频中的坑还是比较多的,比如版本方法废弃,获取库地址废弃等,本文基于该视频整理了前三讲的实战内容。
一、gRPCgRPC是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文件,为了篇幅,本次将其放置在文末,在该文件中,可以找到包含了ProdServiceServer
和ProdServiceClient
两个接口,接下来在服务端实现该接口,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.crt
和ca_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(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.key
和server.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",
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)