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

news2024/12/23 18:16:16

一.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使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/603502.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows Pyqt5配置环境过程(pycharm Anaconda)

必要安装 Anaconda下载地址 Pycharm下载地址 这两个推荐2019年左右的版本就行了&#xff0c;安装的时候选择“add path” Anaconda换源 换源之后叉掉终端之后再创建环境 Anaconda常用命令 Anaconda换源应该是只对conda install 有用&#xff0c;pip还要换源 使用清华源进行…

java-集合

java-集合 一、集合体系结构 集合类的特点 ​ 提供一种存储空间可变的存储模型&#xff0c;存储的数据容量可以随时发生改变 集合类的体系图 ​ 二、单列集合 2.1 Collection集合&#xff08;接口&#xff09; Collection集合概述 是单列集合的顶层接口&#xff0c;它表示一…

汇编栈寄存器SS与SP使用

入栈时,栈段地址与偏移地址计算 使用a命令输入下面汇编,然后使用u命令查看 写入汇编指令到内存 修改CS:IP指向当前代码段 使用t命令执行汇编指令,详细执行如下图标号 注意每行指令执行后寄存器变化. 取内存段单元数据 将内存段单元数据送入寄存器, 多次送入数据到同一寄存…

chatgpt赋能python:Python分词处理的重要性

Python分词处理的重要性 随着互联网的飞速发展&#xff0c;大数据的普及与应用越来越广泛。人们需要从海量的数据中找到自己需要的信息。因此&#xff0c;自然语言处理技术被广泛应用&#xff0c;其中分词技术是自然语言处理中最基础的一项技术。 在这个领域中&#xff0c;Py…

JS逆向——借助playwright实现逆向

原理&#xff1a; 1.修改js文件&#xff0c;将加密方法设定为全局变量并调用。 2.使用playwright替换浏览器加载的js文件 3.在python中通过playwright实现js注入获取加密结果 实现&#xff1a; 以下面链接为例&#xff1a; Scrape | Movie 1.首先&#xff0c;要知道页面…

方法引用相关知识点

这里写目录标题 方法引用方法引用符简介代码演示 Lambda表达式支持的方法引用引用 类方法简介使用 引用对象的实例方法简介操作 引用类的实例方法简介具体代码 引用构造器简介代码演示 二级目录二级目录二级目录二级目录二级目录二级目录 方法引用 方法引用符 简介 注意 这里…

JDK SPI、Spring SPI、Dubbo SPI三种机制的细节与演化

JDK SPI、Spring SPI、Dubbo SPI三种机制的细节与演化 SPI机制 SPI机制的应用 JDBC中加载驱动 Spring SPI Dubbo SPI SPI深入理解 API与SPI的区别 ServiceLoader JDK SPI、Spring SPI、Dubbo SPI综合对比 SPI机制 Java SPI&#xff08;Service Provider Interface&am…

Linux4.3Apache配置与应用

文章目录 计算机系统5G云计算第一章 LINUX Apache配置与应用及网页优化一、构建虚拟 Web 主机二、基于域名的虚拟主机1.为虚拟主机提供域名解析2.为虚拟主机准备网页文档3.添加虚拟主机配置4.设置访问控制5.Options指令解释6.AllowOverride指令解释7.地址限制策略8.加载独立的配…

10个Chatgpt国内可用镜像网页

自 2022 年底推出以来&#xff0c; ChatGPT一直风靡全球&#xff0c;原因显而易见。革命性的聊天机器人 AI 可以完成数量惊人的任务&#xff0c;从进行对话到撰写整篇学期论文。此外&#xff0c;ChatGPT 可以做很多您不知道的事情——从制作品牌徽标到创作音乐等等。 根据Open…

浮点数在内存中的储存(C语言)

浮点数在内存中的储存 一、浮点数简介二、浮点数在内存中的储存1.IEEE 754标准2.单精度浮点数的内存储存3.双精度浮点数的内存储存4.IEEE 754对有效数字M和指数E&#xff0c;还有一些特别规定 三、总结 一、浮点数简介 浮点数是计算机科学中的一种数据类型&#xff0c;用于存储…

2023第四届中国奢侈品数字化创新峰会

2023第四届中国奢侈品数字化创新峰会将于6月26日-27日在上海召开。 此次会议将线上线下同步举行&#xff0c;重点讨论后疫情时代奢侈品行业主要的数字化战略与趋势&#xff0c;探讨数字化技术如何成为与消费者沟通的有效手段和关键媒介&#xff0c;如何做好线上消费者洞察&…

【HashSet集合】概述和特点

HashSet集合概述和特点 1.HashSet概述 HashSet在java.util包下&#xff0c;使用时需要到爆&#xff0c;它是set接口的一个实现类&#xff0c;它所存储的元素是不可重复的&#xff0c;并且元素都是无序的&#xff0c;HashSet是根据对象的哈希值来确定元素在集合中的存储位置&a…

代码随想录第48天

1.打家劫舍&#xff1a; 动规五部曲分析如下&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i]。 2.确定递推公式 决定dp[i]的因素就是第…

chatgpt赋能python:Python分词方法——NLP技术的重要组成部分

Python分词方法——NLP技术的重要组成部分 自然语言处理&#xff08;NLP&#xff09;是人工智能领域的热点研究方向之一。而分词作为NLP技术的重要组成部分&#xff0c;对于中文文本的处理尤为重要。Python作为一种便捷、易学、高效的编程语言&#xff0c;拥有丰富的分词工具。…

陪诊系统开发|陪诊小程序源码|陪诊小程序源码开发

随着互联网的发展&#xff0c;人们对于医疗服务的需求越来越高&#xff0c;而移动互联网的普及也让医疗服务得以更加便捷高效地提供给用户。陪诊小程序是一款为顾客提供陪诊服务的应用程序&#xff0c;可以帮助患者更好地接受医疗服务&#xff0c;同时也为医疗服务的提供者带来…

【Collection集合的遍历】

Collection集合的遍历 Iterator&#xff1a;迭代器是集合的专用的遍历的方式&#xff0c;使用时也需要导包 Iterator iterator()&#xff1a;返回集合中元素的迭代器&#xff0c;通过集合的iterator()方法得到迭代器使用过集合的iterator()方法得到的&#xff0c;所以说它是依…

压缩感知重构之分段弱正交匹配追踪法

算法的重构是压缩感知中重要的一步&#xff0c;是压缩感知的关键之处。因为重构算法关系着信号能否精确重建&#xff0c;国内外的研究学者致力于压缩感知的信号重建&#xff0c;并且取得了很大的进展&#xff0c;提出了很多的重构算法&#xff0c;每种算法都各有自己的优缺点&a…

压缩感知重构之基追踪

压缩感知中很重要的一步就是重构算法&#xff0c;重构算法关系着重建信号的质量。基追踪算法是凸松弛法是很有代表性的一种算法。 由于我们所要求解的问题是方程的个数远远大于未知数的个数&#xff0c;用0范数求解是很难求解出来的&#xff0c;这样就找到一种用范数来代替范数…

实训笔记-6.2

实训笔记 6.2一、座右铭二、新学Java知识1、算法1.1 常见的排序算法1.1.1冒泡排序&#xff08;Bubble Sort&#xff09;1.1.1.1 思想1.1.1.2 代码1.1.1.3 算法空间复杂度和时间复杂度的计算 1.1.2线性查找算法1.1.2.1 思想1.1.2.2 代码1.1.2.3 算法空间复杂度和时间复杂度的计算…

【paddlecls】多机多卡-linux(一:环境搭建)

1. 安装docker&#xff08;引擎&#xff09;&#xff1a; &#xff08;https://docs.docker.com/engine/install/ubuntu/&#xff09; Install Docker Engine on Ubuntu To get started with Docker Engine on Ubuntu, make sure you meet the prerequisites, and then follo…