当前位置: 首页 > news >正文

Kubernetes控制平面组件:Kubelet详解(四):gRPC 与 CRI gRPC实现

云原生学习路线导航页(持续更新中)

  • kubernetes学习系列快捷链接
    • Kubernetes架构原则和对象设计(一)
    • Kubernetes架构原则和对象设计(二)
    • Kubernetes架构原则和对象设计(三)
    • Kubernetes控制平面组件:etcd(一)
    • Kubernetes控制平面组件:etcd(二)
    • Kubernetes控制平面组件:API Server详解(一)
    • Kubernetes控制平面组件:API Server详解(二)
    • Kubernetes控制平面组件:调度器Scheduler(一)
    • Kubernetes控制平面组件:调度器Scheduler(二)
    • Kubernetes控制平面组件:Controller Manager详解
    • Kubernetes控制平面组件:Controller Manager 之 内置Controller详解
    • Kubernetes控制平面组件:Controller Manager 之 NamespaceController 全方位讲解
    • Kubernetes控制平面组件:Kubelet详解(一):API接口层介绍
    • Kubernetes控制平面组件:Kubelet详解(二):核心功能层
    • Kubernetes控制平面组件:Kubelet详解(三):CRI 容器运行时接口层

本文是 kubernetes 的控制面组件 kubelet 系列文章第四篇,主要讲解了gRPC的基本概念,工作流程、关键特性等,并对protobuf的安装方法、使用方法 等做了介绍

  • 希望大家多多 点赞 关注 评论 收藏,作者会更有动力继续编写技术文章

1.为什么学习grpc

  • 在上一节 Kubernetes控制平面组件:Kubelet详解(三):CRI 容器运行时接口层 中我们提到了 CRI是基于grpc的容器运行时接口标准,kubelet与实际运行时通过grpc交互,使用protobuf协议通信。那么grpc究竟是什么东西?
  • 本节将学习下grpc的相关内容

2.RPC 与 gRPC 简介

2.1.应用架构的变化

2.1.1.单体架构

  • 一旦某个服务宕机,会引起整个应用不可用,隔离性差
  • 只能整体应用进行伸缩,浪费资源,可伸缩性差
  • 代码耦合在一起,可维护性差

2.1.2.微服务架构

  • 解决了单体架构的弊端,但同时引入了新问题:
    • 代码冗余
    • 服务和服务之间存在调用关系
  • 微服务调用细节:
    • 服务拆分后,调用变为进程间、服务器间的通信。
    • 需发起网络调用(如 HTTP),但 HTTP 性能较低。
  • 解决方案:引入 RPC(远程过程调用),通过自定义 TCP 协议提升传输效率。

2.2.RPC简介

2.2.1.RPC是什么

  • RPC(Remote Procedure Call,远程过程调用)
    • 全称:Remote Procedure Call
    • 定义:一种用于屏蔽分布式计算中各种调用细节的协议,使开发者能够像调用本地函数一样直接调用远程函数。
      在这里插入图片描述

2.2.2.RPC 客户端与服务端通信过程

  1. 客户端发送数据(以字节流形式传输)
  2. 服务端接收并解析数据,根据预定义约定执行对应操作
  3. 服务端将执行结果返回给客户端

2.2.1.RPC 的核心作用

  1. 封装优化:RPC就是将上述通信过程封装,简化操作流程
  2. 协议标准化:采用公认协议实现规范化通信
  3. 价值创造:通过框架工具直接或间接产生经济效益

2.3.gRPC简介

2.3.1.gRPC简介

  • gRPC官方定义
    • 英文原文:A high-performance, open-source universal RPC framework
    • 中文释义:gRPC是一个高性能、开源的通用RPC框架。
  • gprc官网:https://grpc.io/
  • grpc中文文档:https://doc.oschina.net/grpc
  • 简单理解,rpc是一种通信规范,grpc是对rpc协议的落地实现
    • grpc 的g表示谷歌,并非go,grpc是支持多语言的
      在这里插入图片描述

2.3.2.gRPC核心概念

  • 角色定义
    • Client:调用方,客户端
    • Server:被调用方,服务端
  • 服务定义思想
    • 通过语言无关的方式描述服务,包括:
      • 服务名称
      • 可调用方法
      • 方法的入参与回参格式

2.3.3.gRPC工作流程

  • client端:直接调用预定义的方法即可获得预期的结果,gRPC自动处理底层通信细节
  • server端:只需实现定义的方法逻辑,gRPC自动处理底层通信细节

2.3.4.gRPC关键特性

  • 接口约定模式
    • 类似定义接口,Server实现接口,Client调用server实现的接口代理对象
    • 其他的内容如通信、序列化等底层细节都交给gRPC
  • 语言无关性
    • 支持跨语言调用(如C++服务端 + Golang/Java客户端)
    • 服务定义与编解码过程,均与语言无关
      在这里插入图片描述

2.3.5.gRPC安装

go get google.golang.org/grpc

3.Protocol Buffers 详解

3.1.Protocol Buffers 简介

3.1.1.Protocol Buffers 是什么

  • Protocol Buffers 是谷歌开源的一种数据格式,通常称为 protobuf,适合高性能,对响应速度有要求的数据传输场景。
    • gRPC使用了 Protocol Buffers(protobuf) 进行数据的序列化、反序列化
    • profobuf 是二进制数据格式,需要编码和解码。
    • profobuf 数据本身不具有可读性,只能反序列化之后得到真正可读的数据。
  • 怎么理解 protobuf ?
    • 可以把他当成一个 代码生成工具以及序列化工具
    • 这个工具可以把定义的方法,转换成特定语言的代码。比如你定义了一种类型的参数,他会帮你转换成Golang中的struct结构体,你定义的方法,他会帮你转换成func函数。
    • 此外,在发送请求和接受响应的时候,这个工具还会完成对应的 编码和解码 工作,将你即将发送的数据编码成gRPC能够传输的形式,又或者 解码 接收到的数据格式 为 具体语言的某种数据格式
  • 什么是序列化/反序列化
    • 序列化:将数据结构或对象转换成二进制串的过程
    • 反序列化:将在序列化过程中所产生的二进制串转换成数据结构或者对象的过程

3.1.2.protobuf 的优势

  • 序列化后体积相比Json和XML 很小,适合网络传输
  • 支持跨平台多语言
  • 消息格式升级和兼容性还不错
  • 序列化反序列化速度很快

3.2.protobuf 工具安装

3.2.1.protoc编译器安装

  • windows
    • 下载预编译的二进制文件 protoc-*.zip:https://github.com/protocolbuffers/protobuf/releases
    • 解压到目录(如 C:\protobuf)。
    • 将 bin 目录添加到系统环境变量 PATH 中。
  • mac:
    # 使用 Homebrew 安装
    brew install protobuf
    
  • 验证安装
    protoc --version  # 输出类似 libprotoc 29.3
    

3.2.2.go语言代码生成器安装

  • 我们接下来代码示例都使用go语言,所以这里安装一个protoc-gen-go,用于将proto文件自动生成go文件
  • 如果需要生成其他语言,需要安装其他的语言生成器
# 安装protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest# 确认插件安装成功(确保 $GOPATH/bin 在 PATH 中)
which protoc-gen-go

在这里插入图片描述

3.3.proto文件编写

  • 下面示例包含 基本语法、常用类型、rpc方法
  • 文件名:person.proto
    • proto文件的每个字段,都会指定一个唯一数字标识,决定生成的代码中,字段的位置顺序
syntax = "proto3";  // 使用 proto3 语法package example;    // 包名(用于代码生成时的命名空间)option go_package = "github.com/yourusername/protobuf-example/person"; // Go 代码的导入路径// 定义枚举
enum Gender {UNKNOWN = 0;MALE = 1;FEMALE = 2;
}// 嵌套消息示例
message Address {string city = 1;string street = 2;int32 zip_code = 3;
}// 主消息
message Person {string name = 1;       // 字符串类型int32 age = 2;         // 整数类型Gender gender = 3;     // 枚举类型repeated string hobbies = 4;   // 数组类型(重复字段)Address address = 5;   // 嵌套消息
}// 可选:RPC 服务定义(如果需要)
service PersonService {rpc GetPersonInfo (PersonRequest) returns (PersonResponse);
}message PersonRequest {int32 person_id = 1;
}message PersonResponse {Person person = 1;
}

3.4.protoc-gen-go 生成 go 代码

3.4.1.安装 Go 插件

# 安装 protoc-gen-go 插件(生成代码工具)
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest# 确认插件安装成功(确保 $GOPATH/bin 在 PATH 中)
which protoc-gen-go

3.4.2.生成 go 结构文件

# 创建生成目录
mkdir -p person# 运行 protoc 命令,生成 go 结构文件
protoc \--go_out=./person \       # 输出目录--go_opt=paths=source_relative \  # 保持相对路径person.proto# 运行 protoc 命令,生成 go-grpc 文件
protoc \--go-grpc_out=./person \--go-grpc_opt=paths=source_relative \person.proto# 如果报错有下面报错,说明protoc-gen-go没有加入环境变量,处理一下就好
# protoc-gen-go: program not found or is not executable
# Please specify a program using absolute path or make sure the program is available in your PATH system variable
# --go_out: protoc-gen-go: Plugin failed with status code 1.
export PATH=$PATH:$HOME/go/bin
  • 生成结果:
    • 文件:person/person.pb.go
    • 内容包含:PersonAddressGender 等结构体和序列化方法。
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.36.6
// 	protoc        v5.29.3
// source: person.protopackage personimport (protoreflect "google.golang.org/protobuf/reflect/protoreflect"protoimpl "google.golang.org/protobuf/runtime/protoimpl"reflect "reflect"sync "sync"unsafe "unsafe"
)const (// Verify that this generated code is sufficiently up-to-date._ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)// Verify that runtime/protoimpl is sufficiently up-to-date._ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)// 定义枚举
type Gender int32const (Gender_UNKNOWN Gender = 0Gender_MALE    Gender = 1Gender_FEMALE  Gender = 2
)// Enum value maps for Gender.
var (Gender_name = map[int32]string{0: "UNKNOWN",1: "MALE",2: "FEMALE",}Gender_value = map[string]int32{"UNKNOWN": 0,"MALE":    1,"FEMALE":  2,}
)func (x Gender) Enum() *Gender {p := new(Gender)*p = xreturn p
}func (x Gender) String() string {return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}func (Gender) Descriptor() protoreflect.EnumDescriptor {return file_person_proto_enumTypes[0].Descriptor()
}func (Gender) Type() protoreflect.EnumType {return &file_person_proto_enumTypes[0]
}func (x Gender) Number() protoreflect.EnumNumber {return protoreflect.EnumNumber(x)
}// Deprecated: Use Gender.Descriptor instead.
func (Gender) EnumDescriptor() ([]byte, []int) {return file_person_proto_rawDescGZIP(), []int{0}
}// 嵌套消息示例
type Address struct {state         protoimpl.MessageState `protogen:"open.v1"`City          string                 `protobuf:"bytes,1,opt,name=city,proto3" json:"city,omitempty"`Street        string                 `protobuf:"bytes,2,opt,name=street,proto3" json:"street,omitempty"`ZipCode       int32                  `protobuf:"varint,3,opt,name=zip_code,json=zipCode,proto3" json:"zip_code,omitempty"`unknownFields protoimpl.UnknownFieldssizeCache     protoimpl.SizeCache
}func (x *Address) Reset() {*x = Address{}mi := &file_person_proto_msgTypes[0]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)
}func (x *Address) String() string {return protoimpl.X.MessageStringOf(x)
}func (*Address) ProtoMessage() {}func (x *Address) ProtoReflect() protoreflect.Message {mi := &file_person_proto_msgTypes[0]if x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use Address.ProtoReflect.Descriptor instead.
func (*Address) Descriptor() ([]byte, []int) {return file_person_proto_rawDescGZIP(), []int{0}
}func (x *Address) GetCity() string {if x != nil {return x.City}return ""
}func (x *Address) GetStreet() string {if x != nil {return x.Street}return ""
}func (x *Address) GetZipCode() int32 {if x != nil {return x.ZipCode}return 0
}// 主消息
type Person struct {state         protoimpl.MessageState `protogen:"open.v1"`Name          string                 `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`                          // 字符串类型Age           int32                  `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`                           // 整数类型Gender        Gender                 `protobuf:"varint,3,opt,name=gender,proto3,enum=example.Gender" json:"gender,omitempty"` // 枚举类型Hobbies       []string               `protobuf:"bytes,4,rep,name=hobbies,proto3" json:"hobbies,omitempty"`                    // 数组类型(重复字段)Address       *Address               `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"`                    // 嵌套消息unknownFields protoimpl.UnknownFieldssizeCache     protoimpl.SizeCache
}func (x *Person) Reset() {*x = Person{}mi := &file_person_proto_msgTypes[1]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)
}func (x *Person) String() string {return protoimpl.X.MessageStringOf(x)
}func (*Person) ProtoMessage() {}func (x *Person) ProtoReflect() protoreflect.Message {mi := &file_person_proto_msgTypes[1]if x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {return file_person_proto_rawDescGZIP(), []int{1}
}func (x *Person) GetName() string {if x != nil {return x.Name}return ""
}func (x *Person) GetAge() int32 {if x != nil {return x.Age}return 0
}func (x *Person) GetGender() Gender {if x != nil {return x.Gender}return Gender_UNKNOWN
}func (x *Person) GetHobbies() []string {if x != nil {return x.Hobbies}return nil
}func (x *Person) GetAddress() *Address {if x != nil {return x.Address}return nil
}type PersonRequest struct {state         protoimpl.MessageState `protogen:"open.v1"`PersonId      int32                  `protobuf:"varint,1,opt,name=person_id,json=personId,proto3" json:"person_id,omitempty"`unknownFields protoimpl.UnknownFieldssizeCache     protoimpl.SizeCache
}func (x *PersonRequest) Reset() {*x = PersonRequest{}mi := &file_person_proto_msgTypes[2]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)
}func (x *PersonRequest) String() string {return protoimpl.X.MessageStringOf(x)
}func (*PersonRequest) ProtoMessage() {}func (x *PersonRequest) ProtoReflect() protoreflect.Message {mi := &file_person_proto_msgTypes[2]if x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use PersonRequest.ProtoReflect.Descriptor instead.
func (*PersonRequest) Descriptor() ([]byte, []int) {return file_person_proto_rawDescGZIP(), []int{2}
}func (x *PersonRequest) GetPersonId() int32 {if x != nil {return x.PersonId}return 0
}type PersonResponse struct {state         protoimpl.MessageState `protogen:"open.v1"`Person        *Person                `protobuf:"bytes,1,opt,name=person,proto3" json:"person,omitempty"`unknownFields protoimpl.UnknownFieldssizeCache     protoimpl.SizeCache
}func (x *PersonResponse) Reset() {*x = PersonResponse{}mi := &file_person_proto_msgTypes[3]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)
}func (x *PersonResponse) String() string {return protoimpl.X.MessageStringOf(x)
}func (*PersonResponse) ProtoMessage() {}func (x *PersonResponse) ProtoReflect() protoreflect.Message {mi := &file_person_proto_msgTypes[3]if x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use PersonResponse.ProtoReflect.Descriptor instead.
func (*PersonResponse) Descriptor() ([]byte, []int) {return file_person_proto_rawDescGZIP(), []int{3}
}func (x *PersonResponse) GetPerson() *Person {if x != nil {return x.Person}return nil
}var File_person_proto protoreflect.FileDescriptorconst file_person_proto_rawDesc = "" +"\n" +"\fperson.proto\x12\aexample\"P\n" +"\aAddress\x12\x12\n" +"\x04city\x18\x01 \x01(\tR\x04city\x12\x16\n" +"\x06street\x18\x02 \x01(\tR\x06street\x12\x19\n" +"\bzip_code\x18\x03 \x01(\x05R\azipCode\"\x9d\x01\n" +"\x06Person\x12\x12\n" +"\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" +"\x03age\x18\x02 \x01(\x05R\x03age\x12'\n" +"\x06gender\x18\x03 \x01(\x0e2\x0f.example.GenderR\x06gender\x12\x18\n" +"\ahobbies\x18\x04 \x03(\tR\ahobbies\x12*\n" +"\aaddress\x18\x05 \x01(\v2\x10.example.AddressR\aaddress\",\n" +"\rPersonRequest\x12\x1b\n" +"\tperson_id\x18\x01 \x01(\x05R\bpersonId\"9\n" +"\x0ePersonResponse\x12'\n" +"\x06person\x18\x01 \x01(\v2\x0f.example.PersonR\x06person*+\n" +"\x06Gender\x12\v\n" +"\aUNKNOWN\x10\x00\x12\b\n" +"\x04MALE\x10\x01\x12\n" +"\n" +"\x06FEMALE\x10\x022Q\n" +"\rPersonService\x12@\n" +"\rGetPersonInfo\x12\x16.example.PersonRequest\x1a\x17.example.PersonResponseB1Z/github.com/yourusername/protobuf-example/personb\x06proto3"var (file_person_proto_rawDescOnce sync.Oncefile_person_proto_rawDescData []byte
)func file_person_proto_rawDescGZIP() []byte {file_person_proto_rawDescOnce.Do(func() {file_person_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_person_proto_rawDesc), len(file_person_proto_rawDesc)))})return file_person_proto_rawDescData
}var file_person_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_person_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_person_proto_goTypes = []any{(Gender)(0),            // 0: example.Gender(*Address)(nil),        // 1: example.Address(*Person)(nil),         // 2: example.Person(*PersonRequest)(nil),  // 3: example.PersonRequest(*PersonResponse)(nil), // 4: example.PersonResponse
}
var file_person_proto_depIdxs = []int32{0, // 0: example.Person.gender:type_name -> example.Gender1, // 1: example.Person.address:type_name -> example.Address2, // 2: example.PersonResponse.person:type_name -> example.Person3, // 3: example.PersonService.GetPersonInfo:input_type -> example.PersonRequest4, // 4: example.PersonService.GetPersonInfo:output_type -> example.PersonResponse4, // [4:5] is the sub-list for method output_type3, // [3:4] is the sub-list for method input_type3, // [3:3] is the sub-list for extension type_name3, // [3:3] is the sub-list for extension extendee0, // [0:3] is the sub-list for field type_name
}func init() { file_person_proto_init() }
func file_person_proto_init() {if File_person_proto != nil {return}type x struct{}out := protoimpl.TypeBuilder{File: protoimpl.DescBuilder{GoPackagePath: reflect.TypeOf(x{}).PkgPath(),RawDescriptor: unsafe.Slice(unsafe.StringData(file_person_proto_rawDesc), len(file_person_proto_rawDesc)),NumEnums:      1,NumMessages:   4,NumExtensions: 0,NumServices:   1,},GoTypes:           file_person_proto_goTypes,DependencyIndexes: file_person_proto_depIdxs,EnumInfos:         file_person_proto_enumTypes,MessageInfos:      file_person_proto_msgTypes,}.Build()File_person_proto = out.Filefile_person_proto_goTypes = nilfile_person_proto_depIdxs = nil
}

3.4.3.生成 go-grpc 文件

# 运行 protoc 命令,生成 go-grpc 文件
protoc \--go-grpc_out=./person \--go-grpc_opt=paths=source_relative \person.proto# 如果报错有下面报错,说明protoc-gen-go没有加入环境变量,处理一下就好
# protoc-gen-go: program not found or is not executable
# Please specify a program using absolute path or make sure the program is available in your PATH system variable
# --go_out: protoc-gen-go: Plugin failed with status code 1.
export PATH=$PATH:$HOME/go/bin
  • 生成结果:
    • 文件:person/person_grpc.pb.go
    • 内容包含:PersonServiceClientPersonServiceServer 等 客户端-服务端 结构与方法
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc             v5.29.3
// source: person.protopackage personimport (context "context"grpc "google.golang.org/grpc"codes "google.golang.org/grpc/codes"status "google.golang.org/grpc/status"
)// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9const (PersonService_GetPersonInfo_FullMethodName = "/example.PersonService/GetPersonInfo"
)// PersonServiceClient is the client API for PersonService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// 可选:RPC 服务定义(如果需要)
type PersonServiceClient interface {GetPersonInfo(ctx context.Context, in *PersonRequest, opts ...grpc.CallOption) (*PersonResponse, error)
}type personServiceClient struct {cc grpc.ClientConnInterface
}func NewPersonServiceClient(cc grpc.ClientConnInterface) PersonServiceClient {return &personServiceClient{cc}
}func (c *personServiceClient) GetPersonInfo(ctx context.Context, in *PersonRequest, opts ...grpc.CallOption) (*PersonResponse, error) {cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)out := new(PersonResponse)err := c.cc.Invoke(ctx, PersonService_GetPersonInfo_FullMethodName, in, out, cOpts...)if err != nil {return nil, err}return out, nil
}// PersonServiceServer is the server API for PersonService service.
// All implementations must embed UnimplementedPersonServiceServer
// for forward compatibility.
//
// 可选:RPC 服务定义(如果需要)
type PersonServiceServer interface {GetPersonInfo(context.Context, *PersonRequest) (*PersonResponse, error)mustEmbedUnimplementedPersonServiceServer()
}// UnimplementedPersonServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedPersonServiceServer struct{}func (UnimplementedPersonServiceServer) GetPersonInfo(context.Context, *PersonRequest) (*PersonResponse, error) {return nil, status.Errorf(codes.Unimplemented, "method GetPersonInfo not implemented")
}
func (UnimplementedPersonServiceServer) mustEmbedUnimplementedPersonServiceServer() {}
func (UnimplementedPersonServiceServer) testEmbeddedByValue()                       {}// UnsafePersonServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PersonServiceServer will
// result in compilation errors.
type UnsafePersonServiceServer interface {mustEmbedUnimplementedPersonServiceServer()
}func RegisterPersonServiceServer(s grpc.ServiceRegistrar, srv PersonServiceServer) {// If the following call pancis, it indicates UnimplementedPersonServiceServer was// embedded by pointer and is nil.  This will cause panics if an// unimplemented method is ever invoked, so we test this at initialization// time to prevent it from happening at runtime later due to I/O.if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {t.testEmbeddedByValue()}s.RegisterService(&PersonService_ServiceDesc, srv)
}func _PersonService_GetPersonInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {in := new(PersonRequest)if err := dec(in); err != nil {return nil, err}if interceptor == nil {return srv.(PersonServiceServer).GetPersonInfo(ctx, in)}info := &grpc.UnaryServerInfo{Server:     srv,FullMethod: PersonService_GetPersonInfo_FullMethodName,}handler := func(ctx context.Context, req interface{}) (interface{}, error) {return srv.(PersonServiceServer).GetPersonInfo(ctx, req.(*PersonRequest))}return interceptor(ctx, in, info, handler)
}// PersonService_ServiceDesc is the grpc.ServiceDesc for PersonService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PersonService_ServiceDesc = grpc.ServiceDesc{ServiceName: "example.PersonService",HandlerType: (*PersonServiceServer)(nil),Methods: []grpc.MethodDesc{{MethodName: "GetPersonInfo",Handler:    _PersonService_GetPersonInfo_Handler,},},Streams:  []grpc.StreamDesc{},Metadata: "person.proto",
}

4.基于gRPC的客户端与服务端编写

// TODO

相关文章:

  • Android清单文件
  • Nexus首次亮相迪拜 TOKEN2049:以“手机 + 钱包 + 公链 + RWA”生态系统引领未来区块链基建
  • 2025年中国DevOps工具选型指南:主流平台能力横向对比
  • Idea 设置编码UTF-8 Idea中 .properties 配置文件中文乱码
  • Java中的异常机制
  • 时序数据库IoTDB分布式系统监控基础概述
  • 2025年中国主流DevOps平台对比分析:Gitee、阿里云效与GitLab CE的技术适配与合规实践全景解读
  • vue-ganttastic甘特图label标签横向滚动固定方法
  • 多模态论文笔记——NaViT
  • 零基础用 Hexo + Matery 搭建博客|Github Pages 免费部署教程
  • NineData 社区版 V4.1.0 正式发布,新增 4 条迁移链路,本地化数据管理能力再升级
  • RabbitMq消息阻塞,立即解决方案
  • NNLM神经网络语言模型总结
  • 使用 hover-class 实现触摸态效果 - uni-app 教程
  • 使用VSCode编辑Markdown+PlantUml
  • 推荐一个Winform开源的UI工具包
  • HTTP / HTTPS 协议
  • 移动网页调试工具实战:从 Chrome 到 WebDebugX 的效率演进
  • 【C/C++】自定义类型:结构体
  • Ubuntu 系统默认已安装 python,此处只需添加一个超链接即可
  • 美叙领导人25年来首次会面探索关系正常化,特朗普下令解除对叙经济制裁
  • 为什么越来越多景区,把C位留给了书店?
  • 沪喀同心|为新疆青少年提供科普大餐,“小小博物家(喀什版)”启动
  • 京东一季度净利增长五成,营收增速创近三年新高,称外卖业务取得显著进展
  • 梅花奖在上海丨陈丽俐“婺剧折戏专场”:文戏武做,武戏文唱
  • 联合国秘书长欢迎中美经贸高层会谈成果