[golang microservices] 4. Introduction to gRPC, Protobuf combined with gRPC to create microservices

1.g Introduction to the RPC framework

  1. Introduction

gRPC is a high-performance , open-source and general-purpose RPC framework designed for mobile terminals and HTTP/2 . It currently provides C, Java and Go language versions, namely: grpc, grpc-java, grpc-go, and the C version supports C , C++, Node.js, Python, Ruby,
Objective-C, PHP and C# support
  1. gRPC features

  • (1). Provide the implementation of almost all mainstream languages, breaking the language barrier

  • (2). Designed based on the HTTP/2 standard , bringing things such as bidirectional flow , flow control , header compression , multiplexing requests on a single TCP connection , etc.

  • (3). Protocol Buffers serialization is used by default , and the performance is much better than RESTful Json

  • (4). The tool chain is mature , the code generation is convenient , and it can be used out of the box

  • (5). Support two-way streaming requests and responses , friendly to batch processing and low-latency scenarios

These features make it perform better on mobile devices , saving power and space

With gRPC, you can define a service in a .proto file once, and use any language that supports it to implement the client

And on the server side, gRPC uses protocol buffers by default, which is a set of mature structured data serialization mechanisms open sourced by Google (of course, other data formats such as JSON can also be used). GRPC services can be created with proto and defined with protocol buffers message types Method parameters and return types .

The gRPC client can directly call remote programs on different servers, just like calling local programs. It is easy to build distributed applications and services . Like many RPC systems, services are responsible for implementing defined interfaces and processing client requests . Clients end according to the interface description

Directly call the required services, the client and the server can be implemented in different languages ​​supported by gRPC

References

gRPC official document Chinese version: http://doc.oschina.net/grpc?t=60133

gRPC official website: https://grpc.io

2. The use of gRPC

The package used by GRPC is google.golang.org/grpc , you can use go get -u -v
google.golang.org/grpc to download the package in the project , or you can use go mod tidy to download the package, if you look at it from the perspective of Protobuf , gRPC is just a generator for generating code for the service interface. The following example introduces the usage of gRPC
  1. Case 1 - Implementing basic grpc microservices

1). Realize the server

(1). Create the file main.go

Create the file main.go under micro/grpc_demo/server/hello
package main

import "fmt"

//rpc远程调用的接口,需要实现hello.proto中定义的Hello接口,以及里面的方法

func main()  {
    fmt.Println("hello")
}

Then use the command go mod init hello to initialize the project to prepare for future development, and then use the command go mod tidy to download dependencies, as shown below:

Then run the go run .\main.go command to test whether it is successful:

Print out hello, indicating success

(2). Create a .proto file

Create hello.proto file under micro/grpc_demo/server/hello/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). Compile the .proto file

使用命令 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结构体: 就是.proto生成的服务,需要实现SayHello方法

// HelloServer is the server API for Hello service.
type HelloServer interface {
    // 通过rpc来指定远程调用的方法:
    // SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
    SayHello(context.Context, *HelloReq) (*HelloRes, error)
}
2).RegisterHelloServer
RegisterHelloServer:注册服务用的
func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
    s.RegisterService(&_Hello_serviceDesc, srv)
}
3).NewHelloClient
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

在micro/grpc_demo/client/hello下 创建文件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文件几个重要方法/结构体讲解

参考实现服务端步骤(4),和上面一致

(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("%#v\r\n", res)
    fmt.Printf("调用成功: %s", res.Message)
}

3).测试客户端调用服务端微服务是否成功

(1).启动服务端

在server/hello下运行命令 go run .\main.go,如下:

(2).启动客服端,请求服务端方法

在client/hello下运行命令 go run .\main.go,如下:

返回了服务端的数据,说明微服务操作完成,这时服务端展示如下:

  1. 案例2- 实现商品的增加,获取商品列表

要求通过AddGoods 增加一个商品数据,通过GetGoods 获取商品列表
先实现通过AddGoods 增加一个商品数据的微服务

1).实现增加商品服务端

(1).创建文件main.go

在micro/grpc_demo/server/goods下 创建文件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文件

在micro/grpc_demo/server/goods/proto下 创建goods.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("%#v\r\n", 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
也可以把server里面的goods.proto直接复制过来使用,效果都是一样的
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("%#v\r\n", res2.GoodsList[i])
    }
}

6).测试获取商品客户端调用获取商品服务端微服务是否成功

(1).启动服务端

在server/goods下运行命令 go run .\main.go,如下:

(2).启动客服端,请求服务端方法

在client/goods下运行命令 go run .\main.go,如下:

返回了服务端的数据,说明微服务操作完成,这时服务端展示如下:

还有就是获取一条商品数据,这个在.protoservice Goods中增加代码:

rpc GetOneGoods(GetOneGoodsReq) returns (GetOneGoodsRes)

并实现GetOneGoodsReq,GetOneGoodsRes,相关代码如下:

//获取一条数据传入参数
message GetOneGoodsReq {
    int32 id = 1;
}

//获取一条数据返回参数
message GetOneGoodsRes {
    GoodsModel oneGoods=1;
}

其余逻辑和上面讲解方案一致,ok,商品的增加,查询案例操作完成

[上一节][golang 微服务] 3. ProtoBuf认识,安装以及golang 中ProtoBuf使用

[下一节][golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群

Guess you like

Origin blog.csdn.net/zhoupenghui168/article/details/130959000