一.gRPC框架的介绍
gRPC是一个
高性能、
开源和
通用的
RPC 框架,
面向移动端和
HTTP/2 设计,目前提供 C、Java 和 Go语言版本,分别是:grpc, grpc–java,
grpc–go,其中 C 版本支持 C, C++, Node.js, Python, Ruby,
Objective-C, PHP 和 C# 支持
-
gRPC 特点
有了gRPC, 可以一次性的在一个 .proto 文件中定义服务,并使用任何支持它的语言去实现客户端
和服务端,gRPC默认使用protocol buffers,它是google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如JSON),可以用proto创建GRPC服务,用protocol buffers消息类型来定义方法参数和返回类型。
在gRPC客户端可以直接调用不同服务器上的远程程序,就想调用本地程序一样,很容易构建分布式应用和服务,和很多RPC系统一样,服务负责实现定义好的接口并处理客户端请求,客户端根据接口描述
直接调用需要的服务,客户端和服务器可以分别使用gRPC支持的不同语言实现
gRPC 官方文档中文版:http://doc.oschina.net/grpc?t=60133
gRPC官网:https://grpc.io
二.gRPC的使用
GRPC使用的包是
google.golang.org/grpc ,可以在项目中使用
go get –u –v
google.golang.org/grpc 来下载包,也可以使用
go mod tidy下载包,如果从Protobuf的角度看,gRPC只不过是一个针对service接口生成代码的生成器,下面举例介绍gRPC的用法
1).实现服务端
(1).创建文件main.go
package main
import "fmt"
//rpc远程调用的接口,需要实现hello.proto中定义的Hello接口,以及里面的方法
func main() {
fmt.Println("hello")
}
然后通过命令go mod init hello初始化项目,为以后开发做准备,然后通过命令 go mod tidy 来下载依赖,如下图:
然后运行go run .main.go 命令,测试是否成功:
(2).创建.proto文件
syntax = "proto3"; //proto版本
option go_package= "./helloService"; //表示在目录helloService下面生成hello.pb.go,以及对应的包名
//通过service创建一个RPC服务, 生成一个Hello接口
service Hello { // Hello可以是小写, 无所谓
//通过rpc来指定远程调用的方法:
//SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
rpc SayHello(HelloReq) returns (HelloRes);
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
message HelloReq {
string name = 1;
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
message HelloRes {
string message = 1;
}
(3).编译.proto文件
使用命令
protoc –go_out=plugins=grpc:. *.proto 对.proto文件进行编译,生成
.pb.go的服务
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.15.5
// source: hello.proto
package helloService
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)
)
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
type HelloReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *HelloReq) Reset() {
*x = HelloReq{}
if protoimpl.UnsafeEnabled {
mi := &file_hello_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HelloReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelloReq) ProtoMessage() {}
func (x *HelloReq) ProtoReflect() protoreflect.Message {
mi := &file_hello_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 HelloReq.ProtoReflect.Descriptor instead.
func (*HelloReq) Descriptor() ([]byte, []int) {
return file_hello_proto_rawDescGZIP(), []int{0}
}
func (x *HelloReq) GetName() string {
if x != nil {
return x.Name
}
return ""
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
type HelloRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *HelloRes) Reset() {
*x = HelloRes{}
if protoimpl.UnsafeEnabled {
mi := &file_hello_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HelloRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelloRes) ProtoMessage() {}
func (x *HelloRes) ProtoReflect() protoreflect.Message {
mi := &file_hello_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 HelloRes.ProtoReflect.Descriptor instead.
func (*HelloRes) Descriptor() ([]byte, []int) {
return file_hello_proto_rawDescGZIP(), []int{1}
}
func (x *HelloRes) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
var File_hello_proto protoreflect.FileDescriptor
var file_hello_proto_rawDesc = []byte{
0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a,
0x08, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x24, 0x0a,
0x08, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x32, 0x29, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x20, 0x0a, 0x08,
0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x09, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
0x52, 0x65, 0x71, 0x1a, 0x09, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x42, 0x10,
0x5a, 0x0e, 0x2e, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_hello_proto_rawDescOnce sync.Once
file_hello_proto_rawDescData = file_hello_proto_rawDesc
)
func file_hello_proto_rawDescGZIP() []byte {
file_hello_proto_rawDescOnce.Do(func() {
file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData)
})
return file_hello_proto_rawDescData
}
var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_hello_proto_goTypes = []interface{}{
(*HelloReq)(nil), // 0: HelloReq
(*HelloRes)(nil), // 1: HelloRes
}
var file_hello_proto_depIdxs = []int32{
0, // 0: Hello.SayHello:input_type -> HelloReq
1, // 1: Hello.SayHello:output_type -> HelloRes
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_hello_proto_init() }
func file_hello_proto_init() {
if File_hello_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HelloReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HelloRes); 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_hello_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_hello_proto_goTypes,
DependencyIndexes: file_hello_proto_depIdxs,
MessageInfos: file_hello_proto_msgTypes,
}.Build()
File_hello_proto = out.File
file_hello_proto_rawDesc = nil
file_hello_proto_goTypes = nil
file_hello_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
// HelloClient is the client API for Hello service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloClient interface {
// 通过rpc来指定远程调用的方法:
// SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error)
}
type helloClient struct {
cc grpc.ClientConnInterface
}
func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
return &helloClient{cc}
}
func (c *helloClient) SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error) {
out := new(HelloRes)
err := c.cc.Invoke(ctx, "/Hello/SayHello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HelloServer is the server API for Hello service.
type HelloServer interface {
// 通过rpc来指定远程调用的方法:
// SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
SayHello(context.Context, *HelloReq) (*HelloRes, error)
}
// UnimplementedHelloServer can be embedded to have forward compatible implementations.
type UnimplementedHelloServer struct {
}
func (*UnimplementedHelloServer) SayHello(context.Context, *HelloReq) (*HelloRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}
func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
s.RegisterService(&_Hello_serviceDesc, srv)
}
func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HelloServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Hello/SayHello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HelloServer).SayHello(ctx, req.(*HelloReq))
}
return interceptor(ctx, in, info, handler)
}
var _Hello_serviceDesc = grpc.ServiceDesc{
ServiceName: "Hello",
HandlerType: (*HelloServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _Hello_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "hello.proto",
}
(4).生成的.pb.go文件几个重要方法/结构体讲解
1).HelloServer
// HelloServer is the server API for Hello service.
type HelloServer interface {
// 通过rpc来指定远程调用的方法:
// SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
SayHello(context.Context, *HelloReq) (*HelloRes, error)
}
2).RegisterHelloServer
func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
s.RegisterService(&_Hello_serviceDesc, srv)
}
3).NewHelloClient
func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
return &helloClient{cc}
}
(5).在main.go目录下运行命令 go mod tidy,加载生成的.pb.go中引入的包
(6).在main.go中编写服务端代码
package main
import (
"context"
"fmt"
"go_code/micro/grpc_demo/server/hello/proto/helloService"
"net"
"google.golang.org/grpc"
)
//grpc远程调用的接口,需要实现hello.proto中定义的Hello服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现HelloServer的接口
type Hello struct{}
//SayHello方法参考hello.pb.go中的接口
/*
// HelloServer is the server API for Hello service.
type HelloServer interface {
// 通过rpc来指定远程调用的方法:
// SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
SayHello(context.Context, *HelloReq) (*HelloRes, error)
}
*/
*/
func (this Hello) SayHello(c context.Context, req *helloService.HelloReq) (*helloService.HelloRes, error) {
fmt.Println(req)
return &helloService.HelloRes{
Message: "你好" + req.Name,
}, nil
}
func main() {
//1. 初始一个 grpc 对象
grpcServer := grpc.NewServer()
//2. 注册服务
//helloService.RegisterHelloServer(grpcServer, &Hello{})
// &Hello{}和 new(Hello)相同
helloService.RegisterHelloServer(grpcServer, new(Hello))
//3. 设置监听, 指定 IP、port
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println(err)
}
// 4退出关闭监听
defer listener.Close()
//5、启动服务
grpcServer.Serve(listener)
}
测试服务端是否启动成功,运行go run .main.go,没有保存,说明启动成功:
2).实现客户端
(1).创建文件main.go
然后通过命令go mod init hello初始化项目,为以后开发做准备,然后通过命令 go mod tidy 来下载依赖,操作和实现服务端步骤(1)类似,然后运行go run .main.go 命令,测试是否成功,打印出hello,说明成功
(2).创建.proto文件
在micro/grpc_demo/server/client/proto下
创建hello.proto文件,
里面的代码和
实现服务端步骤(2)一致
syntax = "proto3"; //proto版本
option go_package= "./helloService"; //表示在目录helloService下面生成hello.pb.go,以及对应的包名
//通过service创建一个RPC服务, 生成一个Hello接口
service Hello { // Hello可以是小写, 无所谓
//通过rpc来指定远程调用的方法:
//SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
rpc SayHello(HelloReq) returns (HelloRes);
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
message HelloReq {
string name = 1;
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
message HelloRes {
string message = 1;
}
(3).编译.proto文件
使用命令
protoc –go_out=plugins=grpc:. *.proto 对.proto文件进行编译,生成
.pb.go的服务,步骤和
实现服务端步骤(3)一致
生成的文件目录如下:
生成.pb.go代码和实现服务端步骤(3)中生成的.pd,go代码一致
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.15.5
// source: hello.proto
package helloService
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)
)
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
type HelloReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *HelloReq) Reset() {
*x = HelloReq{}
if protoimpl.UnsafeEnabled {
mi := &file_hello_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HelloReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelloReq) ProtoMessage() {}
func (x *HelloReq) ProtoReflect() protoreflect.Message {
mi := &file_hello_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 HelloReq.ProtoReflect.Descriptor instead.
func (*HelloReq) Descriptor() ([]byte, []int) {
return file_hello_proto_rawDescGZIP(), []int{0}
}
func (x *HelloReq) GetName() string {
if x != nil {
return x.Name
}
return ""
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
type HelloRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *HelloRes) Reset() {
*x = HelloRes{}
if protoimpl.UnsafeEnabled {
mi := &file_hello_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HelloRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelloRes) ProtoMessage() {}
func (x *HelloRes) ProtoReflect() protoreflect.Message {
mi := &file_hello_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 HelloRes.ProtoReflect.Descriptor instead.
func (*HelloRes) Descriptor() ([]byte, []int) {
return file_hello_proto_rawDescGZIP(), []int{1}
}
func (x *HelloRes) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
var File_hello_proto protoreflect.FileDescriptor
var file_hello_proto_rawDesc = []byte{
0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a,
0x08, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x24, 0x0a,
0x08, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x32, 0x29, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x20, 0x0a, 0x08,
0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x09, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
0x52, 0x65, 0x71, 0x1a, 0x09, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x42, 0x10,
0x5a, 0x0e, 0x2e, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_hello_proto_rawDescOnce sync.Once
file_hello_proto_rawDescData = file_hello_proto_rawDesc
)
func file_hello_proto_rawDescGZIP() []byte {
file_hello_proto_rawDescOnce.Do(func() {
file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData)
})
return file_hello_proto_rawDescData
}
var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_hello_proto_goTypes = []interface{}{
(*HelloReq)(nil), // 0: HelloReq
(*HelloRes)(nil), // 1: HelloRes
}
var file_hello_proto_depIdxs = []int32{
0, // 0: Hello.SayHello:input_type -> HelloReq
1, // 1: Hello.SayHello:output_type -> HelloRes
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_hello_proto_init() }
func file_hello_proto_init() {
if File_hello_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HelloReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*HelloRes); 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_hello_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_hello_proto_goTypes,
DependencyIndexes: file_hello_proto_depIdxs,
MessageInfos: file_hello_proto_msgTypes,
}.Build()
File_hello_proto = out.File
file_hello_proto_rawDesc = nil
file_hello_proto_goTypes = nil
file_hello_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
// HelloClient is the client API for Hello service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloClient interface {
// 通过rpc来指定远程调用的方法:
// SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error)
}
type helloClient struct {
cc grpc.ClientConnInterface
}
func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
return &helloClient{cc}
}
func (c *helloClient) SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error) {
out := new(HelloRes)
err := c.cc.Invoke(ctx, "/Hello/SayHello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HelloServer is the server API for Hello service.
type HelloServer interface {
// 通过rpc来指定远程调用的方法:
// SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
SayHello(context.Context, *HelloReq) (*HelloRes, error)
}
// UnimplementedHelloServer can be embedded to have forward compatible implementations.
type UnimplementedHelloServer struct {
}
func (*UnimplementedHelloServer) SayHello(context.Context, *HelloReq) (*HelloRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}
func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
s.RegisterService(&_Hello_serviceDesc, srv)
}
func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HelloServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Hello/SayHello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HelloServer).SayHello(ctx, req.(*HelloReq))
}
return interceptor(ctx, in, info, handler)
}
var _Hello_serviceDesc = grpc.ServiceDesc{
ServiceName: "Hello",
HandlerType: (*HelloServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _Hello_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "hello.proto",
}
(4).生成的.pb.go文件几个重要方法/结构体讲解
(5).在main.go目录下运行命令 go mod tidy,加载生成的.pb.go中引入的包
(6).在main.go中编写客户端代码
package main
//grpc客户端代码
import (
"clientHello/proto/helloService"
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
// 1、连接服务器
/*
credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
DialOption,用于连接服务器。
*/
grpcClient, err := grpc.Dial("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println(err)
}
//2、注册客户端
client := helloService.NewHelloClient(grpcClient)
//3、调用服务端函数, 实现HelloClient接口:SayHello()
/*
// HelloClient is the client API for Hello service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloClient interface {
// 通过rpc来指定远程调用的方法:
// SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error)
}
*/
res, err1 := client.SayHello(context.Background(), &helloService.HelloReq{
Name: "张三",
})
if err1 != nil {
fmt.Printf("调用服务端代码失败: %s", err1)
return
}
fmt.Printf("%#vrn", res)
fmt.Printf("调用成功: %s", res.Message)
}
3).测试客户端调用服务端微服务是否成功
(1).启动服务端
(2).启动客服端,请求服务端方法
返回了服务端的数据,说明微服务操作完成,这时服务端展示如下:
要求通过AddGoods
增加一个商品数据,通过GetGoods
获取商品列表
先实现通过AddGoods
增加一个商品数据的微服务
1).实现增加商品服务端
(1).创建文件main.go
package main
import "fmt"
//rpc远程调用的接口,需要实现goods.proto中定义的Goods接口,以及里面的方法
func main() {
fmt.Println("goods")
}
然后通过命令go mod init goods初始化项目,为以后开发做准备,然后通过命令 go mod tidy 来下载依赖,如下图:
然后运行go run .main.go 命令,测试是否成功:
打印出goods,说明成功
(2).创建.proto文件
syntax = "proto3"; //proto版本
option go_package= "./goodsService"; //表示在目录goodsService下面生成goods.pb.go,以及对应的包名
//通过service创建一个RPC服务, 生成一个Goods接口, 服务端实现Goods里面的方法, 客户端调用
service Hello { // Goods可以是小写, 无所谓
//通过rpc来指定远程调用的方法:
//AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
rpc AddGoods(AddGoodsReq) returns (AddGoodsRes);
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品
message AddGoodsReq {
string title = 1; //商品标题
double price = 2; //价格
string content = 3; //内容
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
message AddGoodsRes {
string message = 1; // 返回结果描述: 成功的描述以及失败的描述
bool success = 2; //返回结果标识: 是否成功
}
(3).编译.proto文件
使用命令
protoc –go_out=plugins=grpc:. *.proto 对.proto文件进行编译,生成
.pb.go的服务
生成的文件目录如下:
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.15.5
// source: goods.proto
package goodsService
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)
)
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品
type AddGoodsReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` //商品标题
Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` //价格
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` //内容
}
func (x *AddGoodsReq) Reset() {
*x = AddGoodsReq{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsReq) ProtoMessage() {}
func (x *AddGoodsReq) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 AddGoodsReq.ProtoReflect.Descriptor instead.
func (*AddGoodsReq) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{0}
}
func (x *AddGoodsReq) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *AddGoodsReq) GetPrice() float64 {
if x != nil {
return x.Price
}
return 0
}
func (x *AddGoodsReq) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
type AddGoodsRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` // 返回结果描述: 成功的描述以及失败的描述
Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` //返回结果标识: 是否成功
}
func (x *AddGoodsRes) Reset() {
*x = AddGoodsRes{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsRes) ProtoMessage() {}
func (x *AddGoodsRes) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 AddGoodsRes.ProtoReflect.Descriptor instead.
func (*AddGoodsRes) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{1}
}
func (x *AddGoodsRes) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *AddGoodsRes) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
var File_goods_proto protoreflect.FileDescriptor
var file_goods_proto_rawDesc = []byte{
0x0a, 0x0b, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x53, 0x0a,
0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05,
0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74,
0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74,
0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65,
0x6e, 0x74, 0x22, 0x41, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75,
0x63, 0x63, 0x65, 0x73, 0x73, 0x32, 0x2f, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x26,
0x0a, 0x08, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x0c, 0x2e, 0x41, 0x64, 0x64,
0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x41, 0x64, 0x64, 0x47, 0x6f,
0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x67, 0x6f, 0x6f, 0x64,
0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_goods_proto_rawDescOnce sync.Once
file_goods_proto_rawDescData = file_goods_proto_rawDesc
)
func file_goods_proto_rawDescGZIP() []byte {
file_goods_proto_rawDescOnce.Do(func() {
file_goods_proto_rawDescData = protoimpl.X.CompressGZIP(file_goods_proto_rawDescData)
})
return file_goods_proto_rawDescData
}
var file_goods_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_goods_proto_goTypes = []interface{}{
(*AddGoodsReq)(nil), // 0: AddGoodsReq
(*AddGoodsRes)(nil), // 1: AddGoodsRes
}
var file_goods_proto_depIdxs = []int32{
0, // 0: Hello.AddGoods:input_type -> AddGoodsReq
1, // 1: Hello.AddGoods:output_type -> AddGoodsRes
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_goods_proto_init() }
func file_goods_proto_init() {
if File_goods_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_goods_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsRes); 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_goods_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_goods_proto_goTypes,
DependencyIndexes: file_goods_proto_depIdxs,
MessageInfos: file_goods_proto_msgTypes,
}.Build()
File_goods_proto = out.File
file_goods_proto_rawDesc = nil
file_goods_proto_goTypes = nil
file_goods_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
// HelloClient is the client API for Hello service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloClient interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error)
}
type helloClient struct {
cc grpc.ClientConnInterface
}
func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
return &helloClient{cc}
}
func (c *helloClient) AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error) {
out := new(AddGoodsRes)
err := c.cc.Invoke(ctx, "/Hello/AddGoods", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HelloServer is the server API for Hello service.
type HelloServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
}
// UnimplementedHelloServer can be embedded to have forward compatible implementations.
type UnimplementedHelloServer struct {
}
func (*UnimplementedHelloServer) AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddGoods not implemented")
}
func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
s.RegisterService(&_Hello_serviceDesc, srv)
}
func _Hello_AddGoods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddGoodsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HelloServer).AddGoods(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Hello/AddGoods",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HelloServer).AddGoods(ctx, req.(*AddGoodsReq))
}
return interceptor(ctx, in, info, handler)
}
var _Hello_serviceDesc = grpc.ServiceDesc{
ServiceName: "Hello",
HandlerType: (*HelloServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "AddGoods",
Handler: _Hello_AddGoods_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "goods.proto",
}
(4).生成的.pb.go文件几个重要方法/结构体讲解
1).GoodsServer
GoodsServer结构体: 就是.proto生成的服务,需要实现AddGoods方法
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
}
2).RegisterGoodsServer
RegisterGoodsServer:注册服务用的
func RegisterGoodsServer(s *grpc.Server, srv GoodsServer) {
s.RegisterService(&_Goods_serviceDesc, srv)
}
3).NewGoodsClient
NewGoodsClient:注册客户端
func NewGoodsClient(cc grpc.ClientConnInterface) GoodsClient {
return &goodsClient{cc}
}
(5).在main.go目录下运行命令 go mod tidy,加载生成的.pb.go中引入的包
(6).在main.go中编写服务端代码
package main
import (
"context"
"fmt"
"goods/proto/goodsService"
"net"
"google.golang.org/grpc"
"serverHello/proto/helloService"
)
//rpc远程调用的接口,需要实现goods.proto中定义的Goods服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现GoodsServer的接口
type Goods struct{}
//GoodsServer方法参考goods.pb.go中的接口
/*
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
}
*/
func (this Goods) AddGoods(c context.Context, req *goodsService.AddGoodsReq) (*goodsService.AddGoodsRes, error) {
fmt.Println(req)
return &goodsService.AddGoodsRes{
Message: "增加成功" + req.Title,
Success: true,
}, nil
}
func main() {
//1. 初始一个 grpc 对象
grpcServer := grpc.NewServer()
//2. 注册服务
//helloService.RegisterGoodsServer(grpcServer, &Goods{})
// &Hello{}和 new(Hello)相同
goodsService.RegisterGoodsServer(grpcServer, new(Goods))
//3. 设置监听, 指定 IP、port
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println(err)
}
// 4退出关闭监听
defer listener.Close()
//5、启动服务
grpcServer.Serve(listener)
}
测试服务端是否启动成功,运行go run .main.go,没有保存,说明启动成功:
2).实现增加商品客户端
(1).创建文件main.go
在micro/grpc_demo/client/下
创建文件main.go
然后通过命令go mod init client初始化项目,为以后开发做准备,然后通过命令 go mod tidy 来下载依赖,操作和实现服务端步骤(1)类似,然后运行go run .main.go 命令,测试是否成功,打印出client,说明成功
(2).创建.proto文件
在micro/grpc_demo/server/client/proto下
创建goods.proto文件,
里面的代码和
实现服务端步骤(2)一致
syntax = "proto3"; //proto版本
option go_package= "./goodsService"; //表示在目录goodsService下面生成goods.pb.go,以及对应的包名
//通过service创建一个RPC服务, 生成一个Goods接口, 服务端实现Goods里面的方法, 客户端调用
service Goods { // Goods可以是小写, 无所谓
//通过rpc来指定远程调用的方法:
//AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
rpc AddGoods(AddGoodsReq) returns (AddGoodsRes);
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品
message AddGoodsReq {
string title = 1; //商品标题
double price = 2; //价格
string content = 3; //内容
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
message AddGoodsRes {
string message = 1; // 返回结果描述: 成功的描述以及失败的描述
bool success = 2; //返回结果标识: 是否成功
}
(3).编译.proto文件
使用命令
protoc –go_out=plugins=grpc:. *.proto 对.proto文件进行编译,生成
.pb.go的服务,步骤和
实现服务端步骤(3)一致
生成的文件目录如下:
生成.pb.go代码和实现服务端步骤(3)中生成的.pd,go代码一致
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.15.5
// source: goods.proto
package goodsService
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)
)
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品
type AddGoodsReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` //商品标题
Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` //价格
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` //内容
}
func (x *AddGoodsReq) Reset() {
*x = AddGoodsReq{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsReq) ProtoMessage() {}
func (x *AddGoodsReq) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 AddGoodsReq.ProtoReflect.Descriptor instead.
func (*AddGoodsReq) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{0}
}
func (x *AddGoodsReq) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *AddGoodsReq) GetPrice() float64 {
if x != nil {
return x.Price
}
return 0
}
func (x *AddGoodsReq) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
type AddGoodsRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` // 返回结果描述: 成功的描述以及失败的描述
Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` //返回结果标识: 是否成功
}
func (x *AddGoodsRes) Reset() {
*x = AddGoodsRes{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsRes) ProtoMessage() {}
func (x *AddGoodsRes) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 AddGoodsRes.ProtoReflect.Descriptor instead.
func (*AddGoodsRes) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{1}
}
func (x *AddGoodsRes) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *AddGoodsRes) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
var File_goods_proto protoreflect.FileDescriptor
var file_goods_proto_rawDesc = []byte{
0x0a, 0x0b, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x53, 0x0a,
0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05,
0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74,
0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74,
0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65,
0x6e, 0x74, 0x22, 0x41, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75,
0x63, 0x63, 0x65, 0x73, 0x73, 0x32, 0x2f, 0x0a, 0x05, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x26,
0x0a, 0x08, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x0c, 0x2e, 0x41, 0x64, 0x64,
0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x41, 0x64, 0x64, 0x47, 0x6f,
0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x67, 0x6f, 0x6f, 0x64,
0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_goods_proto_rawDescOnce sync.Once
file_goods_proto_rawDescData = file_goods_proto_rawDesc
)
func file_goods_proto_rawDescGZIP() []byte {
file_goods_proto_rawDescOnce.Do(func() {
file_goods_proto_rawDescData = protoimpl.X.CompressGZIP(file_goods_proto_rawDescData)
})
return file_goods_proto_rawDescData
}
var file_goods_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_goods_proto_goTypes = []interface{}{
(*AddGoodsReq)(nil), // 0: AddGoodsReq
(*AddGoodsRes)(nil), // 1: AddGoodsRes
}
var file_goods_proto_depIdxs = []int32{
0, // 0: Goods.AddGoods:input_type -> AddGoodsReq
1, // 1: Goods.AddGoods:output_type -> AddGoodsRes
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_goods_proto_init() }
func file_goods_proto_init() {
if File_goods_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_goods_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsRes); 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_goods_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_goods_proto_goTypes,
DependencyIndexes: file_goods_proto_depIdxs,
MessageInfos: file_goods_proto_msgTypes,
}.Build()
File_goods_proto = out.File
file_goods_proto_rawDesc = nil
file_goods_proto_goTypes = nil
file_goods_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
// GoodsClient is the client API for Goods service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GoodsClient interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error)
}
type goodsClient struct {
cc grpc.ClientConnInterface
}
func NewGoodsClient(cc grpc.ClientConnInterface) GoodsClient {
return &goodsClient{cc}
}
func (c *goodsClient) AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error) {
out := new(AddGoodsRes)
err := c.cc.Invoke(ctx, "/Goods/AddGoods", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
}
// UnimplementedGoodsServer can be embedded to have forward compatible implementations.
type UnimplementedGoodsServer struct {
}
func (*UnimplementedGoodsServer) AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddGoods not implemented")
}
func RegisterGoodsServer(s *grpc.Server, srv GoodsServer) {
s.RegisterService(&_Goods_serviceDesc, srv)
}
func _Goods_AddGoods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddGoodsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GoodsServer).AddGoods(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Goods/AddGoods",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GoodsServer).AddGoods(ctx, req.(*AddGoodsReq))
}
return interceptor(ctx, in, info, handler)
}
var _Goods_serviceDesc = grpc.ServiceDesc{
ServiceName: "Goods",
HandlerType: (*GoodsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "AddGoods",
Handler: _Goods_AddGoods_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "goods.proto",
}
(4).生成的.pb.go文件几个重要方法/结构体讲解
参考实现服务端步骤(4),和上面一致
(5).在main.go目录下运行命令 go mod tidy,加载生成的.pb.go中引入的包
(6).在main.go中编写客户端代码
package main
//grpc客户端代码
import (
"client/proto/goodsService"
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
// 1、连接服务器
/*
credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
DialOption,用于连接服务器。
*/
grpcClient, err := grpc.Dial("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println(err)
}
//2、注册客户端
client := goodsService.NewGoodsClient(grpcClient)
//3、调用服务端函数, 实现GoodsClient接口:SayHello()
/*
// GoodsClient is the client API for Goods service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GoodsClient interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error)
}
*/
res, err1 := client.AddGoods(context.Background(), &goodsService.AddGoodsReq{
Title: "测试商品",
Price: 20,
Content: "测试商品的内容",
})
if err1 != nil {
fmt.Printf("调用服务端代码失败: %s", err1)
return
}
fmt.Printf("%#vrn", res)
fmt.Printf("调用成功: %s", res.Message)
}
3).测试增加商品客户端调用增加商品服务端微服务是否成功
(1).启动服务端
在server/goods下运行命令 go run .main.go,如下:
(2).启动客服端,请求服务端方法
在client/goods下运行命令 go run .main.go,如下:
返回了服务端的数据,说明微服务操作完成,这时服务端展示如下:
4).实现获取商品列表服务端
在上面相关文件中增加代码
(1).在goods.proto文件中完善代码
在micro/grpc_demo/server/goods/proto/
goods.proto文件
里面完善获取商品相关代码,增加了
rpc AddGoods(AddGoodsReq) returns (AddGoodsRes)
,
message GoodsModel
,修改了
message AddGoodsReq
syntax = "proto3"; //proto版本
option go_package= "./goodsService"; //表示在目录goodsService下面生成goods.pb.go,以及对应的包名
//通过service创建一个RPC服务, 生成一个Goods接口, 服务端实现Goods里面的方法, 客户端调用
service Goods { // Goods可以是小写, 无所谓
//通过rpc来指定远程调用的方法:
//AddGoods方法, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
rpc AddGoods(AddGoodsReq) returns (AddGoodsRes);
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品,字段和数据库表统一
message AddGoodsReq {
string title = 1; //商品标题
double price = 2; //价格
string content = 3; //内容
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
message AddGoodsRes {
string message = 1; // 返回结果描述: 成功的描述以及失败的描述
bool success = 2; //返回结果标识: 是否成功
}
(2).编译.proto文件
使用命令
protoc –go_out=plugins=grpc:. *.proto 对.proto文件进行编译,生成
.pb.go的服务,步骤和
实现增加商品服务端步骤(3)一致
目录如下:
代码如下:
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.15.5
// source: goods.proto
package goodsService
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)
)
// message: 因为增加商品,获取商品,都有商品的Title,Price,Content等数据,
// 故单独定义一个商品的结构体message, 让AddGoodsReq, GetGoodsRes调用
type GoodsModel struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` //商品标题
Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` //价格
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` //内容
}
func (x *GoodsModel) Reset() {
*x = GoodsModel{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GoodsModel) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GoodsModel) ProtoMessage() {}
func (x *GoodsModel) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 GoodsModel.ProtoReflect.Descriptor instead.
func (*GoodsModel) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{0}
}
func (x *GoodsModel) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *GoodsModel) GetPrice() float64 {
if x != nil {
return x.Price
}
return 0
}
func (x *GoodsModel) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品
type AddGoodsReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Goods *GoodsModel `protobuf:"bytes,1,opt,name=goods,proto3" json:"goods,omitempty"` // 定义一个GoodsModel的切片message
}
func (x *AddGoodsReq) Reset() {
*x = AddGoodsReq{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsReq) ProtoMessage() {}
func (x *AddGoodsReq) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 AddGoodsReq.ProtoReflect.Descriptor instead.
func (*AddGoodsReq) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{1}
}
func (x *AddGoodsReq) GetGoods() *GoodsModel {
if x != nil {
return x.Goods
}
return nil
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
type AddGoodsRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` // 返回结果描述: 成功的描述以及失败的描述
Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` //返回结果标识: 是否成功
}
func (x *AddGoodsRes) Reset() {
*x = AddGoodsRes{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsRes) ProtoMessage() {}
func (x *AddGoodsRes) ProtoReflect() protoreflect.Message {
mi := &file_goods_proto_msgTypes[2]
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 AddGoodsRes.ProtoReflect.Descriptor instead.
func (*AddGoodsRes) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{2}
}
func (x *AddGoodsRes) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *AddGoodsRes) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
// message: 获取商品的请求参数message, 可以为空
type GetGoodsReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetGoodsReq) Reset() {
*x = GetGoodsReq{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetGoodsReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetGoodsReq) ProtoMessage() {}
func (x *GetGoodsReq) ProtoReflect() protoreflect.Message {
mi := &file_goods_proto_msgTypes[3]
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 GetGoodsReq.ProtoReflect.Descriptor instead.
func (*GetGoodsReq) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{3}
}
// message: 获取商品返回的参数结果
type GetGoodsRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
GoodsList []*GoodsModel `protobuf:"bytes,1,rep,name=goodsList,proto3" json:"goodsList,omitempty"` // 返回的是一个商品相关的切片
}
func (x *GetGoodsRes) Reset() {
*x = GetGoodsRes{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetGoodsRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetGoodsRes) ProtoMessage() {}
func (x *GetGoodsRes) ProtoReflect() protoreflect.Message {
mi := &file_goods_proto_msgTypes[4]
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 GetGoodsRes.ProtoReflect.Descriptor instead.
func (*GetGoodsRes) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{4}
}
func (x *GetGoodsRes) GetGoodsList() []*GoodsModel {
if x != nil {
return x.GoodsList
}
return nil
}
var File_goods_proto protoreflect.FileDescriptor
var file_goods_proto_rawDesc = []byte{
0x0a, 0x0b, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x52, 0x0a,
0x0a, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74,
0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01,
0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65,
0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
0x74, 0x22, 0x30, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71,
0x12, 0x21, 0x0a, 0x05, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x0b, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x05, 0x67, 0x6f,
0x6f, 0x64, 0x73, 0x22, 0x41, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52,
0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f,
0x64, 0x73, 0x52, 0x65, 0x71, 0x22, 0x38, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64,
0x73, 0x52, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x09, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x4c, 0x69, 0x73,
0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d,
0x6f, 0x64, 0x65, 0x6c, 0x52, 0x09, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x32,
0x57, 0x0a, 0x05, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x47,
0x6f, 0x6f, 0x64, 0x73, 0x12, 0x0c, 0x2e, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52,
0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73,
0x12, 0x26, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x0c, 0x2e, 0x47,
0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x47, 0x65, 0x74,
0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x67, 0x6f,
0x6f, 0x64, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_goods_proto_rawDescOnce sync.Once
file_goods_proto_rawDescData = file_goods_proto_rawDesc
)
func file_goods_proto_rawDescGZIP() []byte {
file_goods_proto_rawDescOnce.Do(func() {
file_goods_proto_rawDescData = protoimpl.X.CompressGZIP(file_goods_proto_rawDescData)
})
return file_goods_proto_rawDescData
}
var file_goods_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_goods_proto_goTypes = []interface{}{
(*GoodsModel)(nil), // 0: GoodsModel
(*AddGoodsReq)(nil), // 1: AddGoodsReq
(*AddGoodsRes)(nil), // 2: AddGoodsRes
(*GetGoodsReq)(nil), // 3: GetGoodsReq
(*GetGoodsRes)(nil), // 4: GetGoodsRes
}
var file_goods_proto_depIdxs = []int32{
0, // 0: AddGoodsReq.goods:type_name -> GoodsModel
0, // 1: GetGoodsRes.goodsList:type_name -> GoodsModel
1, // 2: Goods.AddGoods:input_type -> AddGoodsReq
3, // 3: Goods.GetGoods:input_type -> GetGoodsReq
2, // 4: Goods.AddGoods:output_type -> AddGoodsRes
4, // 5: Goods.GetGoods:output_type -> GetGoodsRes
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_goods_proto_init() }
func file_goods_proto_init() {
if File_goods_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_goods_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GoodsModel); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsRes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetGoodsReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetGoodsRes); 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_goods_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_goods_proto_goTypes,
DependencyIndexes: file_goods_proto_depIdxs,
MessageInfos: file_goods_proto_msgTypes,
}.Build()
File_goods_proto = out.File
file_goods_proto_rawDesc = nil
file_goods_proto_goTypes = nil
file_goods_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
// GoodsClient is the client API for Goods service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GoodsClient interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error)
// 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
GetGoods(ctx context.Context, in *GetGoodsReq, opts ...grpc.CallOption) (*GetGoodsRes, error)
}
type goodsClient struct {
cc grpc.ClientConnInterface
}
func NewGoodsClient(cc grpc.ClientConnInterface) GoodsClient {
return &goodsClient{cc}
}
func (c *goodsClient) AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error) {
out := new(AddGoodsRes)
err := c.cc.Invoke(ctx, "/Goods/AddGoods", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *goodsClient) GetGoods(ctx context.Context, in *GetGoodsReq, opts ...grpc.CallOption) (*GetGoodsRes, error) {
out := new(GetGoodsRes)
err := c.cc.Invoke(ctx, "/Goods/GetGoods", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
// 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error)
}
// UnimplementedGoodsServer can be embedded to have forward compatible implementations.
type UnimplementedGoodsServer struct {
}
func (*UnimplementedGoodsServer) AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddGoods not implemented")
}
func (*UnimplementedGoodsServer) GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetGoods not implemented")
}
func RegisterGoodsServer(s *grpc.Server, srv GoodsServer) {
s.RegisterService(&_Goods_serviceDesc, srv)
}
func _Goods_AddGoods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddGoodsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GoodsServer).AddGoods(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Goods/AddGoods",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GoodsServer).AddGoods(ctx, req.(*AddGoodsReq))
}
return interceptor(ctx, in, info, handler)
}
func _Goods_GetGoods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetGoodsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GoodsServer).GetGoods(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Goods/GetGoods",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GoodsServer).GetGoods(ctx, req.(*GetGoodsReq))
}
return interceptor(ctx, in, info, handler)
}
var _Goods_serviceDesc = grpc.ServiceDesc{
ServiceName: "Goods",
HandlerType: (*GoodsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "AddGoods",
Handler: _Goods_AddGoods_Handler,
},
{
MethodName: "GetGoods",
Handler: _Goods_GetGoods_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "goods.proto",
}
(3).生成的.pb.go文件几个重要方法/结构体讲解
1).GoodsServer
GoodsServer结构体: 就是.proto生成的服务,需要实现AddGoods方法,GetGoods方法
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
// 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error)
}
2).RegisterGoodsServer
RegisterGoodsServer:注册服务用的
func RegisterGoodsServer(s *grpc.Server, srv GoodsServer) {
s.RegisterService(&_Goods_serviceDesc, srv)
}
3).NewGoodsClient
NewGoodsClient:注册客户端
func NewGoodsClient(cc grpc.ClientConnInterface) GoodsClient {
return &goodsClient{cc}
}
(4).在main.go目录下运行命令 go mod tidy,加载生成的.pb.go中引入的包
(5).在main.go中编写服务端代码
增加了 GetGoods方法的代码
package main
import (
"context"
"fmt"
"goods/proto/goodsService"
"net"
"google.golang.org/grpc"
"strconv"
)
//rpc远程调用的接口,需要实现goods.proto中定义的Goods服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现GoodsServer的接口
type Goods struct{}
//GoodsServer方法参考goods.pb.go中的接口
/*
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
// 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error)
}
*/
//增加商品数据
func (this Goods) AddGoods(c context.Context, req *goodsService.AddGoodsReq) (*goodsService.AddGoodsRes, error) {
fmt.Println(req)
//模拟返回操作,正式项目在这里进行数据库的操作即可,根据操作结果,返回相关数据
return &goodsService.AddGoodsRes{
Message: "增加成功" + req.Goods.Title, //需要获取商品title
Success: true,
}, nil
}
//获取商品列表
func (g Goods) GetGoods(c context.Context, req *goodsService.GetGoodsReq) (*goodsService.GetGoodsRes, error) {
// GoodsList []*GoodsModel
var tempList []*goodsService.GoodsModel //定义返回的商品列表切片
//模拟从数据库中获取商品的请求,循环结果,把商品相关数据放入tempList切片中
for i := 0; i < 10; i++ {
tempList = append(tempList, &goodsService.GoodsModel{
Title: "商品" + strconv.Itoa(i), // strconv.Itoa(i): 整型转字符串类型
Price: float64(i), //float64(i): 强制转换整型为浮点型
Content: "测试商品内容" + strconv.Itoa(i),
})
}
return &goodsService.GetGoodsRes{
GoodsList: tempList,
}, nil
}
func main() {
//1. 初始一个 grpc 对象
grpcServer := grpc.NewServer()
//2. 注册服务
//helloService.RegisterGoodsServer(grpcServer, &Goods{})
// &Hello{}和 new(Hello)相同
goodsService.RegisterGoodsServer(grpcServer, new(Goods))
//3. 设置监听, 指定 IP、port
listener, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println(err)
}
// 4退出关闭监听
defer listener.Close()
//5、启动服务
grpcServer.Serve(listener)
}
测试服务端是否启动成功,运行go run .main.go,没有保存,说明启动成功:
5).实现获取商品客户端
在上面相关文件中增加代码
(1).在goods.proto文件中完善代码
在micro/grpc_demo/client/goods/proto/
goods.proto文件
里面完善获取商品相关代码:
增加了
rpc AddGoods(AddGoodsReq) returns (AddGoodsRes)
,
message GoodsModel
,修改了
message AddGoodsReq
syntax = "proto3"; //proto版本
option go_package= "./goodsService"; //表示在目录goodsService下面生成goods.pb.go,以及对应的包名
//通过service创建一个RPC服务, 生成一个Goods接口, 服务端实现Goods里面的方法, 客户端调用
service Goods { // Goods可以是小写, 无所谓
//通过rpc来指定远程调用的方法:
//AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
rpc AddGoods(AddGoodsReq) returns (AddGoodsRes);
//获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
rpc GetGoods(GetGoodsReq) returns (GetGoodsRes);
}
//message: 因为增加商品,获取商品,都有商品的Title,Price,Content等数据,
//故单独定义一个商品的结构体message, 让AddGoodsReq, GetGoodsRes调用
message GoodsModel {
string title = 1; //商品标题
double price = 2; //价格
string content = 3; //内容
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品
message AddGoodsReq {
GoodsModel goods = 1; // 定义一个GoodsModel的切片message
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
message AddGoodsRes {
string message = 1; // 返回结果描述: 成功的描述以及失败的描述
bool success = 2; //返回结果标识: 是否成功
}
//message: 获取商品的请求参数message, 可以为空
message GetGoodsReq {
}
//message: 获取商品返回的参数结果
message GetGoodsRes {
repeated GoodsModel goodsList = 1; // 返回的是一个商品相关的切片
}
(2).编译.proto文件
使用命令
protoc –go_out=plugins=grpc:. *.proto 对.proto文件进行编译,生成
.pb.go的服务,步骤和上面
实现增加商品服务端步骤(3)一致
生成的文件目录如下:
生成.pb.go代码和实现服务端步骤(3)中生成的.pd,go代码一致
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.15.5
// source: goods.proto
package goodsService
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)
)
// message: 因为增加商品,获取商品,都有商品的Title,Price,Content等数据,
// 故单独定义一个商品的结构体message, 让AddGoodsReq, GetGoodsRes调用
type GoodsModel struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` //商品标题
Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` //价格
Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` //内容
}
func (x *GoodsModel) Reset() {
*x = GoodsModel{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GoodsModel) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GoodsModel) ProtoMessage() {}
func (x *GoodsModel) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 GoodsModel.ProtoReflect.Descriptor instead.
func (*GoodsModel) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{0}
}
func (x *GoodsModel) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *GoodsModel) GetPrice() float64 {
if x != nil {
return x.Price
}
return 0
}
func (x *GoodsModel) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
// AddGoodsReq:增加商品
type AddGoodsReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Goods *GoodsModel `protobuf:"bytes,1,opt,name=goods,proto3" json:"goods,omitempty"` // 定义一个GoodsModel的切片message
}
func (x *AddGoodsReq) Reset() {
*x = AddGoodsReq{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsReq) ProtoMessage() {}
func (x *AddGoodsReq) ProtoReflect() protoreflect.Message {
mi := &file_goods_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 AddGoodsReq.ProtoReflect.Descriptor instead.
func (*AddGoodsReq) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{1}
}
func (x *AddGoodsReq) GetGoods() *GoodsModel {
if x != nil {
return x.Goods
}
return nil
}
// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
type AddGoodsRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` // 返回结果描述: 成功的描述以及失败的描述
Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` //返回结果标识: 是否成功
}
func (x *AddGoodsRes) Reset() {
*x = AddGoodsRes{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddGoodsRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddGoodsRes) ProtoMessage() {}
func (x *AddGoodsRes) ProtoReflect() protoreflect.Message {
mi := &file_goods_proto_msgTypes[2]
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 AddGoodsRes.ProtoReflect.Descriptor instead.
func (*AddGoodsRes) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{2}
}
func (x *AddGoodsRes) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *AddGoodsRes) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
// message: 获取商品的请求参数message, 可以为空
type GetGoodsReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetGoodsReq) Reset() {
*x = GetGoodsReq{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetGoodsReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetGoodsReq) ProtoMessage() {}
func (x *GetGoodsReq) ProtoReflect() protoreflect.Message {
mi := &file_goods_proto_msgTypes[3]
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 GetGoodsReq.ProtoReflect.Descriptor instead.
func (*GetGoodsReq) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{3}
}
// message: 获取商品返回的参数结果
type GetGoodsRes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
GoodsList []*GoodsModel `protobuf:"bytes,1,rep,name=goodsList,proto3" json:"goodsList,omitempty"` // 返回的是一个商品相关的切片
}
func (x *GetGoodsRes) Reset() {
*x = GetGoodsRes{}
if protoimpl.UnsafeEnabled {
mi := &file_goods_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetGoodsRes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetGoodsRes) ProtoMessage() {}
func (x *GetGoodsRes) ProtoReflect() protoreflect.Message {
mi := &file_goods_proto_msgTypes[4]
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 GetGoodsRes.ProtoReflect.Descriptor instead.
func (*GetGoodsRes) Descriptor() ([]byte, []int) {
return file_goods_proto_rawDescGZIP(), []int{4}
}
func (x *GetGoodsRes) GetGoodsList() []*GoodsModel {
if x != nil {
return x.GoodsList
}
return nil
}
var File_goods_proto protoreflect.FileDescriptor
var file_goods_proto_rawDesc = []byte{
0x0a, 0x0b, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x52, 0x0a,
0x0a, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74,
0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01,
0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65,
0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
0x74, 0x22, 0x30, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71,
0x12, 0x21, 0x0a, 0x05, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x0b, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x05, 0x67, 0x6f,
0x6f, 0x64, 0x73, 0x22, 0x41, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52,
0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f,
0x64, 0x73, 0x52, 0x65, 0x71, 0x22, 0x38, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64,
0x73, 0x52, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x09, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x4c, 0x69, 0x73,
0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x4d,
0x6f, 0x64, 0x65, 0x6c, 0x52, 0x09, 0x67, 0x6f, 0x6f, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x32,
0x57, 0x0a, 0x05, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x47,
0x6f, 0x6f, 0x64, 0x73, 0x12, 0x0c, 0x2e, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52,
0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x41, 0x64, 0x64, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73,
0x12, 0x26, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x12, 0x0c, 0x2e, 0x47,
0x65, 0x74, 0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x47, 0x65, 0x74,
0x47, 0x6f, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x67, 0x6f,
0x6f, 0x64, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_goods_proto_rawDescOnce sync.Once
file_goods_proto_rawDescData = file_goods_proto_rawDesc
)
func file_goods_proto_rawDescGZIP() []byte {
file_goods_proto_rawDescOnce.Do(func() {
file_goods_proto_rawDescData = protoimpl.X.CompressGZIP(file_goods_proto_rawDescData)
})
return file_goods_proto_rawDescData
}
var file_goods_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_goods_proto_goTypes = []interface{}{
(*GoodsModel)(nil), // 0: GoodsModel
(*AddGoodsReq)(nil), // 1: AddGoodsReq
(*AddGoodsRes)(nil), // 2: AddGoodsRes
(*GetGoodsReq)(nil), // 3: GetGoodsReq
(*GetGoodsRes)(nil), // 4: GetGoodsRes
}
var file_goods_proto_depIdxs = []int32{
0, // 0: AddGoodsReq.goods:type_name -> GoodsModel
0, // 1: GetGoodsRes.goodsList:type_name -> GoodsModel
1, // 2: Goods.AddGoods:input_type -> AddGoodsReq
3, // 3: Goods.GetGoods:input_type -> GetGoodsReq
2, // 4: Goods.AddGoods:output_type -> AddGoodsRes
4, // 5: Goods.GetGoods:output_type -> GetGoodsRes
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_goods_proto_init() }
func file_goods_proto_init() {
if File_goods_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_goods_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GoodsModel); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddGoodsRes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetGoodsReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_goods_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetGoodsRes); 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_goods_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_goods_proto_goTypes,
DependencyIndexes: file_goods_proto_depIdxs,
MessageInfos: file_goods_proto_msgTypes,
}.Build()
File_goods_proto = out.File
file_goods_proto_rawDesc = nil
file_goods_proto_goTypes = nil
file_goods_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
// GoodsClient is the client API for Goods service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GoodsClient interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error)
// 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
GetGoods(ctx context.Context, in *GetGoodsReq, opts ...grpc.CallOption) (*GetGoodsRes, error)
}
type goodsClient struct {
cc grpc.ClientConnInterface
}
func NewGoodsClient(cc grpc.ClientConnInterface) GoodsClient {
return &goodsClient{cc}
}
func (c *goodsClient) AddGoods(ctx context.Context, in *AddGoodsReq, opts ...grpc.CallOption) (*AddGoodsRes, error) {
out := new(AddGoodsRes)
err := c.cc.Invoke(ctx, "/Goods/AddGoods", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *goodsClient) GetGoods(ctx context.Context, in *GetGoodsReq, opts ...grpc.CallOption) (*GetGoodsRes, error) {
out := new(GetGoodsRes)
err := c.cc.Invoke(ctx, "/Goods/GetGoods", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
// 通过rpc来指定远程调用的方法:
// AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
// 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error)
}
// UnimplementedGoodsServer can be embedded to have forward compatible implementations.
type UnimplementedGoodsServer struct {
}
func (*UnimplementedGoodsServer) AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddGoods not implemented")
}
func (*UnimplementedGoodsServer) GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetGoods not implemented")
}
func RegisterGoodsServer(s *grpc.Server, srv GoodsServer) {
s.RegisterService(&_Goods_serviceDesc, srv)
}
func _Goods_AddGoods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddGoodsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GoodsServer).AddGoods(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Goods/AddGoods",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GoodsServer).AddGoods(ctx, req.(*AddGoodsReq))
}
return interceptor(ctx, in, info, handler)
}
func _Goods_GetGoods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetGoodsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GoodsServer).GetGoods(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/Goods/GetGoods",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GoodsServer).GetGoods(ctx, req.(*GetGoodsReq))
}
return interceptor(ctx, in, info, handler)
}
var _Goods_serviceDesc = grpc.ServiceDesc{
ServiceName: "Goods",
HandlerType: (*GoodsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "AddGoods",
Handler: _Goods_AddGoods_Handler,
},
{
MethodName: "GetGoods",
Handler: _Goods_GetGoods_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "goods.proto",
}
也可以直接复制server/goods/goodsService/goods.pb.go里面的代码,直接使用,效果是一样的
(4).生成的.pb.go文件几个重要方法/结构体讲解
参考实现增加商品服务端步骤(4),和上面一致
(5).在main.go目录下运行命令 go mod tidy,加载生成的.pb.go中引入的包
(6).在main.go中编写客户端代码
增加res2, _ := client.
GetGoods(context.Background(), &goodsService.GetGoodsReq{})获取商品的方法
package main
import (
"client/proto/goodsService"
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
// 1、连接服务器
/*
credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
DialOption,用于连接服务器。
*/
grpcClient, err := grpc.Dial("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println(err)
}
//2、注册客户端
client := goodsService.NewGoodsClient(grpcClient)
//增加
res1, _ := client.AddGoods(context.Background(), &goodsService.AddGoodsReq{
Goods: &goodsService.GoodsModel{
Title: "测试商品",
Price: 20,
Content: "测试商品的内容",
},
})
fmt.Println(res1.Message)
fmt.Println(res1.Success)
//获取商品数据
res2, _ := client.GetGoods(context.Background(), &goodsService.GetGoodsReq{})
fmt.Printf("%#v", res2.GoodsList)
for i := 0; i < len(res2.GoodsList); i++ {
fmt.Printf("%#vrn", res2.GoodsList[i])
}
}
6).测试获取商品客户端调用获取商品服务端微服务是否成功
(1).启动服务端
在server/goods下运行命令 go run .main.go,如下:
(2).启动客服端,请求服务端方法
在client/goods下运行命令 go run .main.go,如下:
返回了服务端的数据,说明微服务操作完成,这时服务端展示如下:
还有就是获取一条商品数据,这个在.proto的service Goods中增加代码:
rpc GetOneGoods(GetOneGoodsReq) returns (GetOneGoodsRes)
并实现GetOneGoodsReq,GetOneGoodsRes,相关代码如下:
//获取一条数据传入参数
message GetOneGoodsReq {
int32 id = 1;
}
//获取一条数据返回参数
message GetOneGoodsRes {
GoodsModel oneGoods=1;
}
其余逻辑和上面讲解方案一致,ok,商品的增加,查询案例操作完成
原文地址:https://blog.csdn.net/zhoupenghui168/article/details/130959000
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_47472.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!