1.RPC

1.1 介绍

rpc的调用过程:

image-20240917193033725

客户端:

  1. 建立连接 tcp/http

  2. 将对象序列化为json字符串 - 序列化

  3. 发送json字符串 - 调用成功后实际上接收到的是一个二进制的数据

  4. 等待服务器发送结果

  5. 将服务器返回的数据解析成对象 - 反序列化

代码:

  1. 创建与指定目标(服务端)的连接交互
  2. 创建 server 的客户端对象
  3. 发送 RPC 请求,等待同步响应,得到回调后返回响应结果
  4. 输出响应结果

服务端:

  1. 监听网络端口 80

  2. 读取数据 - 二进制的json数据

  3. 对数据进行反序列化

  4. 开始处理业务逻辑

  5. 将处理的结果序列化成json二进制数据 - 序列化

  6. 将数据返回

代码:

  1. 创建gRPC Server对象:这是Server端的抽象对象。
  2. 注册服务端接口到gRPC Server内部注册中心:这样在接收到请求时,可以通过服务发现找到该接口并进行逻辑处理。
  3. 监听TCP端口:创建Listen来监听TCP端口。
  4. gRPC Server开始lis.Accept,直到Stop。

http:

http 1.x 协议有一个问题:一次性 一旦对方返回了结果 连接断开 ,这样的性能不适用在微服务

http 2.0 :长连接 ➡ grpc

http协议底层使用的也是tcp

两种协议选择:

  1. 直接基于tcp/udp协议去封装一层协议 myhttp, 不通用,
  2. http 2.0 有http的特性也有长连接的特性,grpc

**call ID:**即每个调用函数有唯一的ID


2.gRPC

2.1 安装protobuf

它类似于 JSON,但体积更小、速度更快,并且会生成本机语言绑定。您只需定义一次希望如何对数据进行结构化,然后即可使用特殊生成的源代码轻松地将结构化数据写入各种数据流并从中读取数据,并使用各种语言。

protobuf编译器:

go get -u xxx

测试:

protoc

安装gRPC核心库:

go get goole.golang.org/grpc

安装go的代码生成工具:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

2.2 protobuf文件编写

protoc --go_out=. hello.proto
protoc --go-grpc_out=. hello.proto

message 类似于 go的 struct

repeated 可重复字段,重复的值的顺序会被保留在go中定义为切片

服务定义

在.protobuf文件中定义一个RPC服务接口

service SayHello {
rpc SayHello(HelloRequest) returns SayHello(HelloReponse)
}

image-20240923121206882

2.3 ProtoBuf语法

定义消息类型

首先,我们来看一个非常简单的示例。假设您想要定义一个搜索请求消息格式,其中每个搜索请求都有一个查询字符串、您感兴趣的特定结果页面以及每页的结果数量。以下是用于定义消息类型的 .proto 文件。

syntax = "proto3";

message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
}
  • 文件的第一行指定您正在使用 proto3 语法:如果您不这样做,则协议缓冲区编译器将假定您正在使用 proto2。这必须是文件中的第一行非空、非注释行。
  • SearchRequest 消息定义指定三个字段(名称/值对),每个字段对应于您希望包含在此类消息中的每条数据。每个字段都有一个名称和一个类型。

指定字段类型

在前面的示例中,所有字段都是 标量类型:两个整数(page_numberresults_per_page)和一个字符串(query)。您还可以为字段指定 枚举 和复合类型,如其他消息类型。

分配字段编号

您必须为消息定义中的每个字段指定 1536,870,911 之间的一个数字,并遵守以下限制

  • 给定的数字必须在该消息的所有字段中唯一。
  • 字段编号19,00019,999为 Protocol Buffers 实现保留。如果您在消息中使用这些保留字段编号之一,协议缓冲区编译器将发出警告。
  • 您不能使用任何先前保留的字段编号或已分配给扩展的任何字段编号。

指定字段标签

消息字段可以是以下之一

  • optionaloptional 字段处于两种可能状态之一

    • 字段已设置,并且包含显式设置或从线路解析的值。它将序列化到线路。
    • 字段未设置,并将返回默认值。它不会序列化到线路。

    您可以检查该值是否已显式设置。

  • repeated:此字段类型可以在格式良好的消息中重复零次或多次。将保留重复值的顺序。

  • map:这是一个配对键/值字段类型。有关此字段类型的更多信息,请参阅 Maps

  • 如果没有应用显式字段标签,则假定默认字段标签,称为“隐式字段存在”。(您无法将字段显式设置为此状态。)格式良好的消息可以有零个或一个此字段(但不能多于一个)。您也无法确定此类型的字段是否已从线路解析。隐式存在字段将序列化到线路,除非它是默认值。有关此主题的更多信息,请参阅 字段存在

添加更多消息类型

可以在单个 .proto 文件中定义多个消息类型。如果您要定义多个相关消息,这很有用 - 例如,如果您想定义与 SearchResponse 消息类型相对应的回复消息格式,则可以将其添加到相同的 .proto

message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
}

message SearchResponse {
...
}

合并消息会导致膨胀虽然可以在单个 .proto 文件中定义多种消息类型(例如消息、枚举和服务),但当在一个文件中定义大量具有不同依赖项的消息时,它也可能导致依赖项膨胀。建议尽可能在每个 .proto 文件中包含较少的消息类型。

添加注释

要为 .proto 文件添加注释,请使用 C/C++ 样式的 ///* ... */ 语法。

/* SearchRequest represents a search query, with pagination options to
* indicate which results to include in the response. */

message SearchRequest {
string query = 1;
int32 page_number = 2; // Which page number do we want?
int32 results_per_page = 3; // Number of results to return per page.
}

删除字段

如果操作不当,删除字段可能会导致严重问题。

当不再需要某个字段且已从客户端代码中删除所有引用时,可以从消息中删除该字段定义。但是,必须保留已删除的字段号。如果不保留字段号,开发人员将来有可能重新使用该字段号。

还应保留字段名称,以允许消息的 JSON 和 TextFormat 编码继续进行解析。

保留字段

如果通过完全删除字段或将其注释掉来更新消息类型,则未来的开发人员可以在对类型进行自己的更新时重新使用该字段号。这可能会导致严重问题,如重新使用字段号的后果中所述。

为确保这种情况不会发生,请将已删除的字段号添加到 reserved 列表中。为确保消息的 JSON 和 TextFormat 实例仍可解析,还应将已删除的字段名称添加到 reserved 列表中。

如果任何未来的开发人员尝试使用这些保留的字段号或名称,则协议缓冲区编译器会发出警告。

message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}

保留的字段号范围是包含的(9 到 119、10、11 相同)。请注意,不能在同一个 reserved 语句中混合字段名称和字段号。

.proto 生成什么?

  • 对于 Go,编译器会生成一个 .pb.go 文件,其中包含一个类型,用于描述文件中每种消息类型。

默认值

当解析消息时,如果编码的消息不包含特定的隐式存在元素,则访问解析对象中的相应字段将返回该字段的默认值。这些默认值是特定于类型的

  • 对于字符串,默认值为空字符串。
  • 对于字节,默认值为空字节。
  • 对于布尔值,默认值是 false。
  • 对于数字类型,默认值是零。
  • 对于枚举,默认值是第一个定义的枚举值,它必须是 0。
  • 对于消息字段,该字段未设置。