C++基于protobuf实现仿RabbitMQ消息队列---技术认识2

前言:

项目git链接mq/mqdemo/muduo/protobuf/protobuf_client.cpp · 耀空/项目mq - 码云 - 开源中国

使用技术简介:

开发主语言:C++

序列化框架:Protobuf 二进制序列化

网络通信:自定义应用层协议 + muduo 库(对 tcp 长连接的封装、并且使用 epoll 的事件驱动模式,实现高并发服务器与客户端)

源数据信息数据库: SQLite3

单元测试框架: Gtest

  • 这里主要介绍后三个技术使用

SQlite3

基本上认识接口是参数代表什么,就可以简单是使用了

就简单介绍一下,然后直接封装一个SqliteHelper类,快速上手


SQLite3 官方文档:https://www.sqlite.org/c3ref/funclist.html

API介绍

线程安全等级:

查看当前数据库在编译阶段是否启动了线程安全 int sqlite3_threadsafe();

0-未启用; 1-启用

需要注意的是 sqlite3 是有三种安全等级的:

  1. 非线程安全模式
  2. 线程安全模式(不同的连接在不同的线程/进程间是安全的,即一个 句柄不能用于多线程间)
  3. 串行化模式(可以在不同的线程/进程间使用同一个句柄)

若在编译阶段启动了线程安全,则在程序运行阶段可以通过参数选择线程安全等级

创建/打开数据库文件,并返回操作句柄

int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );

flag:

  • SQLITE_OPEN_READWRITE -- 以可读可写方式打开数据库文件
  • SQLITE_OPEN_CREATE -- 不存在数据库文件则创建
  • SQLITE_OPEN_NOMUTEX--多线程模式,只要不同的线程使用不同的 连接即可保证线程安全
  • SQLITE_OPEN_FULLMUTEX--串行化模式 返回:SQLITE_OK 表示成功

执行语句

int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err) int (*callback)(void*,int,char**,char**)

  • void* : 是设置的在回调时传入的 arg 参数
  • int:一行中数据的列数 char**:存储一行数据的字符指针数组
  • char**:每一列的字段名称 这个回调函数有个 int 返回值,成功处理的情况下必须返回 0,返回非 0 会触发 ABORT 退出程序
  • 返回:SQLITE_OK 表示成功

销毁句柄

int sqlite3_close(sqlite3* db); 成功返回 SQLITE_OK

int sqlite3_close_v2(sqlite3*);推荐使用--无论如何都会返回 SQLITE_OK

获取错误信息

const char *sqlite3_errmsg(sqlite3* db);


API的使用

将这几个接将这几个接口封装成一个类口封装成一个类,快速上手
具体思路如下:


代码见:

封装sqlite3为一个类https://gitee.com/yaokong123/project-mq/tree/master/mq/mqdemo/sqlite3


如何打开文件呢

数据库 的查询指令




future 异步操作的结果

介绍

std::future 是 C++11 标准库中的一个模板类,它表示一个异步操作的结果。当我们在多线程编程中使用异步任务时,std::future 可以帮助我们在需要的时候获取任务的执行结 果。std::future 的一个重要特性是能够阻塞当前线程,直到异步操作完成,从而确保我们在获取结果时不会遇到未完成的操作。

应用场景(了解就行)

  • 异步任务: 当我们需要在后台执行一些耗时操作时,如网络请求或计算密集型任 务等,std::future 可以用来表示这些异步任务的结果。通过将任务与主线程分离,我们 可以实现任务的并行处理,从而提高程序的执行效率
  • 并发控制: 在多线程编程中,我们可能需要等待某些任务完成后才能继续执行其 他操作。通过使用 std::future,我们可以实现线程之间的同步,确保任务完成后再获取 结果并继续执行后续操作。
  • 结果获取:std::future 提供了一种安全的方式来获取异步任务的结果。我们可以使 用 std::future::get()函数来获取任务的结果,此函数会阻塞当前线程,直到异步操作完 成。这样,在调用 get()函数时,我们可以确保已经获取到了所需的结果

async

std::async 关联异步任务

std::future 可以帮助我们在需要的时候获取任务的执行 结果。

std::future 的一个重要特性是能够阻塞当前线程,直到异步操作完成,从而确保 我们在获取结果时不会遇到未完成的操作。

要注意的是两个参数

默认情况下,std::async 是否启动一个新线程,或者在等待 future 时,任务是否同步运行都取决于你给的参数

  • std::launch::deferred 在执行get获取异步结果的时候,才会执行异步任务
  • std::launch::async 内部会创建工作线程,异步的完成任务(创建async就开始执行)

std::future<int> result = std::async(std::launch::async, Add, 1, 22);

std::future<int> result = std::async(std::launch::deferred, Add, 1, 22);

还有一个不经常用的:std::launch::deferred | std::launch::async 内部通过系统等条件自动选择策略


packeage_task

std::packaged_task 就是将任务和 std::future 绑定在一起的模板,是一种对任务的封装。

  • pakcaged_task 是一个模板类,实例化的对象可以对一个函数进行二次封装,
  • pakcaged_task可以通过get_future获取一个future对象,来获取封装的这个函数的异步执行结果

可以把 std::future 和 std::async 看成是分开的, 而 std::packaged_task 则是一个整体。


  • 只说作用是无法理解的,大家可以看完测试代码,就能完全理解怎么用的了

代码

mq/mqdemo/future/package_task.cc · 耀空/项目mq - 码云 - 开源中国https://gitee.com/yaokong123/project-mq/blob/master/mq/mqdemo/future/package_task.cc


解释一下,std::packaged_task到底是什么?

std::packaged_task<int(int,int)> task(Add);

std::future<int> fu = task.get_future();

task(11, 22); task可以当作一个可调用对象来调用执行任务

  • 但是它又不能完全的当作一个函数来使用,因为不能直接调用

std::async(std::launch::async, task, 11, 22);

std::thread thr(task, 11, 22);

  • 但是我们可以把task定义成为一个指针,传递到线程中,然后进行解引用执行
  • 但是如果单纯指针指向一个对象,存在生命周期的问题,很有可能出现风险
  • 思想就是在堆上new对象,用智能指针管理它的生命周期

不能完全单座函数,也不能单纯单做一个指针,则可以认为,packaged task是在堆上new的一个对象;有智能指针管理其生命周期;


promise

  • 网上的定义:std::promise 提供了一种设置值的方式,它可以在设置之后通过相关联的 std::future 对象进行读取。(std::future<int> fu = prom.get_future();)
  • 换种说法就是之前说过 std::future 可以读取一个异步函数的返回值了, 但是要等待就绪, 而 std::promise 就提供一种方式手动让 std::future 就绪
  • 简单的来说,就是通过在线程中对promise对象设置数据,其他线程中通过promise得到的future获取设置数据的方式,实现获取异步任务执行结果的功能
  • 只说作用是无法理解的,大家可以看完测试代码,就能完全理解怎么用的了

代码

mq/mqdemo/future/promise.cc · 耀空/项目mq - 码云 - 开源中国https://gitee.com/yaokong123/project-mq/blob/master/mq/mqdemo/future/promise.cc

GTest

  • GTest 是一个跨平台的 C++单元测试框架,由 google 公司发布。gtest 是为了在不同 平台上为编写 C++单元测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化等等

GTest 使用

gtest的简单使用

使用就两点,TEST 宏和断言;

TEST宏

cpp 复制代码
TEST(test_case_name, test_name) 
TEST_F(test_fixture,test_name) 
  • TEST:主要用来创建一个简单测试, 它定义了一个测试函数, 在这个函数中可 以使用任何 C++代码并且使用框架提供的断言进行检查

  • TEST_F:主要用来进行多样测试,适用于多个测试场景如果需要相同的数据配置 的情况, 即相同的数据测不同的行为
    断言

  • ASSERT_系列:如果当前点检测失败则退出当前函数

  • EXPECT_系列:如果当前点检测失败则继续往下执行

简单介绍:(具体使用其实也很简单,找下面代码用例,看一下例子,就知道怎么用了

cpp 复制代码
// bool 值检查 
ASSERT_TRUE(参数),期待结果是 true 
ASSERT_FALSE(参数),期待结果是 false 
 
//数值型数据检查 
ASSERT_EQ(参数 1,参数 2),传入的是需要比较的两个数 equal 
ASSERT_NE(参数 1,参数 2),not equal,不等于才返回 true 
ASSERT_LT(参数 1,参数 2),less than,小于才返回 true 
ASSERT_GT(参数 1,参数 2),greater than,大于才返回 true 
ASSERT_LE(参数 1,参数 2),less equal,小于等于才返回 true 
ASSERT_GE(参数 1,参数 2),greater equal,大于等于才返回 true

事件机制

这给项目主要就用到了,事件机制

GTest 提供了三种常见的的事件:

  • **全局事件:**针对整个测试程序。实现全局的事件机制,需要创建一个自己的类, 然后继承 testing::Environment 类,然后分别实现成员函数 SetUp 和 TearDown,同时 在 main 函数内进行调用 testing::AddGlobalTestEnvironment(new MyEnvironment); 函数添加全局的事件机制
  • **TestSuite 事件:**针对一个个测试套件。测试套件的事件机制我们同样需要去创建 一个类,继承自 testing::Test,实现两个静态函数 SetUpTestCase 和 TearDownTestCase,测试套件的事件机制不需要像全局事件机制一样在 main 注册, 而是需要将我们平时使用的 TEST 宏改为 TEST_F 宏。
  • **TestCase 事件:**针对一个个测试用例。测试用例的事件机制的创建和测试套件的 基本一样,不同地方在于测试用例实现的两个函数分别是 SetUp 和 TearDown, 这两个 函数也不是静态函数

TestSuite 事件:

○ SetUpTestCase() 函数是在测试套件第一个测试用例开始前执行

○ TearDownTestCase() 函数是在测试套件最后一个测试用例结束后执行

○ 需要注意 TEST_F 的第一个参数是我们创建的类名,也就是当前测试套件的 名称,这样在 TEST_F 宏的测试套件中就可以访问类中的成员了。
TestCase 事件:

○ SetUp()函数是在一个测试用例的开始前执行

○ TearDown()函数是在一个测试用例的结束后执行

一般来说,suite事件和case事件 是可以同时使用的,因为它们测试用例的事件机制的创建和测试套件的 基本一样,主要就差在函数的调用时机不同

结合 下面TestCase 事件和TestSuite 事件代码看,就能完全理解了;

SetUpTestCase() // 所有测试前只执行一次

SetUp() // 测试用例1前执行

insert_test // 测试用例1

TearDown() // 测试用例1后执行

SetUp() // 测试用例2前执行

size_test // 测试用例2

TearDown() // 测试用例2后执行

TearDownTestCase() // 所有测试后只执行一次


这其实没什么说的,就是测试用例,结合测试代码,就十分容易理解

代码用例:

断言代码:

mq/mqdemo/gtest/assert.cc · 耀空/项目mq - 码云 - 开源中国https://gitee.com/yaokong123/project-mq/blob/master/mq/mqdemo/gtest/assert.cc

全局事件
mq/mqdemo/gtest/global.cc · 耀空/项目mq - 码云 - 开源中国https://gitee.com/yaokong123/project-mq/blob/master/mq/mqdemo/gtest/global.cc

TestCase 事件和TestSuite 事件
mq/mqdemo/gtest/suit.cc · 耀空/项目mq - 码云 - 开源中国https://gitee.com/yaokong123/project-mq/blob/master/mq/mqdemo/gtest/suit.cc

相关推荐
Chen--Xing6 小时前
LeetCode 11.盛最多水的容器
c++·python·算法·leetcode·rust·双指针
DeltaTime6 小时前
二 线性变换, 齐次坐标, 变换组合, 变换分解, 3D变换
c++·3d·图形渲染
XiaoHu02076 小时前
C++的IO流
开发语言·c++
ULTRA??6 小时前
排序算法之快排与TIMSORT的比较测试,python
c++·python·算法·golang
小妖同学学AI6 小时前
告别SQL编写!开源WrenAI实现自然语言与数据库的智能对话
数据库·sql·开源
开始了码7 小时前
UDP 协议详解与 Qt 实战应用
qt·网络协议·udp
胡萝卜3.07 小时前
构建安全的C++内存管理体系:从RAII到智能指针的完整解决方案
运维·开发语言·c++·人工智能·安全·智能指针·raii
拾光Ծ7 小时前
【优选算法】双指针算法:专题一
数据结构·c++·算法
MSTcheng.7 小时前
【C++】如何快速实现一棵支持key或key-value的二叉搜索树?关键技巧一文掌握!
开发语言·c++·算法·二叉搜索树