[golang 微服务] 4. gRPC介绍,Protobuf结合gRPC 创建微服务

一.gRPC框架的介绍

  1. 简介

gRPC是一个 高性能开源通用 RPC 框架面向移动端HTTP/2 设计,目前提供 C、Java 和 Go语言版本,分别是:grpc, grpc-java, grpc-go,其中 C 版本支持 C, C++, Node.js, Python, Ruby,
Objective-C, PHP 和 C# 支持
  1. gRPC 特点

  • (1).提供几乎所有主流语言的实现,打破语言隔阂

  • (2).基于 HTTP/2 标准设计,带来诸如双向流流控头部压缩单 TCP 连接上的多复用请求等

  • (3).默认使用Protocol Buffers序列化,性能相较于RESTful Json好很多

  • (4).工具链成熟代码生成便捷,开箱即用

  • (5).支持双向流式的请求和响应,对批量处理、低延时场景友好

这些特性使得其在 移动设备上表现更好, 更省电节省空间占用

有了gRPC, 可以一次性的在一个 .proto 文件中定义服务,并使用任何支持它的语言去实现客户端

和服务端,gRPC默认使用protocol buffers,它是google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如JSON),可以用proto创建GRPC服务,用protocol buffers消息类型来定义方法参数返回类型

在gRPC客户端可以直接调用不同服务器上的远程程序,就想调用本地程序一样,很容易构建分布式应用和服务,和很多RPC系统一样,服务负责实现定义好的接口处理客户端请求,客户端根据接口描述

直接调用需要的服务,客户端和服务器可以分别使用gRPC支持的不同语言实现

参考资料

gRPC 官方文档中文版:http://doc.oschina.net/grpc?t=60133

gRPC官网:https://grpc.io

二.gRPC的使用

GRPC使用的包是 google.golang.org/grpc ,可以在项目中使用 go get -u -v
google.golang.org/grpc 来下载包,也可以使用 go mod tidy下载包,如果从Protobuf的角度看,gRPC只不过是一个针对service接口生成代码的生成器,下面举例介绍gRPC的用法
  1. 案例1-实现基本的grpc微服务

1).实现服务端

(1).创建文件main.go

在micro/grpc_demo/server/hello下 创建文件main.go
package main

import "fmt"

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

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

然后通过命令go mod init hello初始化项目,为以后开发做准备,然后通过命令 go mod tidy 来下载依赖,如下图:

然后运行go run .\main.go 命令,测试是否成功:

打印出hello,说明成功

(2).创建.proto文件

在micro/grpc_demo/server/hello/proto下 创建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).编译.proto文件

使用命令 protoc --go_out=plugins=grpc:. *.proto 对.proto文件进行编译,生成 .pb.go的服务

生成的文件目录如下:

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
//     protoc-gen-go v1.26.0
//     protoc        v3.15.5
// source: hello.proto

package helloService

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

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

// message 为传入的参数进行定义消息:结构体类型, 这样就要求客户端传入一个结构体,结构体有一个字符串类型的name参数
type HelloReq struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}

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

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

func (*HelloReq) ProtoMessage() {}

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

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

func (x *HelloReq) GetName() string {
    if x != nil {
        return x.Name
    }
    return ""
}

// message 为返回的参数进行定义消息:结构体类型, 这样就要求服务端返回一个结构体,结构体有一个字符串类型的message参数
type HelloRes struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}

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

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

func (*HelloRes) ProtoMessage() {}

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

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

func (x *HelloRes) GetMessage() string {
    if x != nil {
        return x.Message
    }
    return ""
}

var File_hello_proto protoreflect.FileDescriptor

var file_hello_proto_rawDesc = []byte{
    0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a,
    0x08, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
    0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x24, 0x0a,
    0x08, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73,
    0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73,
    0x61, 0x67, 0x65, 0x32, 0x29, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x20, 0x0a, 0x08,
    0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x09, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
    0x52, 0x65, 0x71, 0x1a, 0x09, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x42, 0x10,
    0x5a, 0x0e, 0x2e, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
    0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
    file_hello_proto_rawDescOnce sync.Once
    file_hello_proto_rawDescData = file_hello_proto_rawDesc
)

func file_hello_proto_rawDescGZIP() []byte {
    file_hello_proto_rawDescOnce.Do(func() {
        file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData)
    })
    return file_hello_proto_rawDescData
}

var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_hello_proto_goTypes = []interface{}{
    (*HelloReq)(nil), // 0: HelloReq
    (*HelloRes)(nil), // 1: HelloRes
}
var file_hello_proto_depIdxs = []int32{
    0, // 0: Hello.SayHello:input_type -> HelloReq
    1, // 1: Hello.SayHello:output_type -> HelloRes
    1, // [1:2] is the sub-list for method output_type
    0, // [0:1] is the sub-list for method input_type
    0, // [0:0] is the sub-list for extension type_name
    0, // [0:0] is the sub-list for extension extendee
    0, // [0:0] is the sub-list for field type_name
}

func init() { file_hello_proto_init() }
func file_hello_proto_init() {
    if File_hello_proto != nil {
        return
    }
    if !protoimpl.UnsafeEnabled {
        file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*HelloReq); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
        file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
            switch v := v.(*HelloRes); i {
            case 0:
                return &v.state
            case 1:
                return &v.sizeCache
            case 2:
                return &v.unknownFields
            default:
                return nil
            }
        }
    }
    type x struct{}
    out := protoimpl.TypeBuilder{
        File: protoimpl.DescBuilder{
            GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
            RawDescriptor: file_hello_proto_rawDesc,
            NumEnums:      0,
            NumMessages:   2,
            NumExtensions: 0,
            NumServices:   1,
        },
        GoTypes:           file_hello_proto_goTypes,
        DependencyIndexes: file_hello_proto_depIdxs,
        MessageInfos:      file_hello_proto_msgTypes,
    }.Build()
    File_hello_proto = out.File
    file_hello_proto_rawDesc = nil
    file_hello_proto_goTypes = nil
    file_hello_proto_depIdxs = nil
}

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

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

// HelloClient is the client API for Hello service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloClient interface {
    // 通过rpc来指定远程调用的方法:
    // SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
    SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error)
}

type helloClient struct {
    cc grpc.ClientConnInterface
}

func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
    return &helloClient{cc}
}

func (c *helloClient) SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error) {
    out := new(HelloRes)
    err := c.cc.Invoke(ctx, "/Hello/SayHello", in, out, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

// HelloServer is the server API for Hello service.
type HelloServer interface {
    // 通过rpc来指定远程调用的方法:
    // SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
    SayHello(context.Context, *HelloReq) (*HelloRes, error)
}

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

func (*UnimplementedHelloServer) SayHello(context.Context, *HelloReq) (*HelloRes, error) {
    return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}

func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
    s.RegisterService(&_Hello_serviceDesc, srv)
}

func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    in := new(HelloReq)
    if err := dec(in); err != nil {
        return nil, err
    }
    if interceptor == nil {
        return srv.(HelloServer).SayHello(ctx, in)
    }
    info := &grpc.UnaryServerInfo{
        Server:     srv,
        FullMethod: "/Hello/SayHello",
    }
    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
        return srv.(HelloServer).SayHello(ctx, req.(*HelloReq))
    }
    return interceptor(ctx, in, info, handler)
}

var _Hello_serviceDesc = grpc.ServiceDesc{
    ServiceName: "Hello",
    HandlerType: (*HelloServer)(nil),
    Methods: []grpc.MethodDesc{
        {
            MethodName: "SayHello",
            Handler:    _Hello_SayHello_Handler,
        },
    },
    Streams:  []grpc.StreamDesc{},
    Metadata: "hello.proto",
}

(4).生成的.pb.go文件几个重要方法/结构体讲解

1).HelloServer
HelloServer结构体: 就是.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集群

猜你喜欢

转载自blog.csdn.net/zhoupenghui168/article/details/130959000