使用C++快速上手ProtoBuf (一)

news2024/11/22 16:06:21

文章目录

    • 课程目标
    • 一、初始ProtoBuf
      • 1. 序列化概念
      • 2.ProtoBuf是什么
      • 3.ProtoBuf的使⽤特点
    • 二、安装ProtoBuf
    • 三、教学思路
    • 四、快速上⼿
      • 步骤1:创建.proto文件
      • 步骤2:编译contacts.proto⽂件,⽣成C++⽂件
      • 步骤3:序列化与反序列化的使⽤
      • ⼩结ProtoBuf使⽤流程

课程目标

  • 认识ProtoBuf是什么,写demo了解ProtoBuf的使用流程。
  • 学习proto3语法,对最新的语法进行深度学习
  • 实战ProtoBuf,在网络传输过程中的使用。
  • 总结,对比多种序列化协议,分析ProtoBuf适合那些场景的使用。

一、初始ProtoBuf

在这里插入图片描述

1. 序列化概念

序列化和反序列化:

  • 序列化:把对象转换为字节序列的过程称为对象的序列化。
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

什么情况下需要序列化:

  • 存储数据:当你想把的内存中的对象状态保存到⼀个⽂件中或者存到数据库中时。
  • ⽹络传输:⽹络直接传输数据,但是⽆法直接传输对象,所以要在传输前序列化,传输完成后反序列化成对象。例如我们之前学习过socket编程中发送与接收数据。

如何实现序列化:

  • xml、json、ProtoBuf。

2.ProtoBuf是什么

将结构化数据进行序列化的一种方式。

文档解释:

  • ProtocolBuffers是Google的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤于(数据)通信协议、数据存储等。
  • ProtocolBuffers类⽐于XML,是⼀种灵活,⾼效,⾃动化机制的结构数据序列化⽅法,但是⽐XML更⼩、更快、更为简单。
  • 你可以定义数据的结构,然后使⽤特殊⽣成的源代码轻松的在各种数据流中使⽤各种语⾔进⾏编写和读取结构数据。你甚⾄可以更新数据结构,⽽不破坏由旧数据结构编译的已部署程序。

简单来讲,ProtoBuf(全称为ProtocolBuffer)是让结构数据序列化的⽅法,其具有以下特点

  • 语言⾔⽆关、平台⽆关:即ProtoBuf⽀持Java、C++、Python等多种语⾔,⽀持多个平台例如linux或者windows。
  • ⾼效:即⽐XML更⼩、更快、更为简单。-- 序列化的数据更小,数据小意味着传输更快,操作更简单。
  • 扩展性、兼容性好:你可以更新数据结构,⽽不影响和破坏原有的旧程序。-- 序列化组件的共同特点

3.ProtoBuf的使⽤特点

程序员手动编写网络协议,最原始的方式:

  • 网络通信就是端对端通信,由请求协议,响应协议,协议的反序列化和序列方法构成。
  • 请求协议和响应协议就是数据结构。
  • 网络传输的是二进制序列,序列化最原始的方法就是将数据结构以二进制的形式转换为序列。
  • 反序列化最原始的方法就是使用数据结构进行反序列化。
  • 最原始的方式的优点:
    • 操作简单
  • 最原始的方式的缺点:
    • 程序员需要手动编写序列化和反序列化方法,例如编写时需要解决粘包问题。
    • 没有扩展性,兼容性:一旦一端数据结构更新,影响和破坏旧程序。

ProtoBuf的使⽤特点:

  • 如下图,
  • 一开始,程序员定制协议时
  • 对于程序员来说,属性字段必须写的,后两项开发起来繁琐耗时。
    在这里插入图片描述
  • 如下图,
  • 有了ProtoBuf我们定义的是message而不是class。
  • 程序员只需要定义一系列的属性字段。
  • 处理字段的方法和处理类的方法由Protoc编译器生成。

在这里插入图片描述

  • 如下图,
  • 编写.proto⽂件,⽬的是为了定义结构对象(message)及属性内容。
  • 使⽤protoc编译器编译.proto⽂件,⽣成⼀系列接⼝代码,存放在新⽣成头⽂件和源⽂件中。
  • 依赖⽣成的接⼝,将编译⽣成的头⽂件包含进我们的代码中,实现对.proto⽂件中定义的字段进⾏设置和获取,和对message对象进⾏序列化和反序列化。
    在这里插入图片描述

总的来说:ProtoBuf是需要依赖通过编译⽣成的头⽂件和源⽂件来使⽤的。有了这种代码⽣成机制,开发⼈员再也不⽤吭哧吭哧地编写那些协议解析的代码了(⼲这种活是典型的吃⼒不讨好)。

二、安装ProtoBuf

https://editor.csdn.net/md/?articleId=130808175

三、教学思路

对ProtoBuf的完整学习,将使⽤项⽬推进的⽅式完成教学:即对于ProtoBuf知识内容的展开,会对⼀个项⽬进⾏⼀个版本⼀个版本的升级去讲解ProtoBuf对应的知识点。

在后续的内容中,将会实现⼀个通讯录项⽬。对通讯录⼤家应该都不陌⽣,⼀般,通讯录中包含了⼀批的联系⼈,每个联系⼈⼜会有很多的属性,例如姓名、电话等等。

随着对通讯录项⽬的升级,我们对ProtoBuf的学习与使⽤就越深⼊

四、快速上⼿

在快速上⼿中,会编写第⼀版本的通讯录1.0。在通讯录1.0版本中,将实现:

  • 对⼀个联系⼈的信息使⽤PB进⾏序列化,并将结果打印出来。
  • 对序列化后的内容使⽤PB进⾏反序列,解析出联系⼈信息并打印出来。
  • 联系⼈包含以下信息:姓名、年龄。
    通过通讯录1.0,我们便能了解使⽤ProtoBuf初步要掌握的内容,以及体验到ProtoBuf的完整使⽤流程。

步骤1:创建.proto文件

⽂件规范

  • 创建.proto⽂件时,⽂件命名应该使⽤全⼩写字⺟命名,多个字⺟之间⽤ _ 连接。例如:
    lower_snake_case.proto
  • 书写.proto⽂件代码时,应使⽤2个空格的缩进。

我们为通讯录1.0新建⽂件: contacts.proto

添加注释

  • 向⽂件添加注释,可使⽤ // 或者 /* ... */

指定proto3语法

  • ProtocolBuffers语⾔版本3,简称proto3,是.proto⽂件最新的语法版本。proto3简化了ProtocolBuffers语⾔,既易于使⽤,⼜可以在更⼴泛的编程语⾔中使⽤。它允许你使⽤Java,C++,Python等多种语⾔⽣成protocolbuffer代码。
  • 在.proto⽂件中,要使⽤ syntax = "proto3"; 来指定⽂件语法为proto3,并且必须写在除去注释内容的第⼀⾏。如果没有指定,编译器会使⽤proto2语法。在通讯录1.0的contacts.proto⽂件中,可以为⽂件指定proto3语法,内容如下:
syntax = "proto3";

package声明符

  • package是⼀个可选的声明符,能表⽰.proto⽂件的命名空间,在项⽬中要有唯⼀性。它的作⽤是为了避免我们定义的消息出现冲突。
  • 在通讯录1.0的contacts.proto⽂件中,可以声明其命名空间,内容如下:
syntax = "proto3";
package contacts;

定义消息(message)

消息(message):要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。这⾥再提⼀下为什么要定义消息?

  • 在⽹络传输中,我们需要为传输双⽅定制协议。定制协议说⽩了就是定义结构体或者结构化数据,⽐如,tcp,udp报⽂就是结构化的。
  • 再⽐如将数据持久化存储到数据库时,会将⼀系列元数据统⼀⽤对象组织起来,再进⾏存储。

所以ProtoBuf就是以message的⽅式来⽀持我们定制协议字段,后期帮助我们形成类和⽅法来使⽤。在通讯录1.0中我们就需要为联系⼈定义⼀message。

.proto⽂件中定义⼀个消息类型的格式为:

message 消息类型名{

}

注意事项:`消息类型命名规范:使⽤驼峰命名法,⾸字⺟⼤写`

为contacts.proto(通讯录1.0)新增联系⼈message,内容如下:

syntax = "proto3";
package contacts;

// 定义联系人消息
message PeopleInfo{

}

定义消息字段

在message中我们可以定义其属性字段,字段定义格式为:字段类型字段名=字段唯⼀编号;

  • 字段名称命名规范:全⼩写字⺟,多个字⺟之间⽤_连接。
  • 字段类型分为:标量数据类型和特殊类型(包括枚举、其他消息类型等)。
  • 字段唯⼀编号:⽤来标识字段,⼀旦开始使⽤就不能够再改变。

该表格展⽰了定义于消息体中的标量数据类型,以及编译.proto⽂件之后⾃动⽣成的类中与之对应的字段类型。在这⾥展⽰了与C++语⾔对应的类型。

.proto TypeNotesC++ Type
doubledouble
floatfloat
int32使⽤变⻓编码[1]。负数的编码效率较低⸺——若字段可能为负值,应使⽤sint32代替。int32
int64使⽤变⻓编码[1]。负数的编码效率较低⸺——若字段可能为负值,应使⽤sint64代替。int64
uint32使⽤变⻓编码[1]。uint32
uint64使⽤变⻓编码[1]。uint64
sint32使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的int32类型。int32
sint64使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的int64类型。int64
fixed32定⻓4字节。若值常⼤于2^28则会⽐uint32更⾼效。uint32
fixed64定⻓8字节。若值常⼤于2^56则会⽐uint64更⾼效。uint64
sfixed32定⻓4字节。int32
sfixed64定⻓8字节。int64
boolbool
string包含UTF-8和ASCII编码的字符串,⻓度不能超过2^32。string
bytes可包含任意的字节序列但⻓度不能超过2^32。string

详细解释如上表格:

名字解释说明
使⽤变⻓编码[1]经过protobuf编码后,原本4字节或8字节的数可能会被变为其他字节数。

更新contacts.proto(通讯录1.0),新增姓名、年龄字段:

// 首行:语法指定行
syntax = "proto3";
package contacts;

// 定义联系人message
message PeopleInfo {
  string name = 1;  // 姓名
  int32 age = 2;    // 年龄  
}

在这⾥还要特别讲解⼀下字段唯⼀编号的范围:

1 ~ 536,870,911(2^29-1),其中 19000 ~ 19999不可⽤。

19000~19999不可⽤是因为:在Protobuf协议的实现中,对这些数进⾏了预留。如果⾮要在.proto⽂件中使⽤这些预留标识号,例如将name字段的编号设置为19000,编译时就会报警:

// 消息中定义了如下编号,代码会告警:
// Field numbers 19,000 through 19,999 are reserved for the protobuf implementation

string name = 19000;

值得⼀提的是,范围为1~15的字段编号需要⼀个字节进⾏编码,16 ~ 2047内的数字需要两个字节进⾏编码。编码后的字节不仅只包含了编号,还包含了字段类型。所以1~15要⽤来标记出现⾮常频繁的字段,要为将来有可能添加的、频繁出现的字段预留⼀些出来。

步骤2:编译contacts.proto⽂件,⽣成C++⽂件

编译命令
编译命令⾏格式为:

protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto
  • protoc :是 Protocol Buffer 提供的命令⾏编译⼯具。
  • --proto_path :指定 被编译的.proto⽂件所在⽬录,可多次指定。可简写成 -I IMPORT_PATH
  • 如不指定该参数,则在当前⽬录进⾏搜索。
  • 当某个.proto ⽂件 import 其他.proto ⽂件时,或需要编译的 .proto ⽂件不在当前⽬录下,这时就要⽤-I来指定搜索⽬录。 --cpp_out= 指编译后的⽂件为 C++ ⽂件。
  • OUT_DIR: 指编译后⽣成⽂件的⽬标路径。 path/to/file.proto 要编译的.proto⽂件

编译contacts.proto⽂件命令如下:

# 第一种方式,在当前contacts.proto文件目录下执行。
protoc --cpp_out=. contacts.proto
# 第二种方式,在contacts.proto文件父目录下执行。
protoc -I ./fast_start --cpp_out=./fast_start/ contacts.proto

编译contacts.proto⽂件后会⽣成什么

编译contacts.proto⽂件后,会⽣成所选择语⾔的代码,我们选择的是C++,所以编译后⽣成了两个⽂件: contacts.pb.h contacts.pb.cc

对于编译⽣成的C++代码,包含了以下内容:

  • 对于每个message,都会⽣成⼀个对应的消息类。
  • 在消息类中,编译器为每个字段提供了获取和设置⽅法,以及⼀下其他能够操作字段的⽅法。
  • 编辑器会针对于每个 .proto ⽂件⽣成 .h 和 .cc ⽂件,分别⽤来存放类的声明与类的实现。

contacts.pb.h部分代码展⽰

class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message {
public:
	using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
	void CopyFrom(const PeopleInfo& from);
	using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
	void MergeFrom( const PeopleInfo& from) {
	PeopleInfo::MergeImpl(*this, from);
	}
	static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
	return "PeopleInfo";
	}
	// string name = 1;
	void clear_name();
	const std::string& name() const;
	template <typename ArgT0 = const std::string&, typename... ArgT>
	void set_name(ArgT0&& arg0, ArgT... args);
	std::string* mutable_name();
	PROTOBUF_NODISCARD std::string* release_name();
	void set_allocated_name(std::string* name);
	// int32 age = 2;
	void clear_age();
	int32_t age() const;
	void set_age(int32_t value);
};

述的例⼦中:

  • 每个字段都有设置和获取的⽅法,getter的名称与⼩写字段完全相同,setter⽅法以set_开头。
  • 每个字段都有⼀个clear_⽅法,可以将字段重新设置回empty状态。

contacts.pb.cc中的代码就是对类声明⽅法的⼀些实现,在这⾥就不展开了。

到这⾥有同学可能就有疑惑了,那之前提到的序列化和反序列化⽅法在哪⾥呢?在消息类的⽗类MessageLite 中,提供了读写消息实例的⽅法,包括序列化⽅法和反序列化⽅法。

class MessageLite {
public:
	//序列化:
	bool SerializeToOstream(ostream* output) const; // 将序列化后数据写⼊⽂件流
	bool SerializeToArray(void *data, int size) const;
	bool SerializeToString(string* output) const;
	//反序列化:
	bool ParseFromIstream(istream* input); // 从流中读取数据,再进⾏反序列化动作
	bool ParseFromArray(const void* data, int size);
	bool ParseFromString(const string& data);
};

注意:

  • 序列化的结果为⼆进制字节序列,⽽⾮⽂本格式。
  • 以上三种序列化的⽅法没有本质上的区别,只是序列化后输出的格式不同,可供不同的应⽤场景使⽤。
  • 序列化的API函数均为const成员函数,因为序列化不会改变类对象的内容,⽽是将序列化的结果保存到函数⼊参指定的地址中。
  • 详细messageAPI可以参⻅完整列表。

步骤3:序列化与反序列化的使⽤

创建⼀个测试⽂件test.cc,⽅法中我们实现:

  • 对⼀个联系⼈的信息使⽤PB进⾏序列化,并将结果打印出来。
  • 对序列化后的内容使⽤PB进⾏反序列,解析出联系⼈信息并打印出来。

test.cc 文件

#include <iostream> 
#include "contacts.pb.h"
 

int main() { 
    std::string people_str; 

    {
        // 对⼀个联系⼈的信息使⽤ PB 进⾏序列化,并将结果打印出来。
        contacts::PeopleInfo people; 
        people.set_name("张珊"); 
        people.set_age(20); 
        if (!people.SerializeToString(&people_str)) { 
            std::cerr << "序列化联系⼈失败!" << std::endl; 
            return -1;
        }
        std::cout << "序列化成功,结果:" << people_str << std::endl; 
    }
    
    {
        // 对序列化后的内容使⽤ PB 进⾏反序列,解析出联系⼈信息并打印出来。
        contacts::PeopleInfo people; 
        if (!people.ParseFromString(people_str)) { 
            std::cerr << "反序列化联系⼈失败!" << std::endl; 
            return -1;
        } 
        std::cout << "反序列化成功!" << std::endl
                  << "姓名: " << people.name() << std::endl
                  << "年龄: " << people.age() << std::endl;
    }

    return 0;
} 

contacts.proto 文件

// 首行:语法指定行
syntax = "proto3";
package contacts;

// 定义联系人message
message PeopleInfo {
  string name = 1;  // 姓名
  int32 age = 2;    // 年龄  
}

2.执行一下语句


# 1. 编译 contacts文件 
g++ -o testPB test.cc contacts.pb.cc -lprotobuf -std=c++11
# 2. 编译 test.cc, 生成执行程序 testPB
protoc --cpp_out=. contacts.proto
  • -lprotobuf:依赖的库, 必加,不然会有链接错误。
  • std=c++11: 使用c++语法。

执⾏ TestPB ,可以看⻅people经过序列化和反序列化后的结果:

./testPB

# 打印如下说明成功了

[YYK@VM-8-7-centos fast_start]$ ./testPB
序列化成功,结果:
张珊
反序列化成功!
姓名: 张珊
年龄: 20

由于ProtoBuf是把联系⼈对象序列化成了⼆进制序列,这⾥⽤string来作为接收⼆进制序列的容器。
所以在终端打印的时候会有换⾏等⼀些乱码显⽰。
所以相对于xml和JSON来说,因为被编码成⼆进制,破解成本增⼤,ProtoBuf编码是相对安全的。

⼩结ProtoBuf使⽤流程

在这里插入图片描述

  • 1.编写.proto⽂件,⽬的是为了定义结构对象(message)及属性内容。
  • 2.使⽤protoc编译器编译.proto⽂件,⽣成⼀系列接⼝代码,存放在新⽣成头⽂件和源⽂件中。
  • 3.依赖⽣成的接⼝,将编译⽣成的头⽂件包含进我们的代码中,实现对.proto⽂件中定义的字段进⾏ 设置和获取,和对message对象进⾏序列化和反序列化。
  • 总的来说:ProtoBuf是需要依赖通过编译⽣成的头⽂件和源⽂件来使⽤的。有了这种代码⽣成机制,开发⼈员再也不⽤吭哧吭哧地编写那些协议解析的代码了(⼲这种活是典型的吃⼒不讨好)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2分钟快速上手工作流的创建与运行

前段时间&#xff0c;偶然发现一款低代码产品&#xff0c;工作流很突出&#xff0c;至少我觉得&#xff0c;设计颇有精妙之处。 可视化工作流是实现看板的第一步&#xff0c;要想业务人员也能了然其中的运行逻辑&#xff0c;这款产品可是做到了白痴也看得懂的程度&#xff01;…

讲个小故事

5月21日&#xff0c;沈梦辰在微博晒出了一组照片&#xff0c;记录下520这天杜海涛去机场接机的甜蜜瞬间。杜海涛不仅给沈梦辰送上兔子造型的红玫瑰花束&#xff0c;同时还准备了现切的新鲜西瓜。同一天&#xff0c;李湘女儿王诗龄也在网络晒出了自己的520礼物&#xff0c;那是一…

DOS命令(了解)

目录 一、 DOS 介绍 二、相关的知识补充: 相对路径&#xff0c; 绝对路径 ​三、常用的 dos 命令 1) 查看当前目录是有什么内容 dir 2) 切换到其他盘下&#xff1a;盘符号 cd : change directory 3) 切换到当前盘的其他目录下 (使用相对路径和绝对路径演示), ..\表示上一级…

网关Netfilx Zuul:---(Eureka高可用操作)

之前我们创建完成了3个Eureka的客户端的服务操作&#xff0c;你会发现我们还是没有能够通过微服来进行对他访问&#xff0c;还是必须通过自己服务的端口号来进行访问&#xff0c;那么我们的微服务是没有能够完成的&#xff0c;这个时候我们就需要通过网关进行操作 其实网关就是…

618大促聚焦“低价”与“规则简化”

618年中大促即将拉开帷幕。 多个电商平台已经公布今年618促销节的节奏与玩法&#xff0c;618开启之前&#xff0c;电商平台也纷纷表达了对于年中大促的重视&#xff0c;淘宝天猫618总负责人暮珊此前表示&#xff0c;“今年淘宝天猫618是历史上最大投入的一届”。抖音副总裁木青…

设计模式之【策略模式】,去掉繁琐的if-else,实现算法的动态替换

文章目录 一、什么是策略模式1、策略模式应用场景2、状态模式与策略模式的区别3、策略模式优缺点4、策略模式的三大角色 二、实例1、策略模式的一般写法2、促销活动案例3、网购订单支付案例4、DispatcherServlet的优化5、文件排序案例 三、源码中的策略模式1、Comparator接口2、…

在字节做了5年的软件测试,被辞了,太真实了...

先简单说下&#xff0c;涵哥是某不知名 985 的本硕&#xff0c;17 年毕业加入字节&#xff0c;以“人员优化”的名义无情被裁员&#xff0c;之后跳槽到了有赞&#xff0c;一直从事软件测试的工作。还差一个月也6年了吧&#xff0c;算是在这行的资深划水员。6年的时间也让涵哥从…

ChatGPT 辅助生成PPT

前言 介绍 ChatGPT 与 MindShow 结合高效生成 PPT。 文章目录 前言一、准备工具二、使用步骤1. 内容生成2. 制作 PPT三、小节一、准备工具 ChatGPT:MindShow:MindShow网站 MindShow 内置了丰富的模板、图表和设计元素。具有自动排版功能,可根据输入内容智能调整布局。二、使…

29岁测试被辞,面试2个月还找不到工作....

最近一个29岁老同学联系我&#xff0c;因为被公司辞退&#xff0c;找我倾诉&#xff0c;于是写下此文。 他是14年二本毕业&#xff0c;在我的印象里人特别懒&#xff0c;不爱学习&#xff0c;专业不好&#xff0c;毕业前因为都没找到合适工作&#xff0c;直接去创业了&#xf…

导入报错:Limits: MIN_INFLATE_RATIO: 0.010000, Entry: xl/drawings/drawing1.xml

今天突然遇到一个现场提出的问题&#xff1a;说导入文件导入不成功&#xff0c;咋突然有这个问题 2023-05-23 14:19:11,174 ERROR [http-nio-9104-exec-5] o.a.c.c.C.[.[.[.[dispatcherServlet] [DirectJDKLog.java : 175] Servlet.service() for servlet [dispatcherServlet]…

VMwareESXI虚拟机黑群晖7.2 正式版 (懒人包)

版本说明&#xff1a; VMware Workstation 桌面版虚拟机&#xff0c;可下载VMware专用版本 VMware ESXi虚拟机&#xff0c;可以下载OVA版本 VMware Workstation桌面版虚拟机 使用教程&#xff1a; 1.下载VMware专用版本&#xff0c;然后进行解压&#xff0c;双击解压出来的&q…

前端Vue:权限管理,给角色分配权限

&#x1f449;前端-Vue权限控制&#xff0c;菜单权限&#xff0c;按钮权限_一人创客的博客-CSDN博客 目录 介绍&#xff1a; 前端权限的概念&#xff1a; 前端权限的意义&#xff1a; Vue权限管理的代码实现&#xff1a; 菜单 刷新界⾯菜单消失 标识⽤户名, ⽅便查看当前…

chatgpt赋能Python-python_kali

Python与Kali Linux&#xff1a;SEO攻击的更有效方法 介绍 Python是一种强大且高度灵活的编程语言&#xff0c;也是许多安全专家、黑客和网络攻击者所喜欢的工具之一。Kali Linux是一个专注于安全审计、渗透测试和网络安全的Linux发行版&#xff0c;其中包含了许多流行的安全…

微服务开发系列 第四篇:分页查询

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

Chat GPT 教您如何发现和处理无效数据

Chat GPT 教您如何发现和处理无效数据 在进行数据管理时&#xff0c;无论是数据分析、数据挖掘还是机器学习项目&#xff0c;无效数据都可能对结果造成严重的影响。因此&#xff0c;发现和处理无效数据变得至关重要。本文将从如何处理无效数据的角度&#xff0c;详细探讨数据清…

数据结构与算法(五)

哈希表&#xff08;hash&#xff09; 什么是hash&#xff1f; 散列,是把任意长度的输入通过散列算法变换成固定长度输出&#xff0c;该输出的值就是散列值。这种转换是一种压缩映射。映射表达的是一一对应的关系&#xff0c;也就是说&#xff0c;散列值的空间通常会小于输入空…

[算法前沿]--014-DeepSpeed-Chat 模型训练实战<下>

文章目录 1.实战Step1:监督微调1.1 任务说明: 使用标定的数据对预训练模型进行微调评价与测试:2 实战Step2:Reward模型微调3.实战Step3:RLHF训练3.评价与测试4.QA参考1.实战Step1:监督微调 基础语言模型是指只在大规模文本语料中进行了预训练的模型,未经过指令和下游任务…

淘宝商品历史价格API接口 调用说明及功能介绍

淘宝商品历史价格API是一款可以帮助用户获取淘宝商品历史价格数据的接口。通过该接口&#xff0c;用户可以轻松地获取某个商品在过去一段时间中的价格趋势和波动情况&#xff0c;以便更好地了解该商品的市场走势和价值变化情况。 该API具备以下功能&#xff1a; 1. 支持多种查…

对于大流量请求的处理方案(NATNginx)

情况描述&#xff1a; 如图所示&#xff0c;厂家的A服务器&#xff0c;到客户的C服务器不通&#xff0c;需要我这边通过B服务器做一次流量转发。 由于&#xff0c;每次请求数据流都太大&#xff0c;怕HTTPS方式&#xff0c;会出现请求超时&#xff0c;断开连接。 解决方案&am…

什么是自动化测试框架?我们该如何搭建自动化测试框架?

无论是在自动化测试实践&#xff0c;还是日常交流中&#xff0c;经常听到一个词&#xff1a;框架。之前学习自动化测试的过程中&#xff0c;一直对“框架”这个词知其然不知其所以然。 最近看了很多自动化相关的资料&#xff0c;加上自己的一些实践&#xff0c;算是对“框架”…