Protocol Buffers V3语法全解
目录
- protobuf介绍
- protobuf使用
- protoc命令
- 语法
- 定义消息类型
- 指定字段类型
- 分配字段编号
- 指定字段规则
- 添加更多消息类型
- 注释
- 保留字段
- 从.proto文件生成了什么?
- 值类型
- 默认值
- 枚举
- 使用其他消息类型
- 导入定义
- 嵌套类型
- 更新消息类型
- 未知字段
- any任意类型
- oneof
- oneof 特性
- 兼容性问题
- Maps
- Packages
- 定义服务
- JSON 映射
- Options
- 参考文档
protobuf介绍
Protobuf是Protocol Buffer的简称,它是Google公司于2008年开源的一种高效的平台无关、语言无关、可扩展的数据格式,目前Protobuf作为接口规范的描述语言,可以作为Go语言RPC接口的基础工具。
protobuf使用
protobuf是一个与语言无关的一个数据协议,所以我们需要先编写IDL文件然后借助专用工具生成指定语言的代码,从而实现数据的序列化与反序列化过程。
大致开发流程如下: 1. IDL编写 2. 生成指定语言的代码 3. 序列化和反序列化
protoc命令
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
- IMPORT_PATH指定寻找proto 的目录去解决import 带来的依赖问题,如果省略,默认是当前文件夹。多个import 文件夹可以通过参数使用–proto_path多次来解决,编译器将会按顺序搜索。也可以用简写-I=IMPORT_PATH来表示–proto_path
- –cpp_out,–java_out,–go_out等等代表指定生成的语言,可以生成多个语言
- path/to/file.proto 代表输入的proto 文件,可以用*.proto 代表输入文件夹内多个文件
语法
定义消息类型
syntax = "proto3";message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;
}
- 文件第一行指定您使用的语法:如果不这样做,编译器将假定使用的是proto2。
- 这必须是.proto3文件的第一个非空、非注释行。
- 消息定义指定了三个字段(名称/值对),每个字段对应于要包含在该类型消息中的数据段。
指定字段类型
在上面的示例中,所有字段都是标量类型(scalar types): 两个整数(page_number和 result_per_page)和一个字符串(query)。但是也可以为字段指定组合类型,包括枚举和其他消息类型。
分配字段编号
- 消息定义中的每个字段都有一个唯一编号。
- 这些字段编号用于标识消息二进制格式的字段,并且在消息类型投入使用后不应更改。
- 请注意,1到15范围内的字段编号需要一个字节进行编码,编码内包括字段号和字段类型(参考协议缓冲区编码)。
- 16到2047范围内的字段编号需要两个字节(进行编码)。因此,您应该把1到15的消息编号留给非常频繁出现的消息元素。
- 请记住为将来可能添加的频繁出现的元素留出一些空间。可以指定的最小字段号为1,最大字段号为
(2**29)-1
(字段数字会作为key,key最后三位是类型)或536870911
。您也不能使用数字19000
到19999
(字段描述符),因为它们是协议缓冲区的保留数字,如果你在你的.proto中使用了这些数字,编译器会报错。同样,不能使用任何以前保留的字段号。
指定字段规则
消息字段可以是下列字段之一:
- singular: 格式正确的消息可以有这个字段的零个或一个(但不能多于一个)。这是 proto3语法的默认字段规则。
- repeated: 该字段可以在格式正确的消息中重复任意次数(包括零次)。重复值的顺序将被保留。
在 proto3中,标量数值类型的repeated
字段默认使用packed
编码。(参考Protocol Buffer Encoding,可以找到关于packed编码的更多信息)
添加更多消息类型
- 可以在一个.proto 文件中定义多个消息类型。
- 如果想定义与 SearchRequest 消息类型对应的应答消息格式SearchResponse,就可以将其添加到同一个.proto文件中。
message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;
}message SearchResponse {...
}
注释
.proto文件使用了C/C++风格的//
和/* ... */
语法。
/* SearchRequest 表示一个分页查询 * 其中有一些字段指示响应中包含哪些结果 */message SearchRequest {string query = 1;int32 page_number = 2; // 页码数int32 result_per_page = 3; // 每页返回的结果数
}
保留字段
- 如果通过完全删除某个字段或把它注释掉来更新消息类型,则将来的用户可以在对该类型进行自己的更新时重用该字段编号。
- 如果以后加载相同.proto的旧版本,这可能会导致数据损坏、隐私漏洞等严重问题。
- 确保不会发生这种情况的一种方法是使用reserved关键字指定已删除字段的字段编号为保留编号(也要指定已删除字段名称为保留名称(name),以规避JSON序列化问题)。将来有任何用户试图使用这些字段标识符时,协议缓冲区编译器将报错。
message Foo {reserved 2, 15, 9 to 11;reserved "foo", "bar";
}
注意,不能在同一个reserved语句中混合字段名和字段编号。
从.proto文件生成了什么?
当使用 protocol buffer 编译器来运行.proto文件时,编译器用选择的语言生成描述的消息类型
,包括获取和设置字段值,将消息序列化为输出流,以及从输入流解析消息的代码。
-
对于Python:Python 编译器为.proto文件中的每个消息类型生成一个带静态描述符的模块,然后与 metaclass 一起使用,在运行时创建必要的 Python 数据访问类。
-
对于 Go:编译器为文件中的每种消息类型生成一个类型(type)到一个.pb.go 文件。
-
其他…
值类型
.proto Type | Notes | C++ Type | Java/Kotlin Type[1] | Python Type[3] | Go Type | PHP Type |
---|---|---|---|---|---|---|
double | double | double | float | float64 | float | |
float | float | float | float | float32 | float | |
int32 | 使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,则使用 sint32代替。 | int32 | int | int | int32 | integer |
int64 | 使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,则使用 sint64代替。 | int64 | long | int/long[4] | int64 | integer/string[6] |
uint32 | 使用变长编码。 | uint32 | int[2] | int/long[4] | uint32 | integer |
uint64 | 使用变长编码。 | uint64 | long[2] | int/long[4] | uint64 | integer/string[6] |
sint32 | 使用可变长度编码。带符号的 int 值。这些编码比普通的 int32更有效地编码负数。 | int32 | int | int | int32 | integer |
sint64 | 使用可变长度编码。带符号的 int 值。这些编码比普通的 int64更有效地编码负数。 | int64 | long | int/long[4] | int64 | integer/string[6] |
fixed32 | 总是四个字节。如果值经常大于228,则比 uint32更有效率。 | uint32 | int[2] | int/long[4] | uint32 | integer |
fixed64 | 总是8字节。如果值经常大于256,则比 uint64更有效率。 | uint64 | integer/string[6] | |||
sfixed32 | 总是四个字节。 | int32 | int | int | int32 | integer |
sfixed64 | 总是八个字节。 | int64 | integer/string[6] | |||
bool | bool | boolean | bool | bool | boolean | |
string | 字符串必须始终包含 UTF-8编码的或7位 ASCII 文本,且不能长于232。 | string | String | str/unicode[5] | string | string |
bytes | 可以包含任何不超过232字节的任意字节序列。 | string | ByteString | str (Python 2) bytes (Python 3) | []byte | string |
默认值
当解析消息时,如果编码消息不包含特定的 singular 元素,则解析对象中的相应字段将设置为该字段的默认值。
- string:默认值为空字符串
- bytes:默认值为空字节
- boolean:默认值为false
- 数值类型:默认值为0
- 枚举:默认值为第一个定义的枚举值,该值必须是0
- 消息字段:不设默认值,它的确切值取决于语言。
- repeated 字段的默认值为空(通常是适配语言中的空列表)。
枚举
定义消息类型时,可能希望其中一个字段只包含预定义值列表中的一个
message SearchRequest {string query = 1;int32 page_number = 2;int32 result_per_page = 3;enum Corpus {UNIVERSAL = 0;WEB = 1;IMAGES = 2;LOCAL = 3;NEWS = 4;PRODUCTS = 5;VIDEO = 6;}Corpus corpus = 4;
}
Corpus enum 的第一个常量映射为零,每个 enum 定义必须包含一个常量,该常量映射为零且作为它的第一个元素。这是因为:
- 必须有一个零值,这样我们就可以使用0作为数值默认值。
- 零值必须是第一个元素,以便与 proto2语义兼容,其中第一个枚举值总是默认值。
可以通过将相同的值赋给不同的枚举常量来定义别名。为此,需要将allow_alias选项设置为true,否则协议编译器将在找到别名时将生成错误消息。
message MyMessage1 {enum EnumAllowingAlias {// 允许别名option allow_alias = true;UNKNOWN = 0;STARTED = 1;RUNNING = 1;}
}
message MyMessage2 {enum EnumNotAllowingAlias {UNKNOWN = 0;STARTED = 1;// RUNNING = 1; // 取消对此行的注释将导致内部出现编译错误,外部出现警告消息。}
}
- 枚举器常量必须在32位整数的范围内。由于enum使用可变编码,因此负值效率很低,所以不建议使用。您可以在定义的消息内部定义枚举,如上面的示例所示,也可以在外部定义枚举——这些枚举可以在.proto文件中的任何消息定义中重用。您还可以使用
_MessageType_._EnumType_
语法将一条消息中声明的枚举类型用作另一条消息中的字段类型。 - 当使用协议缓冲区编译器编译一个使用了枚举的.proto文件时,对于Python,会生成一个特殊EnumDescriptor类,用于在运行时生成的类中创建一组具有整数值的符号常量。
syntax = "proto3";
option go_package = "protos/pbs";
enum TestType {Hello1=0;Hello2=1;Hello3=2;Hello4=3;
}message HelloRequest {string greeting = 1;TestType en=2;
}message HelloResponse {string reply = 1;
}
使用其他消息类型
当使用其他消息的时候,如果在本文件,直接使用就可以了。Result代表自定义消息类型
message SearchResponse {repeated Result results = 1;
}message Result {string url = 1;string title = 2;// []stringrepeated string snippets = 3;
}
导入定义
可以通过 import 来使用来自其他 .proto 文件的定义。
import "myproject/other_protos.proto";
默认情况下,只能从直接导入的 .proto 文件中使用定义。但是,有时你可能需要将 .proto 文件移动到新的位置,这样就可以在旧目录放一个占位的.proto文件,使用import public
将所有导入转发到新位置,而不必直接移动.proto文件并修改所有的地方。
任何导入包含import public语句的proto的人都可以传递地依赖import public依赖项
// new.proto
// All definitions are moved here
-----------------------------------------------
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
-----------------------------------------------
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto
protocol 编译器使用命令行-I/–proto_path参数指定的一组目录中搜索导入的文件。如果没有给该命令行参数,则查看调用编译器的目录。
一般来说,应该将 --proto_path 参数设置为项目的根目录并为所有导入使用正确的名称。
// user_business.proto
syntax = "proto3";
option go_package = "protos/pbs";
import "share/user.proto";
//获取角色信息请求
message GetUserRequest {}
//获取角色信息响应
message GetUserResponse {User user=1;
}
// user.proto
syntax = "proto3";
option go_package = "protos/pbs";//用户定义
message User {string Id=1;string Name=2;string Age=3;
}
嵌套类型
可以在其他消息类型中定义和使用消息类型,在 SearchResponse消息中定义Result
:
message SearchResponse {message Result {string url = 1;string title = 2;repeated string snippets = 3;}repeated Result results = 1;
}
如果要在其父消息类型之外重用此消息类型,请通过_Parent_._Type_
使用:
message SomeOtherMessage {SearchResponse.Result result = 1;
}
可以随心所欲地将信息一层又一层嵌入其中:
message Outer { // Level 0message MiddleAA { // Level 1message Inner { // Level 2int64 ival = 1;bool booly = 2;}}message MiddleBB { // Level 1message Inner { // Level 2int32 ival = 1;bool booly = 2;}}
}
更新消息类型
如果现有的消息类型不再满足你的所有需要(例如,消息格式需要增加一个字段),但是仍然希望使用用旧格式创建的代码。在不破坏任何现有代码的情况下更新消息类型非常简单,只需遵守以下规则:
- 不要更改任何现有字段的字段编号
- 如果添加新字段,则使用“旧”消息格式的代码序列化的任何消息仍然可以由新生成的代码进行解析。你应该记住这些元素的默认值,以便新代码可以正确地与旧代码生成的消息交互。类似地,由新代码创建的消息也可以由旧代码解析:旧二进制文件在解析时忽略新字段。
- 可以删除字段,前提是在更新的消息类型中不再使用此字段编号。您可能需要重命名字段,或者添加前缀
OBSOLETE_
,或者使用reserved保留字段
编号,以便.proto的未来用户不会意外地重用该编号。 int32
、uint32
、int64
、uint64
和bool
都是兼容的——这意味着你可以在不破坏向前或向后兼容性的情况下将一个字段从这些类型中的一个更改为另一个。- 如果一个数字被解析到一个并不适当的类型中,你会得到与在 C++ 中将数字转换为该类型相同的效果(例如,如果一个64位的数字被读作 int32,它将被截断为32位)
sint32
和sint64
相互兼容,但与其他整数类型不兼容。string
和bytes
是兼容的,只要字节是有效的 UTF-8。- 如果
bytes
包含消息的编码版本,则嵌入的消息与字节兼容。 fixed32
与sfixed32
兼容fixed64
与sfixed64
兼容。- 对于
string
、bytes
和消息字段,optional
字段与repeated
字段兼容。给定重复字段的序列化数据作为输入,如果该字段是基元类型
字段,则期望该字段为optional(可选的)
字段的客户端将获取最后一个输入值;如果该字段是消息类型字段,则合并所有输入元素。请注意,对于数值类型(包括bool
和enum
),这通常是不安全的。数字类型的重复字段可以按压缩格式序列化,当需要可选字段时,将无法正确解析压缩格式。 enum
在格式方面与int32
、uint32
、int64
和uint64
兼容(请注意,如果不适合,值将被截断)。但是要注意,当消息被反序列化时,客户端代码可能会对它们进行不同的处理:例如,未识别的proto3枚举类型将保留在消息中,但在反序列化消息时如何表示这些类型取决于客户端语言。整型字段总是保持它们的值。- 将单个值更改为新的
oneof
成员是安全的,并且二进制兼容。如果确保没有代码一次设置多个字段,那么将多个字段移动到新的oneof
字段中可能是安全的。将任何字段移到现有oneof
字段中都不安全。
未知字段
- 未知字段是格式良好的协议缓冲区序列化数据,表示解析器无法识别的字段。例如,当一个旧二进制代码解析一个带有新字段的新二进制代码发送的数据时,这些新字段在旧二进制代码中成为未知字段。
- 最初,proto3消息在解析过程中总是丢弃未知字段,但在3.5版中,重新引入了未知字段的保留,以匹配proto2的行为。
- 在版本3.5和更高版本中,解析期间保留未知字段,并将其包含在序列化输出中。
any任意类型
- Any 消息类型允许你将消息作为嵌入类型使用,而不需要其 .proto 定义。
- Any包含一个以字节表示的任意序列化消息,以及一个URL,该URL充当该消息的全局唯一标识符并解析为该消息的类型。
- 要使用 Any类型,需要导入google/protobuf/any.proto。
- 如果编译报错,将上述文件下载好复制到自己的proto 目录再编译。
import "google/protobuf/any.proto";message ErrorStatus {string message = 1;repeated google.protobuf.Any details = 2;
}
给定消息类型的默认类型 URL 是type.googleapis.com/_packagename_._messagename_
。
不同的语言实现将支持运行库助手以类型安全的方式打包和解包 Any值。例如在java中,Any类型会有特殊的pack()和unpack()访问器,在C++中会有PackFrom()和UnpackTo()方法。
oneof
如果在平时在一个消息有许多字段,但是最多设置一个字段,我们可以使用oneof 来执行并节省内存。
Oneof 字段类似于常规字段,除了Oneof共享内存的所有字段之外,最多可以同时设置一个字段。设置Oneof 的任何成员都会自动清除所有其他成员。您可以使用case()或WhichOneof()方法检查Oneof 中的哪个值被设置(如果有的话),具体取决于选择的语言。
syntax = "proto3";
option go_package = "protos/pbs";message SubMessage {int32 Id=1;string Age2=2;}
message SampleMessage {oneof test_oneof {string name = 4;SubMessage sub_message = 9;}
}
要定义 oneof 字段需要在你的.proto文件中使用oneof关键字并在后面跟上名称,然后将其中一个字段添加到该字段的定义中。你可以添加任何类型的字段,除了map字段和repeated字段。
oneof 特性
- 设置一个字段将自动清除该字段的所有其他成员。如果设置了多个 oneof字段,那么只保留最后的一个值。
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message(); // Will clear name field.
CHECK(!message.has_name());
- oneof 不支持repeated。
- 反射作用于oneof的字段。
package mainimport ("fmt""grpcdemo/protobuf/any/protos/pbs"
)func main() {p:=&pbs.SampleMessage{TestOneof: &pbs.SampleMessage_Name{Name: "hello"},}fmt.Println(p)fmt.Println(p.GetTestOneof())p.TestOneof=&pbs.SampleMessage_SubMessage{SubMessage: &pbs.SubMessage{Id: 1}}fmt.Println(p)fmt.Println(p.GetTestOneof())
}
兼容性问题
添加或删除其中一个字段时要小心。如果检查 oneof 的值返回 None/NOT_SET,则可能意味着 oneof 尚未设置或已设置为 oneof 的另一个字段。这种情况是无法区分的,因为无法知道未知字段是否是 oneof 成员。
标签重用问题
- 将 optional 可选字段移入或移出 oneof:在序列化和解析 message 后,你可能会丢失一些信息(某些字段将被清除)。但是,你可以安全地将单个字段移动到新的 oneof 中,并且如果已知只有一个字段被设置,则可以移动多个字段。
- 删除 oneof 字段并将其重新添加回去:在序列化和解析 message 后,这可能会清除当前设置的 oneof 字段。
- 拆分或合并 oneof:这与移动常规的 optional 字段有类似的问题。
Maps
map<key_type, value_type> map_field = N;message Project {key_type key = 1;value_type value = 2;
}// 比如key是string val是Project类型
map<string, Project> projects = 3;
其中key_type
可以是任何整型或字符串类型(因此,除了浮点类型和字节之外的任何标量类型)。注意enum
不是一个有效的key_type
。value_type
可以是除其他map
以外的任何类型。
- 映射字段不能使用repeated关键字。
- 映射值的Wire格式排序和映射迭代排序未定义,因此不能依赖特定顺序的映射项。
为.proto生成文本格式时,映射按键排序。数字键按数字排序。 - 如果为映射字段提供键但没有值,则序列化字段时的行为与语言有关。在C++、Java和Python中,类型的默认值被序列化,而在其他语言中没有任何序列化。
- 生成的map API目前适用于所有proto3支持的语言。
Packages
可以向.proto文件中添加可选的package明符,以防止协议消息类型之间的名称冲突,
可以理解为go的包。
package foo.bar;
message Open { ... }
定义服务
如果要在RPC(Remote Procedure Call,远程过程调用)系统中使用消息类型,可以在.proto文件中定义RPC服务接口。
service SearchService {rpc Search(SearchRequest) returns (SearchResponse);
}
JSON 映射
proto3 | JSON | JSON example | 描述【译】 |
---|---|---|---|
message | object | {"fooBar": v, | 生成JSON对象。消息字段名被映射到lowerCamelCase并成为JSON对象键。如果指定了json_name 选项,则指定的值将用作键。解析器接受小驼峰命秘法名称(或由json_name 选项指定的名称)和原始proto字段名称。null是所有字段类型的可接受值,并被视为相应字段类型的默认值。 |
enum | string | "FOO_ | 使用proto中指定的枚举值的名称。解析器接受枚举名和整数值。 |
map<K,V> | object | {"k": v, | 所有键都转换为字符串。 |
repeated V | array | [v, | null被接受为空列表[]。 |
bool | true, false | true, | |
string | string | "Hello World!" | |
bytes | base64 string | "YWJjMTIzIT8kKiYoKSctPUB+" | JSON值将是使用带填充的标准base64编码的字符串编码的数据。包含或不包含填充的标准或url安全base64编码都可以接受。 |
int32, fixed32, uint32 | number | 1, | JSON值将是一个十进制数。可接受数字或字符串。 |
int64, fixed64, uint64 | string | "1", | JSON值将是十进制字符串。可接受数字或字符串。 |
float, double | number | 1. | JSON值将是一个数字或特殊字符串值"NaN", "Infinity",和"-Infinity"中的一个。数字或字符串都可以接受。指数符号也被接受。-0被认为等于0。 |
Any | object | {"@type": "url", | 如果`Any`类型包含一个具有特殊JSON映射的值,它将按如下方式转换:{"@type":xxx,"value":yyy} 。否则,该值将转换为JSON对象,并插入“@type”字段以指示实际的数据类型。 |
Timestamp | string | "1972-01-01T10:00:20. | 使用RFC3339,其中生成的输出将始终是**Z**规格化的,并使用0、3、6或9个小数位数。也接受“Z”以外的偏移。 |
Duration | string | "1. | 生成的输出总是包含0、3、6或9个小数位数(取决于所需的精度),后跟“s”后缀。接受任何小数位数(没有小数也可以),只要它们符合纳秒精度,并且需要“s”后缀。 |
Struct | object | { … } | 任何JSON对象。见struct.proto 。 |
Wrapper types | various types | 2, | 包装器在JSON中使用与包装原语类型相同的表示形式,只是在数据转换和传输期间允许并保留null。 |
FieldMask | string | "f. | 见field_mask.proto |
ListValue | array | [foo, | |
Value | value | 任何JSON值。详见google.protobuf.Value | |
NullValue | null | JSON null | |
Empty | object | {} | 空的JSON对象 |
Options
-
.proto文件中的单个声明可以使用许多 选项 进行注释。选项不会更改声明的总体含义,但可能会影响在特定上下文中处理声明的方式。可用选项的完整列表在google/protobuf/descriptor.proto中定义。
-
deprecated选项: 设为true 代表字段被废弃,在新代码不应该被使用,在大多数语言都没有实际的效果
option go_package = "tmp/pb"; // 指定生成的Go代码在你项目中的导入路径int32 old_field = 6 [deprecated = true];
参考文档
[1] 深入解析protobuf
相关文章:
Protocol Buffers V3语法全解
目录protobuf介绍protobuf使用protoc命令语法定义消息类型指定字段类型分配字段编号指定字段规则添加更多消息类型注释保留字段从.proto文件生成了什么?值类型默认值枚举使用其他消息类型导入定义嵌套类型更新消息类型未知字段any任意类型oneofoneof 特性兼容性问题…...
MediaPipe之人体关键点检测>>>BlazePose论文精度
BlazePose: On-device Real-time Body Pose tracking BlazePose:设备上实时人体姿态跟踪 论文地址:[2006.10204] BlazePose: On-device Real-time Body Pose tracking (arxiv.org) 主要贡献: (1)提出一个新颖的身体姿态跟踪解决…...
CSS从入门到精通专栏简介
先让我们来欣赏几个精美的网站: Matt Brett - Freelance Web Designer and WordPress Expert 2022 Year in Review • Letterboxd NIO蔚来汽车官方网站 小米官网 Silk – Interactive Generative Art 大屏数据可视化 你是否也有过这样的“烦恼”: * …...
day01常用DOS命令
day01课堂笔记(第一章 Java开发环境的搭建) 1、常用的DOS命令 1.1、怎么打开DOS命令窗口 win键 r (组合键):可以打开“运行”窗口 在运行窗口文本框中输入: cmd 然后回车 1.2、什么是DOS命令呢? 在DOS命令…...
Java设计模式-生成器模式(建造模式)
1.1定义 维基百科定义 生成器模式(英:Builder Pattern)是一种设计模式,又名:建造模式,是一种对象构建模式。 它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象…...
ansible的常用模块介绍
ansible 常用命令/usr/bin/ansible #Ansibe AD-Hoc 临时命令执行工具,常用于临时命令的执行/usr/bin/ansible-doc #Ansible 模块功能查看工具/usr/bin/ansible-galaxy #下载/上传优秀代码或Roles模块 的官网平台,基于网络的/usr/bin/ansible-playbo…...
你不会还不知道如何监测用户的网络是否在线吧?
我最近遇到一个需求,要给网站添加一个用户网络离线提醒。要求我们要实时监测用户的网络状态,当用户断网了,我们要立马给用户弹出一个断网提醒。 那你可能会问,为什么要做这么一个需求呢?用户断网了,网页不…...
ASM Quorum FailGroup RAC on Extended Distance Clusters
法定容错组,和它失去联系也不影响集群运行 参考: How to Manually Add NFS voting disk to an Extended Cluster using ASM in 11.2 (Doc ID 1421588.1) Mount Options for Oracle files when used with NFS on NAS devices (Doc ID 359515.1) RAC: Fre…...
VHDL语言基础-时序逻辑电路-触发器
目录 触发器: D触发器: 触发器的VHDL描述: 触发器的仿真波形如下:编辑 时钟边沿检测的三种方法: 方法一: 方法二: 方法三: 带有Q非的D触发器: 带有Q非的D触发器的描述&am…...
也许你应该学学 postman了
使用 最简单的方法就是直接在浏览器中复制 Copy as cURL ,然后把数据导入 postman,然后 send ,收工。 我们这里拿 知乎首页 举例 在对应的请求下复制 cURL 打开 postman , 点击左上角的 Import , 选择Paste Raw Tex…...
VHDL语言基础-状态机设计-ASM图法状态机设计
目录 有限状态机的描述方法: ASM图: 状态转移图: 状态转移列表: MDS图: ASM图法状态机设计: ASM图的组成: 状态框: 判断框: 条件框: 状态框与条件框…...
Python文件的属性获取,重命名,目录的创建,显示和改变
1. 文件的属性获取 os.stat()函数可以获取文件的属性,该函数会返回一个和系统平台有关的stat_result对象, 具备一组可访问的属性,可以通过 stat_result.attribute 这样的格式来访问各个属性的值。 字 段描 述st_modeinode 保护模式st_inoin…...
好用的iPhone 数据恢复软件精选
随着 Apple 的 iTunes / iCloud 备份服务的兴起,我们总是假设这些信息在我们需要的时候可以随时访问。然而,事实是,意想不到的“不幸”发生了,比如 iOS 升级失败、忘记密码,或者更严重的情况,如进水或被盗。…...
Linux搭建redis集群6.x版本【超简单】
Linux搭建redis集群6.x版本【超简单】::::本文主要展示如何在一台服务器上搭建集群,核心思想就是复制实例,修改启动端口,实际上跟在几台服务器的操作都是一样的。一.安装redis wget http://dow…...
双重检查锁是如何避免缓存雪崩的,代码例子说明
双重检查锁是如何避免缓存雪崩的什么是缓存雪崩解决方案双重检查锁是如何工作的什么是缓存雪崩 缓存雪崩是指缓存同时失效,造成大量的缓存请求都请求到后端数据库,导致后端系统压力过大而瘫痪的情况。 解决方案 设置缓存的失效时间为随机值࿰…...
【成为架构师课程系列】架构设计中的核心思维方法
架构设计中的核心思维方法 目录 前言 #一、抽象思维 #二、分层思维 #三、分治思维 #四、演化思维 #五、如何培养架构设计思维...
Apollo/Nacos配置动态刷新原理及优劣
一. 配置方式 这里只说与Spring集成后的配置方式,这也是项目中主要使用的方式 Apollo 在属性上直接加value注解,这个属性就会随着配置的更改动态更新类实现ConfigChangeListener,在类中方法上ApolloConfigChangeListener注解,注解…...
docker的基本管理
Docker的概念云计算三层架构服务说明应用IAAS基础设施及服务硬件(服务器、网络设置、防火墙等)虚拟化网络虚拟化(大二层)例:openstackPAAS平台及服务环境例:数据库、 docker 、kubernetesSAAS应用及服务应用…...
2023年房地产投资-租金和IRR研究报告
第一章 概况 房地产投资租赁是指置业投资者在购买到物业后,首先对该物业进行适当整饰与装修,之后以出租人的身份,以口头协议或签订合同的形式,将房屋交付承租人占有、使用与收益,由承租人向出租人交付租金的行为。通过…...
2023-2-10刷题情况
青蛙过河 题目描述 小青蛙住在一条河边, 它想到河对岸的学校去学习。小青蛙打算经过河里 的石头跳到对岸。 河里的石头排成了一条直线, 小青蛙每次跳跃必须落在一块石头或者岸上。 不过, 每块石头有一个高度, 每次小青蛙从一块石头起跳, 这块石头的高度就 会下降 1 , 当石头…...
Python学习-----无序序列2.0(集合的创建、添加、删除以及运算)
目录 前言: 什么是集合 集合的三大特性 1.集合的创建 (1)直接创建 (2)强制转换 2.集合的添加 (1)add()函数 (2)update() 函数 3.集合元…...
2023最详细的接口测试用例设计教程
一、接口测试流程 1、需求讨论 2、需求评审 3、场景设计 4、数据准备 5、测试执行 二、分析接口文档元素 1、接口名称 2、接口地址 3、支持格式 4、请求方式 5、请求参数(参数名称、类型、是否必填、参数说明等) 6、返回参数(返回…...
【数据库】 数据库的理论基础详解
目录 一, 什么是数据库 二, 数据库管理系统(DBMS) 三,数据库与文件系统的区别 1,对比区别: 2,优缺点总结: 四,数据库的发展史 五,常见数据库 1, 关系型…...
Linux环境运行Maven 生成的hadoop jar包
运行命令: hadoop jar ./jar包名字 class对象路径 输入路径 输出路径 linux内部jar包测试 cd 到以下目录,创建以下文件夹 [rootreagan180 ~]# cd /opt/soft/hadoop313/share/hadoop/mapreduce/ 创建文件夹(读取路径) [roo…...
ThreadPoolExecutor原理解析
1. 工作原理1.1 流程图1.2 执行示意图从上图得知如果当前运行的线程数小于corePoolSize(核心线程数),则会创建新线程作为核心线程来执行任务(注意,执行这一步需要获取全局锁)。如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQue…...
谷粒学苑第二章前端框架-2.2前端框架开发过程
一、前端框架开发过程 第一步:添加路由 src/router模块用来管理路由。 第二步:点击某个路由,显示路由对应页面内容 component: () > import(/views/table/index), 表示路由对应的页面,是views/table/index.vue页面 第三步&a…...
权限管理实现的两种方式(详解)
登录的接口请求的三个内容:1. token2. 用户信息、角色信息3. 菜单信息第一种:基于角色Role的动态路由管理 (不推荐,但市场用的比较多)首先列出枚举每个角色对应几个路由,然后根据用户登录的角色遍历枚举出来的角色动态注册对应的路…...
【C++】智能指针思路解析和模拟实现
此篇文章就从以下几个方面出发,带你了解智能指针的方方面面1.为什么需要智能指针当我们开辟内存并使用的时候,我们的顺序应该是这样:开辟内存-》使用内存-》释放内存问题就出现在第三步,开辟好了,也使用了,…...
SpringCloud(18):Sentinel流控降级入门
Sentinel本地应用流控降级实现分为三步: 创建本地应用搭建本地Sentinel控制台本地应用接入本地Sentinel控制台1 本地应用创建 整体流程分析 创建springboot项目在项目的pom.xml文件中引入sentinel-core的依赖坐标创建TestController,定义使用限流规则运行测试具体流程 1.创…...
C++【多态】
文章目录1、多态的概念2、多态的定义及实现2-1、多态的构成条件2-2、虚函数2-3、虚函数的重写2-4 多态样例2-5、协变2-6、 析构函数与virtual2-7、函数重载、函数隐藏(重定义)与虚函数重写(覆盖)的对比2-8、override 和 final&…...
广告制作公司怎么拉业务/百度seo 优化
原文地址为: 看了保证你笑到抽筋 2010最新语录1、种草不让人去躺,不如改种仙人掌! 2、我心眼儿有些小,但是不缺;我脾气很好,但不是没有! 3、人和猪的区别就是:猪一直是猪,…...
网站开发所需基础知识/深圳网站优化公司
故障现象:逻辑DG数据库日志能够应用,但是确不会将应用后的日志删除,查看日志发现已经自动设置了TURNING OFF LOG AUTO DELETECompleted: alter database register logfile /archivelog/archive_2_25797_614088933.arcTue Apr 24 11:06:10 201…...
网站开发定制/seo软件全套
作业要求一 提交截图: 6-7: 6-8: 6-9: 7-1: 7-2: 7-3: 7-4: 7-5: 作业要求二 题目6-7删除字符中数字字符 1、设计思路: (1)第一步:本题…...
常熟高端网站建设/石家庄seo代理商
实现了五层协议1)物理层:对应osi的物理层2)网络接口层;osi的数据链路层3)internet层:osi模型在internet网使用前提出,未考虑网间连接。4)传输层:5)应用层:对应…...
58网站怎么样做效果会更好/千锋教育出来好找工作吗
电脑加网络硬盘步骤有哪些电脑加网络硬盘步骤有哪些?今天应届毕业生小编要给大家介绍的是电脑加网络硬盘的方法!下面是具体步骤请大家仔细观看!一、申请开通请在“用户注册”页面按要求注册。二、使用方式(网络硬盘新IP地址是:10.100.48.29)网络硬盘有以下几种访问…...
温岭网站开发/朋友圈信息流广告投放价格
Exchange Server 2016 RTM 可以在 Windows Server 2012、Windows Server 2012 R2 和 Windows Server 2016 的标准版和企业版中进行部署安装,但 Exchange Server 2016 只支持完整 GUI 的 Windows Server 而不支持核心安装的系统,本文我们将对快速部署方式…...