当前位置: 首页 > 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

相关文章:

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

云原生学习路线导航页(持续更新中) kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计(一)Kubernetes架构原则和对象设计(二)Kubernetes架构原则和对象设计(三)Kubernetes控…...

电商平台自动化

为什么要进行独立站自动化 纯人工测试人力成本高,相对效率低 回归测试在通用模块重复进行人工测试,测试效率低 前期调研备选自动化框架(工具): Katalon Applitools Testim 阿里云EMAS Playwright Appium Cypress 相关…...

【kafka】kafka概念,使用技巧go示例

1. Kafka基础概念 1.1 什么是Kafka? Kafka是一个分布式流处理平台,用于构建实时数据管道和流式应用。核心特点: 高吞吐量:每秒可处理百万级消息持久化存储:消息按Topic分区存储在磁盘分布式架构:支持水平…...

计算机系统结构——Cache性能分析

一、实验目的 加深对Cache的基本概念、基本组织结构以及基本工作原理的理解。掌握Cache容量、相联度、块大小对Cache性能的影响。掌握降低Cache不命中率的各种方法以及这些方法对提高Cache性能的好处。理解LRU与随机法的基本思想以及它们对Cache性能的影响。 二、实验平台 实…...

Spring Web MVC————入门(2)

1,请求 我们接下来继续讲请求的部分,上期将过很多了,我们来给请求收个尾。 还记得Cookie和Seesion吗,我们在HTTP讲请求和响应报文的时候讲过,现在再给大家讲一遍,我们HTTP是无状态的协议,这次的…...

Adobe DC 2025安装教程

一.软件下载 点此下载 二.软件安装...

W1电力线载波通信技术

CK_Label_W1 产品型号:CK_Label_W1 尺寸:37*65*33.7mm 按键:1 指示灯:1 RGB灯(红/绿/蓝/黄/紫/白/青) 外观颜色:白色 合规认证:CE, RoHS 工作温度:0-50℃ 提示功能:蜂鸣器声音…...

现代 Web 自动化测试框架对比:Playwright 与 Selenium 的深度剖析

现代 Web 自动化测试框架对比:Playwright 与 Selenium 的深度剖析 摘要:本文对 Playwright 与 Selenium 在开发适配性、使用难度、场景适用性及性能表现等方面进行了全面深入的对比分析。通过详细的技术实现细节阐述与实测数据支撑,为开发者…...

第二章:CSS秘典 · 色彩与布局的力量

剧情承接:色彩失衡的荒原 林昊穿过 HTML 大门,眼前却是一片 灰白扭曲的荒原。所有页面元素如同幽灵般漂浮,没有色彩、没有结构,错乱无章。 “这是失控的样式荒原。” 零号导师的声音再次响起, “HTML 给了你骨架&…...

ubuntu studio 系统详解

Ubuntu Studio 系统详解:面向多媒体创作的专业 Linux 发行版 一、定位与目标用户 Ubuntu Studio 是 Ubuntu 的官方衍生版本(Flavor),专为 音频、视频、图形设计、音乐制作、影视后期 等多媒体创作场景设计。目标用户包括&#x…...

在 Ubuntu 20.04.6 LTS 中将 SCons 从 3.1.2 升级到 4.9.1

在 Ubuntu 20.04.6 LTS 中将 SCons 从 3.1.2 升级到 4.9.1,可以通过以下步骤完成: 方法 1:使用 pip 安装(推荐) 步骤 1:卸载旧版本 SCons # 如果通过 apt 安装的旧版本,先卸载 sudo apt remov…...

边缘计算网关工业物联网应用:空压机远程运维监控管理

边缘计算网关在空压机远程运维监控管理中的工业物联网应用,主要体现在数据采集与处理、设备监控、故障诊断与预警、远程控制等方面,以下是具体介绍: 数据采集与处理 多源数据采集:边缘计算网关能连接空压机的各类传感器&#xf…...

【大模型面试每日一题】Day 18:大模型中KV Cache的作用是什么?如何通过Window Attention优化其内存占用?

【大模型面试每日一题】Day 18:大模型中KV Cache的作用是什么?如何通过Window Attention优化其内存占用? 📌 题目重现 🌟🌟 面试官:大模型中KV Cache的作用是什么?如何通过Window Attention优…...

Spring的 @Validate注解详细分析

在 Spring Boot 中,参数校验是保证数据合法性的重要手段。除了前面提到的NotNull、Size等基础注解外,JSR-303(Bean Validation 1.0)、JSR-349(Bean Validation 1.1)和 JSR-380(Bean Validation …...

现代计算机图形学Games101入门笔记(三)

三维变换 具体形式缩放,平移 特殊点旋转。这里涉及到坐标系,先统一定义右手坐标系,根据叉乘和右手螺旋判定方向。这里还能法线Ry Sina 正负与其他两个旋转不一样。这里可以用右手螺旋,x叉乘z,发现大拇指朝下&#xff0…...

AI时代的弯道超车之第八章:具体分享几个AI实际操作方法和案例

在这个AI重塑世界的时代,你还在原地观望吗?是时候弯道超车,抢占先机了! 李尚龙倾力打造——《AI时代的弯道超车:用人工智能逆袭人生》专栏,带你系统掌握AI知识,从入门到实战,全方位提升认知与竞争力! 内容亮点: AI基础 + 核心技术讲解 职场赋能 + 创业路径揭秘 打破…...

企业网络新选择:软件定义架构下的MPLS

随着现代企业园区网络和运营商级基础设施的不断发展,多协议标签交换 (MPLS) 已成为一项基础技术,这要归功于其高效的数据包转发、高级流量工程功能以及对多租户环境的强大支持。 什么是MPLS? MPLS(多协议…...

SparkSQL操作Mysql

(一)准备mysql环境 我们计划在hadoop001这台设备上安装mysql服务器,(当然也可以重新使用一台全新的虚拟机)。 以下是具体步骤: 使用finalshell连接hadoop001.查看是否已安装MySQL。命令是: rpm -qa|grep…...

【论文阅读】UNIT: Backdoor Mitigation via Automated Neural Distribution Tightening

ECCV2024 https://github.com/Megum1/UNIT 我们的主要贡献总结如下: 我们引入了UNIT(“AUtomated Neural DIstribution Tightening”),这是一种创新的后门缓解方法,它为每个神经元近似独特的分布边界,用于…...

Android逆向学习(十) IDA逆向编辑Android so文件

Android逆向学习(十) IDA逆向编辑Android so文件 一、 写在前面 这是吾爱破解论坛正己大大的第10个教程 native code在我之前的博客中讲到过,所以这里就不讲了 简单来说,native code就是在android中使用c或c语言进行开发 这样…...

OpenCV + PyAutoGUI + Tkinter + FastAPI + Requests 实现的远程控制软件设计方案

以下是基于 OpenCV PyAutoGUI Tkinter FastAPI Requests 实现的远程控制软件设计方案。该方案分为 被控端(服务端) 和 控制端(客户端),支持屏幕实时查看、键盘映射和鼠标操作。 1. 系统架构 ------------------- …...

C++.神经网络与深度学习(赶工版)(会二次修改)

神经网络与深度学习 1. 神经网络基础1.1 神经元模型与激活函数1.2 神经网络结构与前向传播2.1 损失函数与优化算法均方误差损失函数交叉熵损失函数梯度下降优化算法 2.2 反向传播与梯度计算神经元的反向传播 3.1 神经元类设计与实现神经元类代码实现代码思路 3.2 神经网络类构建…...

砷化镓太阳能电池:开启多元领域能源新篇

砷化镓太阳能电池作为一种高性能的光伏产品,具有诸多独特优势。其中,锗衬底砷化镓太阳能电池表现尤为突出,它具备高转化效率、耐辐照和高电压等特性。在空间供电电源领域,这些优势使其成为人造卫星、太空站、太空探测器和登陆探测…...

[Linux] vim及gcc工具

目录 一、vim 1.vim的模式 2.vim的命令集 (1):命令模式 (2):底行模式 3.vim配置 二、gcc 1.gcc格式及选项 2.工作布置 三、自动化构建工具makefile 1.基本使用方法 2.配置文件解析 3.拓展 在linux操作系统的常用工具中,常用vim来进行程序的编写&#xff1b…...

java加强 -stream流

Stream流是jdk8开始新增的一套api,可以用于操作集合或数组的内容。 Stream流大量的结合了Lambda的语法风格来编程,功能强大,性能高效,代码简洁,可读性好。 体验Stream流 把集合中所有以三开头并且三个字的元素存储到…...

RHCE认证通过率

红帽RHCE考试总体通过率38%(2023年数据),细分数据显示自学者通过率18%,参加官方培训者47%,企业团体考生53%。通过率差异由备考资源和考试策略决定。 RHCE考试重点考Ansible自动化运维,需在3.5小时内完成12…...

OpenEvidence AI临床决策支持工具平台研究报告

平台概述 OpenEvidence是一个专为医疗专业人士设计的临床决策支持工具,旨在通过整合各类临床计算器和先进的人工智能技术,提高医生的诊疗决策效率和准确性。作为一款综合性医疗平台,OpenEvidence将复杂的医学计算流程简化,同时提供个性化的临床建议,使医生能够更快、更准…...

gd32e230c8t6 keil6工程模板

下载固件gd32e230c8t6固件官方下载(需登录) 或 蓝奏云 新建一个文件夹,把固件压缩包里的里的Firmware和Template拖进去 keil新建gd32e230c8工程 必须勾选CMSIS-CORE 新建一个文件夹,双击任意改名 点击manage project it…...

正向代理与反向代理区别及应用

正向代理和反向代理是两种常见的代理服务器类型,它们在网络架构中扮演不同角色,核心区别在于代理对象和使用场景。 1. 正向代理(Forward Proxy) 定义:正向代理是客户端(如浏览器)主动配置的代理…...

自然语言处理入门级项目——文本分类

文章目录 前言1.数据预处理1.1数据集介绍1.2数据集抽取1.3划分数据集1.4数据清洗1.5数据保存 2.样本的向量化表征2.1词汇表2.2向量化2.3自定义数据集2.4备注 结语 前言 本篇博客主要介绍自然语言处理领域中一个项目案例——文本分类,具体而言就是判断评价属于积极还…...

UOS专业版上通过源码安装 Python 3.13 并保留系统默认版本

在 UOS 专业版上通过源码安装 Python 3.13 并保留系统默认版本,可按照以下步骤操作: 1. 安装依赖 首先安装编译 Python 所需的依赖库: sudo apt update sudo apt install -y build-essential zlib1g-dev libncurses5-dev \ libgdbm-dev li…...

【论文笔记】ViT-CoMer

【题目】:ViT-CoMer: Vision Transformer with Convolutional Multi-scale Feature Interaction for Dense Predictions 【引用格式】:Xia C, Wang X, Lv F, et al. Vit-comer: Vision transformer with convolutional multi-scale feature interaction…...

kaggle薅羊毛

参考:https://pytorch-tutorial.readthedocs.io/en/latest/tutorial/chapter05_application/5_1_kaggle/#512-kaggle https://github.com/girls-in-ai/Girls-In-AI/blob/master/machine_learning_diary/data_analysis/kaggle_intro.md 1,code training…...

Python 之 Flask 入门学习

安装 Flask 在开始使用 Flask 之前,需要先安装它。可以通过 pip 命令来安装 Flask: pip install Flask创建第一个 Flask 应用 创建一个简单的 Flask 应用,只需要几行代码。以下是一个最基本的 Flask 应用示例: from flask imp…...

SpringBoot Vue MySQL酒店民宿预订系统源码(支付宝沙箱支付)+代码讲解视频

💗博主介绍💗:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示:文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…...

Oracle日期计算跟Mysql计算日期差距问题-导致两边计算不一致

Oracle数据库对日期做加法时,得到的时间是某天的12:00:00 例: Oracle计算 select (TO_DATE(2025-04-14, YYYY-MM-DD)1.5*365) from dual; 结果:2026/10/13 12:00:00Mysql计算 select DATE_ADD( str_to_date( 2025-04-14, %Y-%m-%d ), INTER…...

多线程(三)

上一期关于线程的执行,咱们说到线程是 “ 随机调度,抢占式执行 ”。所以我们对于线程之间执行的先后顺序是难以预知的。 例如咱们打篮球的时候,球场上的每一位运动员都是一个独立的 “ 执行流 ”,也可以认为是一个线程&#xff0…...

微服务商城(1)开篇、服务划分

参考:https://mp.weixin.qq.com/s?__bizMzg2ODU1MTI0OA&mid2247485597&idx1&sn7e85894b7847cc50df51d66092792453&scene21#wechat_redirect 为什么选择go-zero go-zero 为我们提供了许多高并发场景下的实用工具,比如为了降低接口耗时…...

刘强东 “猪猪侠” 营销:重构创始人IP的符号革命|创客匠人热点评述

当刘强东身着印有外卖箱猪猪侠的 T 恤漫步东京涩谷街头时,这场看似荒诞的行为艺术实则揭开了互联网商业竞争的新篇章。这位曾经以严肃企业家形象示人的京东创始人,正通过二次元 IP 的深度绑定,完成从商业领袖到文化符号的华丽转身。 一、IP …...

MQ消息队列的深入研究

目录 1、Apache Kafka 1.1、 kafka架构设 1.2、最大特点 1.3、功能介绍 1.4、Broker数据共享 1.5、数据一致性 2、RabbitMQ 2.1、架构图 2.2、最大特点 2.3、工作原理 2.4、功能介绍 3、RocketMQ 3.1、 架构设计 3.2、工作原理 3.3、最大特点 3.4、功能介绍 3…...

填涂颜色(bfs)

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言​📝 含泪播种的人一定能含笑收获! 题目描述 由数字 0 0 0 组成的方阵中,有一任意形状的由数字 1 1 1 构成的闭合圈。现…...

FFplay 音视频同步机制解析:以音频为基准的时间校准与动态帧调整策略

1.⾳视频同步基础 1.2 简介 看视频时,要是声音和画面不同步,体验会大打折扣。之所以会出现这种情况,和音视频数据的处理过程密切相关。音频和视频的输出不在同一个线程,就像两个工人在不同车间工作,而且不一定会同时…...

【Linux笔记】——进程信号的捕捉——从中断聊聊OS是怎么“活起来”的

🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:Linux 🌹往期回顾🌹:【Linux笔记】——进程信号的保存 🔖流水不争,争的是滔滔不息 一、信号捕捉的流程二、…...

VCS X-PROP建模以及在方针中的应用

VCS X-PROP建模以及在方针中的应用 摘要:VCS X-Prop(X-Propagation)是 Synopsys VCS 仿真工具中的一种高级功能,用于增强 X 态(未知态)和 Z 态(高阻态)在 RTL 仿真中的建模和传播能力…...

OpenSHMEM 介绍和使用指南

OpenSHMEM 介绍和使用指南 什么是 OpenSHMEM? OpenSHMEM 是一个用于并行计算的标准化 API,它提供了一种分区全局地址空间 (PGAS) 编程模型。OpenSHMEM 最初由 Cray 公司开发,后来成为一个开源项目,旨在为高性能计算提供高效的通…...

Electron入门指南:用前端技术打造桌面应用

🌟 目录速览 什么是Electron?为什么要用Electron?核心概念三分钟掌握快速创建第一个应用典型应用场景开发注意事项常见问题解答 一、什么是Electron?🤔 Electron就像魔法转换器,它能将你熟悉的&#xff1…...

机器学习第十讲:异常值检测 → 发现身高填3米的不合理数据

机器学习第十讲:异常值检测 → 发现身高填3米的不合理数据 资料取自《零基础学机器学习》。 查看总目录:学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章:DeepSeek R1本地与线上满血版部署:超详细手把手指南 一、幼儿…...

【Redis】缓存穿透、缓存雪崩、缓存击穿

1.缓存穿透 是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,导致请求直接穿透缓存到达数据库,给数据库带来压力的情况。 常见的解决方案有两种: 缓存空对象:实现简单,维护方便&am…...

科学养生指南:打造健康生活

在快节奏的现代生活中,健康养生成为人们关注的焦点。科学养生无需复杂理论,掌握以下几个关键要素,就能为身体构筑坚实的健康防线。​ 合理饮食是健康的基础。世界卫生组织建议,每天应摄入至少 5 份蔬菜和水果,保证维生…...

解锁健康生活:现代养生实用方案

早上被闹钟惊醒后匆忙灌下咖啡,中午用外卖应付一餐,深夜刷着手机迟迟不肯入睡 —— 这样的生活模式,正在不知不觉侵蚀我们的健康。科学养生并非遥不可及的目标,只需从生活细节入手,就能逐步改善身体状态。​ 饮食管理…...