【ProtoBuf】proto 3 语法 -- 详解

这个部分会对通讯录进行多次升级,使用 2.x 表示升级的版本,最终将会升级如下内容:

  • 不再打印联系人的序列化结果,而是将通讯录序列化后并写入文件中。
  • 从文件中将通讯录解析出来,并进行打印。
  • 新增联系人属性,共包括:姓名、年龄、电话信息、地址、其他联系方式、备注

一、字段规则

消息的字段可以用下面几种规则来修饰:
  • singular:消息中可以包含该字段零次或⼀次(不超过一次)。 proto3 语法中,字段默认使用该规则。
  • repeated:消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理解为定义了一个数组。

更新 contacts.proto, PeopleInfo 消息中新增 phone_numbers 字段,表示一个联系人有多个号码,可将其设置为 repeated,写法如下:


二、消息类型的定义与使用

1、定义

在单个 .proto 文件中可以定义多个消息体,且支持定义嵌套类型的消息(任意多层)。每个消息体中的字段编号可以重复。

更新 contacts.proto,我们可以将 phone_number 提取出来,单独成为一个消息:

嵌套写法:

非嵌套写法:


2、使用

(1)消息类型可作为字段类型使用


(2)可导入其他 .proto 文件的消息并使用

例如:Phone 消息定义在 phone.proto 文件中:

contacts.proto 中的 PeopleInfo 使用 Phone 消息:

注意 :在 proto3 文件中可以导入 proto2 消息类型并使用它们,反之亦然。

3、创建通讯录 2.0 版本

通讯录 2.x 的需求是向文件中写入通讯录列表,以上只是定义了一个联系人的消息,并不能存放通讯录列表,所以还需要再完善一下 contacts.proto(终版通讯录 2.0):

接着进行一次编译:

contacts.pb.h 更新的部分代码展示:

新增了 PeopleInfo_Phone 类:

更新了 PeopleInfo 类:

新增了 Contacts 类:

上述的例子中:

  • 每个字段都有⼀个 clear_ 方法,可以将字段重新设置回 empty 状态。
  • 每个字段都有设置和获取的方法, 获取方法的方法名称与小写字段名称完全相同。但如果是消息类型的字段,其设置方法为 mutable_ 方法,返回值为消息类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改。
  • 对于使用 repeated 修饰的字段,也就是数组类型,pb 为我们提供了 add_ 方法来新增⼀个值,并且提供了 _size 方法来判断数组存放元素的个数。

(1)通讯录 2.0 的写入实现


A. write.cc(通讯录 2.0)


B. Makefile


C. 运行


D. 查看二进制文件

解释:

  • hexdump:是 Linux 下的一个二进制文件查看工具,它可以将二进制文件转换为 ASCII、八进制、十进制、十六进制格式进行查看。
  • -C:表示每个字节显示为 16 进制和相应的 ASCII 字符。

(2)通讯录 2.0 的读取实现

A. read.cc(通讯录 2.0)


B. Makefile(升级)


C. 运行


D. 另⼀种验证方法  --decode

可以用命令:protoc -h 来查看 ProtoBuf 为我们提供的所有命令 option。

其中 ProtoBuf 提供一个命令选项 --decode,表示从标准输入中读取给定类型的二进制消息,并将其以文本格式写入标准输出。

消息类型必须在 .proto 文件或导入的文件中定义。

在这里是将 utf-8 汉字转为八进制格式输出了。


三、enum 类型

1、定义规则

语法支持我们定义枚举类型并使用。在 .proto 文件中枚举类型的书写规范为:

(1)枚举类型名称

使用驼峰命名法首字母大写。 例如:MyEnum

(2)常量值名称

全大写字母多个字母之间用 _ 连接。例如:ENUM_CONST = 0;

我们可以定义一个名为 PhoneType 的枚举类型,定义如下:

要注意枚举类型的定义有以下几种规则:

  1. 0 值常量必须存在,且要作为第一个元素。这是为了与 proto2 的语义兼容:第一个元素作为默认值,且值为 0。
  2. 枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)。
  3. 枚举的常量值在 32 位整数的范围内。但因负值无效因而不建议使用(与编码规则有关)。

2、定义时注意

将两个 ‘具有相同枚举值名称’ 的枚举类型放在单个 .proto 文件下测试时,编译后会报错:某某某常量已经被定义!所以这里要注意:

  • 同级(同层)的枚举类型,各个枚举类型中的常量不能重名。

  • 单个 .proto 文件下,最外层枚举类型和嵌套枚举类型,不算同级。

  • 多个 .proto 文件下,若一个文件引入了其他文件,且每个文件都未声明 package,每个 proto 文件中的枚举类型都在最外层,算同级。

  • 多个 .proto 文件下,若一个文件引入了其他文件,且每个文件都声明了 package,不算同级。


(1)情况 1:同级枚举类型包含相同枚举值名称

编译后报错:MP 已经定义。

(2)情况 2:不同级枚举类型包含相同枚举值名称

用法正确。


(3)情况 3:多文件下都未声明 package

test_enum.proto:

test_enum2.proto:

编译后报错:MP 已经定义。

(4)情况 4:多文件下都声明了 package

test_enum.proto:

test_enum2.proto:

用法正确。


3、升级通讯录至 2.1 版本

更新 contacts.proto(通讯录 2.1),新增枚举字段并使用,更新内容如下:

编译:


(1)contacts.pb.h 更新的部分代码展示:

A. 新生成的 PeopleInfo_Phone_PhoneType 枚举类


B. 更新的 PeopleInfo_Phone 类

上述的代码中:
  • 对于在 .proto 文件中定义的枚举类型,编译生成的代码中会含有与之对应的枚举类型、校验枚举值是否有效的方法 _IsValid、以及获取枚举值名称的方法 _Name。

  • 对于使用了枚举类型的字段,包含设置和获取字段的方法,已经清空字段的方法 clear_。


(2)更新 write.cc(通讯录 2.1)


(3)更新 read.cc(通讯录 2.1)


(4)代码完成后,编译后进行读写验证

张三的联系电话类型打印出 MP 是因为未设置该字段,导致用了枚举的第一个元素作为默认值。


四、Any 类型

字段还可以声明为 Any 类型,可以理解为泛型类型。使用时可以在 Any 中存储任意消息类型,Any 类型的字段也用 repeated 来修饰。

Any 类型是 Google 已经帮我们定义好的类型,在安装 ProtoBuf 时,其中的 include 目录下查找所有 Google 已经定义好的 .proto 文件。


1、升级通讯录至 2.2 版本

通讯录 2.2 版本会新增联系人的地址信息,我们可以使用 any 类型的字段来存储地址信息。

(1)更新 contacts.proto(通讯录 2.2)

编译:


(2)contacts.pb.h 更新的部分代码展示

A. 新生成的 Address


B. 更新的 PeopleInfo 类

上述的代码中,对于 Any 类型字段:
  • 设置和获取:获取方法的方法名称与小写字段名称完全相同。设置方法可以使用 mutable_ 方法,返回值为 Any 类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改。

(3)更新 write.cc(通讯录 2.2)


(4)更新 read.cc(通讯录 2.2)


(5)代码编写完成后,编译后进行读写


五、oneof 类型

如果消息中有很多可选字段, 并且将来同时只有一个字段会被设置, 那么就可以使用 oneof 加强这个行为,也能有节约内存的效果。


1、升级通讯录至 2.3 版本

通讯录 2.3 版本想新增联系⼈的其他联系方式,比如 qq 或者微信号二选一,我们就可以使用 oneof 字段来加强多选一这个行为。

oneof 字段定义的格式为:oneof 字段名 { 字段1; 字段2; ... }


(1)更新 contacts.proto(通讯录 2.3)

  • 可选字段中的字段编号,不能与非可选字段的编号冲突。
  • 不能在 oneof 中使用 repeated 字段。
  • 将来在设置 oneof 字段中值时,如果将 oneof 中的字段设置多个,那么只会保留最后一次设置的成,之前设置的 oneof 成员会自动清除。

编译:


(2)Acontacts.pb.h 更新的部分代码展示

A. 更新的 PeopleInfo 类

上述的代码中,对于 oneof 字段:

  • 会将 oneof 中的多个字段定义为一个枚举类型。
  • 设置和获取:对 oneof 内的字段进行常规的设置和获取即可,但要注意只能设置⼀个。如果设置多个,那么只会保留最后一次设置的成员。
  • 清空 oneof 字段:clear_ 方法。
  • 获取当前设置了哪个字段:_case 方法。

(3)更新 write.cc(通讯录 2.3)


(4)更新 read.cc(通讯录 2.3)


(5)代码编写完成后,编译后进行读写


六、map 类型

语法支持创建一个关联映射字段,也就是可以使用 map 类型去声明字段类型,格式为:

map<key_type, value_type> map_field = N;
  • key_type 是除了 float 和 bytes 类型以外的任意标量类型。value_type 可以是任意类型。
  • map 字段不可以用 repeated 修饰。
  • map 中存入的元素是无序的。

1、升级通讯录至 2.4 版本

最后,通讯录 2.4 版本想新增联系人的备注信息,可以使用 map 类型的字段来存储备注信息

(1)更新 contacts.proto(通讯录 2.4)

编译:


(2)contacts.pb.h 更新的部分代码展示

A. 更新的 PeopleInfo 类

上述的代码中,对于 Map 类型的字段:
  • 清空 map:clear_ 方法。
  • 设置和获取:获取方法的方法名称与小写字段名称完全相同。设置方法为 mutable_ 方法,返回值为 Map 类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改。

(3)更新 write.cc(通讯录 2.4)


(4)更新 read.cc(通讯录 2.4)


(5)代码编写完成后,编译后进行读写

到此,我们对通讯录 2.x 要求的任务全部完成。


七、默认值

反序列化消息时,如果被反序列化的二进制序列中不包含某个字段,反序列化对象中相应字段时,就会设置为该字段的默认值。

不同的类型对应的默认值不同:

  • 对于字符串默认值为空字符串
  • 对于字节默认值为空字节
  • 对于布尔值默认值为 false
  • 对于数值类型默认值为 0
  • 对于枚举默认值是第⼀个定义的枚举值, 必须为 0。
  • 对于消息字段,未设置该字段。它的取值是依赖于语言
  • 对于设置了 repeated 的字段的默认值是空的(通常是相应语⾔的⼀个空列表)。
  • 对于消息字段oneof 字段和 any 字段,C++ 和 Java 语言中都有 has_ 方法来检测当前字段是否被设置。
  • 对于标量数据类型,在 proto3 语法下,没有生成 has_ 方法

八、更新消息

1、更新规则

如果现有的消息类型已经不再满足我们的需求,例如需要扩展一个字段,在不破坏任何现有代码的情况下更新消息类型非常简单。

遵循如下规则即可:

  • 禁止修改任何已有字段的字段编号

  • 若是移除老字段,要保证不再使用移除字段的字段编号。正确的做法是保留字段编号 (reserved),以确保该编号将不能被重复使用。(不建议直接删除或注释掉字段)

  • int32,uint32,int64,uint64 和 bool 是完全兼容的。可以从这些类型中的一个改为另一个,而不破坏前后兼容性。若解析出来的数值与相应的类型不匹配,会采用与 C++ 一致的处理方案(例如:若将 64 位整数当做 32 位进行读取,它将被截断为 32 位)。

  • sint32 和 sint64 相互兼容,但不与其他的整型兼容。

  • string 和 bytes 在合法 UTF-8 字节前提下也是兼容的。

  • bytes 包含消息编码版本的情况下,嵌套消息与 bytes 也是兼容的。

  • fixed32 与 sfixed32 兼容,fixed64 与 sfixed64兼容。

  • enum 与 int32,uint32, int64 和 uint64 兼容(注意:如果值不匹配会被截断)。但要注意当反序列化消息时会根据语言采用不同的处理方案:例如:未识别的 proto3 枚举类型会被保存在消息中,但是当消息反序列化时如何表示是依赖于编程语言的。整型字段总是会保持其的值。

oneof:

  • ​​​​​​​将一个单独的值更改为新 oneof 类型成员之一是安全和二进制兼容的。

  • 若确定没有代码一次性设置多个值,那么将多个字段移入一个新 oneof 类型也是可行的。

  • 将任何字段移入已存在的 oneof 类型是不安全的。


2、保留字段 reserved

如果通过删除或注释掉字段来更新消息类型,未来的用户在添加新字段时,有可能会使用以前已经存在,但已经被删除或注释掉的字段编号。将来使用该 .proto 的旧版本时的程序会引发很多问题:数据损坏、隐私错误等等。

如果要删除老字段,要保证不使用已经被删除或者已经被注释掉的字段编号。确保不会发生上述这种情况的一种方法是:使用 reserved 将指定字段的编号或名称设置为保留项 。当我们再使用这些编号或名称时,protocol buffer 的编译器将会警告这些编号或名称不可用。举个例子:​​​​​​​

这里它推荐我们改成:int32 birthday = 4;

注意 不要在一行 reserved 声明中同时声明字段编号和名称

(1)创建通讯录 3.0 版本 —— 验证错误删除字段造成的数据损坏

现模拟有两个服务,他们各自使用一份通讯录 .proto 文件,内容约定好了是一模一样的。

  • 服务 1(service):负责序列化通讯录对象,并写入文件中。
  • 服务 2(client):负责读取文件中的数据,解析并打印出来。

  • 一段时间后,service 更新了自己的 .proto 文件,更新内容为:删除了某个字段,并新增了一个字段,新增的字段使用了被删除字段的字段编号,并将新的序列化对象写进了文件。
  • 但 client 并没有更新自己的 .proto 文件。

根据结论,可能会出现数据损坏的现象,下面来验证下这个结论:

新建两个目录:service、client,分别存放两个服务的代码。

A. service 目录下新增 contacts.proto(通讯录 3.0)


B. client 目录下新增 contacts.proto(通讯录 3.0)

分别对两个文件进行编译,可自行操作。


C. 继续对 service 目录下新增 service.cc(通讯录 3.0),负责向文件中写通讯录消息


D. service 目录下新增 makefile


E. client 目录下新增 client.cc(通讯录 3.0),负责向读出文件中的通讯录消息


F. client 目录下新增 makefile


代码编写完成后:再对 service 目录下的 contacts.proto 文件进行更新:删除 age 字段,新增 birthday 字段,新增的字段使用被删除字段的字段编号。

G. 更新后的 contacts.proto(通讯录 3.0)

H. 编译文件 .proto 后,还需要更新一下对应的 service.cc(通讯录 3.0)

这时输入的生日在反序列化时,会被设置到了使用了相同字段编号的年龄上。

结论:如果是移除老字段,要保证不再使用移除字段的字段编号,不建议直接删除或注释掉字段。那么正确的做法是:保留字段编号(reserved),以确保该编号将不能被重复使用。


I. 正确 service 目录下的 contacts.proto 写法如下(终版通讯录 3.0)

编译 .proto 文件后,还需要重新编译下 service.cc,让 service 程序保持使用新生成的 pb C++ 文件。

可以发现 ‘王五’ 的年龄为 0。这是由于新增时未设置年龄,通过 client 程序反序列化时,给年龄字段设置了默认值 0。

再解释一下,假设我们之前有一个存储的数据 ‘李四’,年龄依旧使用了之前设置的生日字段 ‘1221’。那么这是因为在新增 ‘李四’ 时,生日字段的字段编号依旧为 2,并且已经被序列化到文件中了。那么最后再读取时,字段编号依旧为 2。因为使用了 reserved 关键字,ProtoBuf 在编译阶段就拒绝了我们使用已经保留的字段编号。

如果使用了 reserved 2 了,那么 service 给 ‘王五’ 设置的生日 ‘1020’,client 就没法读到了吗?

答案是可以的。


3、未知字段

在通讯录 3.0 版本中,我们向 service 目录下的 contacts.proto 新增了 ‘生日’ 字段,但对于 client 相关的代码并没有任何改动。验证后发现新代码序列化的消息(service)也可以被旧代码(client)解析。新增的 ‘生日’ 字段在旧程序(client)中其实并没有丢失,而是会作为旧程序的未知字段

  • 未知字段解析结构良好的 protocol buffer 已序列化数据中的未识别字段的表示方式。例如,当旧程序解析带有新字段的数据时,这些新字段就会成为旧程序的未知字段。
  • proto3 在解析消息时总是会丢弃未知字段,但在 3.5 版本中重新引⼊了对未知字段的保留机制。所以,在 3.5 或更高版本中,未知字段在反序列化时会被保留,同时也会包含在序列化的结果中。

(1)未知字段从哪获取

A. 了解相关类关系图


B. MessageLite 类介绍(了解)
  • MessageLite 从名字看是轻量级的 message,仅仅提供序列化、反序列化功能。
  • 类定义在 google 提供的 message_lite.h 中。

C. Message 类介绍(了解)
  • 我们自定义的 message 类,都是继承自 Message。
  • Message 最重要的两个接口 GetDescriptor / GetReflection,可以获取该类型对应的 Descriptor 对象指针和 Reflection 对象指针。
  • 类定义在 google 提供的 message.h 中。

D. Descriptor 类介绍(了解)
  • Descriptor:是对 message 类型定义的描述,包括 message 的名字、所有字段的描述、原始的 proto 文件内容等。
  • 类定义在 google 提供的 descriptor.h 中。


E. Descriptor 类介绍(了解)
  • Descriptor:是对 message 类型定义的描述,包括 message 的名字、所有字段的描述、原始的 proto 文件内容等。
  • 类定义在 google 提供的 descriptor.h 中。


F. Reflection 类介绍(了解)
  • Reflection接口类,主要提供了动态读写消息字段的接口,对消息对象的自动读写主要通过该类完成。
  • 提供方法来动态访问 / 修改 message 中的字段,对每种类型,Reflection 都提供了⼀个单独的接口用于读写字段对应的值。

针对所有不同的field类型 FieldDescriptor::TYPE_* ,需要使⽤不同的 Get*() / Set*() / Add*() 接口;

repeated 类型需要使用 GetRepeated*() / SetRepeated*() 接口,不可以和非 repeated 类型接口混用;

message 对象只可以被由它自身的 reflectionmessage.GetReflection()) 来操作;

  • 类中还包含了访问 / 修改未知字段的方法。
  • 类定义在 google 提供的 message.h 中。


G. UnknownFieldSet 类介绍(重要)
  • UnknownFieldSet 包含在分析消息时遇到但未由其类型定义的所有字段。
  • 若要将 UnknownFieldSet 附加到任何消息,请调用 Reflection::GetUnknownFields()。
  • 类定义在 unknown_field_set.h 中。


H. UnknownField 类介绍(重要)
  • 表示未知字段集中的⼀个字段。
  • 类定义在 unknown_field_set.h 中。


(2)升级通讯录 3.1 版本 —— 验证未知字段

A. 更新 client.cc(通讯录 3.1)

在这个版本中,需要打印出未知字段的内容。更新的代码如下:

其他文件均不用做任何修改,重新编译 client.cc,进行一次读操作可得如下结果:

类型为什么为 0 呢?

前面介绍 UnknownField 类中讲到了类中包含了未知字段的几种类型:

enum Type {TYPE_VARINT,TYPE_FIXED32,TYPE_FIXED64,TYPE_LENGTH_DELIMITED,TYPE_GROUP
};
// 类型为 0,即为 TYPE_VARINT。

(4)前后兼容性

根据上述的例子可以得出,pb 是具有向前兼容的。为了叙述方便,把增加了 “生日” 属性的 service 称为 “新模块”;未做变动的 client 称为 “老模块”。

  • 向前兼容:老模块能够正确识别新模块生成或发出的协议。这时新增加的 “生日” 属性会被当作未知字段(pb 3.5 版本及之后)。
  • 向后兼容:新模块也能够正确识别老模块生成或发出的协议。

前后兼容的作用:当我们维护⼀个很庞大的分布式系统时,由于我们无法同时升级所有模块,为了保证在升级过程中,整个系统能够尽可能不受影响,就需要尽量保证通讯协议的 “向后兼容” 或 “向前兼容”。


九、选项 option

.proto 文件中可以声明许多选项,使用 option 标注。选项能影响 proto 编译器的某些处理方式。


1、选项分类

选项的完整列表在 google/protobuf/descriptor.proto 中定义。部分代码:

syntax = "proto2"; // descriptor.proto 使⽤ proto2 语法版本message FileOptions { ... } // ⽂件选项 定义在 FileOptions 消息中message MessageOptions { ... } // 消息类型选项 定义在 MessageOptions 消息中message FieldOptions { ... } // 消息字段选项 定义在 FieldOptions 消息中message OneofOptions { ... } // oneof字段选项 定义在 OneofOptions 消息中message EnumOptions { ... } // 枚举类型选项 定义在 EnumOptions 消息中message EnumValueOptions { .. } // 枚举值选项 定义在 EnumValueOptions 消息中message ServiceOptions { ... } // 服务选项 定义在 ServiceOptions 消息中message MethodOptions { ... } // 服务⽅法选项 定义在 MethodOptions 消息中...

由此可见,选项分为文件级、消息级、字段级等等, 但并没有⼀种选项能作用于所有的类型


2、常用选项列举

(1)optimize_for

该选项为文件选项,可以设置 protoc 编译器的优化级别,分别为 SPEED、CODE_SIZE、 LITE_RUNTIME。受该选项影响,设置不同的优化级别,编译 .proto 文件后生成的代码内容不同。

  • SPEED:protoc 编译器将⽣成的代码是高度优化的,代码运行效率高,但是由此生成的代码编译后会占用更多的空间SPEED 是默认选项。
  • CODE_SIZEproto 编译器将生成最少的类,会占用更少的空间,是依赖基于反射的代码来实现序列化、反序列化和各种其他操作。但和 SPEED 恰恰相反,它的代码运行效率较低。这种方式适合用在包含大量的 .proto 文件,但并不盲目追求速度的应用中。
  • LITE_RUNTIME生成的代码执行效率高,同时生成代码编译后的所占用的空间也是非常少。这是以牺牲 Protocol Buffer 提供的反射功能为代价的,仅仅提供 encoding + 序列化功能,所以我们在链接 BP 库时仅需链接 libprotobuf-lite,而非 libprotobuf。这种模式通常用于资源有限的平台,例如移动手机平台中。
option optimize_for = LITE_RUNTIME;


(2)allow_alias

允许将相同的常量值分配给不同的枚举常量,用来定义别名,该选项为枚举选项。举个例子:


3、设置自定义选项

ProtoBuf 允许自义选项并使用,有兴趣可以参考:

https://protobuf.dev/programming-guides/proto2/

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谈谈大数据采集和常见问题

01 什么是数据采集 数据采集是大数据的基石&#xff0c;不论是现在的互联网公司&#xff0c;物联网公司或者传统的IT公司&#xff0c;每个业务流程环节都会产生大量的数据&#xff0c;同时用户操作的日志也会产生大量的数据&#xff0c;为了将这些结构化和非结构化的数据进行…

【常见开源库的二次开发】基于openssl的加密与解密——单向散列函数(四)

目录&#xff1a; 目录&#xff1a; 一、什么是单项散列函数&#xff1f; 1.1 如何验证文件是否被修改过 1.2 单项散列函数&#xff1a; 二、单向hash抗碰撞 2.1 弱抗碰撞&#xff08;Weak Collision Resistance&#xff09; 2.2 强抗碰撞&#xff08;Strong Collision Resista…

Webpack详解

Webpack Webpack 是一个现代 JavaScript 应用程序的静态模块打包器&#xff08;module bundler&#xff09;。它允许开发者将项目中的资源&#xff08;如 JavaScript、CSS、图片等&#xff09;视为模块&#xff0c;通过分析和处理这些模块之间的依赖关系&#xff0c;将它们打包…

Python酷库之旅-第三方库Pandas(024)

目录 一、用法精讲 61、pandas.to_numeric函数 61-1、语法 61-2、参数 61-3、功能 61-4、返回值 61-5、说明 61-6、用法 61-6-1、数据准备 61-6-2、代码示例 61-6-3、结果输出 62、pandas.to_datetime函数 62-1、语法 62-2、参数 62-3、功能 62-4、返回值 62-…

ospf的MGRE实验

第一步&#xff1a;配IP [R1-GigabitEthernet0/0/0]ip address 12.0.0.1 24 [R1-GigabitEthernet0/0/1]ip address 21.0.0.1 24 [R1-LoopBack0]ip address 192.168.1.1 24 [ISP-GigabitEthernet0/0/0]ip address 12.0.0.2 24 [ISP-GigabitEthernet0/0/1]ip address 21.0.0.2 24…

Hadoop3:HDFS-存储优化之纠删码

一、集群环境 集群一共5个节点&#xff0c;102/103/104/105/106 二、纠删码原理 1、简介 HDFS默认情况下&#xff0c;一个文件有3个副本&#xff0c;这样提高了数据的可靠性&#xff0c;但也带来了2倍的冗余开销。Hadoop3.x引入了纠删码&#xff0c;采用计算的方式&#x…

新一代大语言模型 GPT-5 对工作与生活的影响及应对策略

文章目录 &#x1f4d2;一、引言 &#x1f4d2;二、GPT-5 的发展背景 &#x1f680;&#xff08;一&#xff09;GPT-4 的表现与特点 &#x1f680;&#xff08;二&#xff09;GPT-5 的预期进步 &#x1f4d2;三、GPT-5 对工作的影响 &#x1f680;&#xff08;一&#xf…

交叉编译ethtool(ubuntu 2018)

参考文章&#xff1a;https://www.cnblogs.com/nazhen/p/16800427.html https://blog.csdn.net/weixin_43128044/article/details/137953913 1、下载相关安装包 //ethtool依赖libmul git clone http://git.netfilter.org/libmnl //ethtool源码 git clone http://git.kernel.or…

OpenGL笔记十三之Uniform向量数据传输、使用glUniform3f和glUniform3fv

OpenGL笔记十三之Uniform向量数据传输、使用glUniform3f和glUniform3fv —— 2024-07-14 晚上 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记十三之Uniform向量数据传输、使用glUniform3f和glUniform3fv1.glUniform3f1.1.运行1.2.vs1.3.fs1.4.shader.…

科研绘图系列:R语言雨云图(Raincloud plot)

介绍 雨云图(Raincloud plot)是一种数据可视化工具,它结合了多种数据展示方式,旨在提供对数据集的全面了解。雨云图通常包括以下几个部分: 密度图(Density plot):表示数据的分布情况,密度图的曲线可以展示数据在不同数值区间的密度。箱线图(Box plot):显示数据的中…

.NET MAUI开源架构_1.学习资源分享

最近需要开发Android的App&#xff0c;想预研下使用.NET开源架构.NET MAUI来开发App程序。因此网上搜索了下相关资料&#xff0c;现在把我查询的结果记录下&#xff0c;方便后面学习。 1.官方文档 1.1MAUI官方学习网站 .NET Multi-Platform App UI 文档 - .NET MAUI | Micro…

leetcode简单题27 N.119 杨辉三角II rust描述

// 直接生成杨辉三角当前行 pub fn get_row(row_index: i32) -> Vec<i32> {let mut row vec![1; (row_index 1) as usize];for i in 1..row_index as usize {for j in (1..i).rev() {row[j] row[j] row[j - 1];}}row } // 空间优化的方法 pub fn get_row2(row_ind…

在Mac上免费恢复误删除的Word文档

Microsoft Word for Mac是一个有用的文字处理应用程序&#xff0c;它与Microsoft Office套件捆绑在一起。该软件的稳定版本包括 Word 2019、2016、2011 等。 Word for Mac 与 Apple Pages 兼容;这允许在不同的操作系统版本中使用Word文档&#xff0c;而不会遇到任何麻烦。 与…

springboot websocket 知识点汇总

以下是一个详细全面的 Spring Boot 使用 WebSocket 的知识点汇总 1. 配置 WebSocket 添加依赖 进入maven官网, 搜索spring-boot-starter-websocket&#xff0c;选择版本, 然后把依赖复制到pom.xml的dependencies标签中 配置 WebSocket 创建一个配置类 WebSocketConfig&…

【机器学习】机器学习与图像分类的融合应用与性能优化新探索

文章目录 引言第一章&#xff1a;机器学习在图像分类中的应用1.1 数据预处理1.1.1 数据清洗1.1.2 数据归一化1.1.3 数据增强 1.2 模型选择1.2.1 卷积神经网络1.2.2 迁移学习1.2.3 混合模型 1.3 模型训练1.3.1 梯度下降1.3.2 随机梯度下降1.3.3 Adam优化器 1.4 模型评估与性能优…

SD-WAN组网搭建5G备份方案实现方式

SD-WAN&#xff08;Software-Defined Wide Area Network&#xff0c;软件定义广域网&#xff09;结合5G作为备份链路是现代企业网络弹性策略的一部分&#xff0c;尤其是在需要高可用性和快速故障切换的场景下。以下是实现SD-WAN组网并集成5G备份方案的一般步骤&#xff1a; 1. …

Postfix+Dovecot+Roundcube开源邮件系统搭建系列1-2:系统搭建目标+MariaDB数据库配置(MySQL)

1. 系统搭建目标 通过本系列文章&#xff0c;最终可以部署一套提供如下服务的邮件系统&#xff1a; SMTP服务&#xff1a;由Postfix提供&#xff0c;监听25、465、587端口。POP3服务&#xff1a;由Dovecot提供&#xff0c;监听110、995端口。IMAP服务&#xff1a;由Dovecot提…

2024全球和国内最常用的弱密码,有没有你的?

密码管理器NordPass分析了来自公开来源的超过4.3TB 的密码数据&#xff0c;找出了当前为止&#xff08;2024年&#xff09;最常用&#xff08;最脆弱&#xff09;的密码。 这些密码主要有下面这些特征&#xff1a; 简单且常用&#xff0c;万年弱密码&#xff0c;比如123456、a…

Qt支持LG高级汽车内容平台

Qt Group与LG 电子&#xff08;简称LG&#xff09;正携手合作&#xff0c;将Qt软件框架嵌入其基于 webOS的ACPLG车载娱乐平台&#xff0c;用于应用程序开发。该合作旨在让原始设备制造商&#xff08;OEM&#xff09;的开发者和设计师能为汽车创建更具创新性的沉浸式汽车内容流媒…

Flutter应用开发:掌握StatefulWidget的实用技巧

前言 随着移动应用的日益复杂&#xff0c;状态管理成为了 Flutter 应用开发中的一项重要挑战。 状态&#xff0c;即应用中的可变数据&#xff0c;它驱动着用户界面的渲染和交互。 在 Flutter 这样的声明式 UI 框架中&#xff0c;如何高效、可维护地管理状态&#xff0c;对于…