kizumi_header_banner_img

Learn. Build. Share.

加载中

文章导读

基于脚手架微服务的视频点播系统-数据管理与网络通信部分的预备工作


avatar
xiu114514 2025年9月14日 19
基于脚手架微服务的视频点播系统-数据管理与网络通信部分的预备工作

@TOC

一.数据管理

在前⾯的实现中,程序中的数据、以及界⾯操作等全部搅合在⼀起,不利于代码的维护,为了降低耦合度,引⼊DataCenter类来专门管理程序中的各种数据,比如:分类和标签、视频信息、⽤⼾信息等。 在此之前,我们要先处理下主窗口的close,需要在主窗口关闭时统一处理所有单例模式的实例指针释放(将原来主界面上的quit绑定的槽函数更换为以下函数):

为什么我们不采用智能指针或静态局部变量来实现单例模式呢?那样不是更简洁方便吗?实际上,博主也曾尝试过这类方法,但在程序退出时却频繁引发段错误(Segmentation Fault)。其根本原因在于,这两种方式在程序终止阶段的对象释放顺序是无法保证的——很有可能在主程序或某些核心资源已经被销毁之后,这些单例对象才被析构。而此时如果单例对象在析构过程中试图访问已释放的资源(例如位于已卸载内存中的静态数据或 Qt 系统资源),就会导致非法的内存访问,从而触发段错误。 特别是在 Qt 环境中,许多模块(如 GUI 组件、网络层或数据库连接)高度依赖于 Qt 自身的清理机制和对象生命周期管理。若单例对象的析构顺序与 Qt 内部资源的释放顺序不一致,就极易发生访问已失效 QObject 或其他资源的情况,造成不可预知的崩溃。因此,在 Qt 这类框架中,通常建议使用显式控制的单例生命周期管理机制(如手动销毁或结合 QObject 父子关系),或采用具有明确析构顺序的上下文环境,以避免退出时的资源冲突问题。

我们先新创建一个dataCenter类,然后将其写为单例模式,并写明该单例类的delete函数,以便让主程序退出时释放数据中心的单例指针(注意为了区分模块,这里我们将这个新建的类放到工程目录的model文件夹下,同时我们的类也放到名为model的命名空间中,避免引起冲突):

接下来我们创建一个名为data的.h与.cpp文件,今后所有的数据都将存放到data中。因为现在还无法进行网络通信,所以我们先对已有数据进行管理-分类与标签:

然后在datacenter中添加上对应的成员指针:

细心的读者可能注意到了,我们上面在设置分类标签数据时,还给每一个数据都加上了一个id,这是一种常见的作法。那么这里就有一个问题-为什么客户端向服务端索取数据时,通常递交与服务端的是数据对应的id而不直接是数据名称呢? 博主查询网络总结了以下四点:

  1. 效率与性能 (Efficiency & Performance) 数据量小:一个整型或UUID类型的ID(例如 12345)所占的存储空间和网络传输带宽,远小于一个可能很长的字符串名称(例如 “iPhone 15 Pro Max 1TB 钛金属原色”)。在高并发、海量请求的场景下,这能极大地节省网络带宽、降低I/O压力、加快序列化/反序列化速度。 索引优化:在数据库(如MySQL)中,对数字型的ID字段建立索引并进行查询,其速度远快于对字符串类型的名称字段进行查询。数字比较比字符串比较要快得多,并且数字索引通常更紧凑。直接查询 WHERE id = 12345 的效率远远高于 WHERE product_name = 'iPhone 15 Pro Max…'。

  2. 一致性与唯一性 (Consistency & Uniqueness) 名称可变:名称是可以修改的。比如一本书的书名、一个用户的昵称、一个产品的标题。如果客户端靠名称“《深入理解C++》”来请求数据,而当这本书改名成“《C++ Primer Plus》”后,所有用旧名称发起的请求都会失败。而ID(如 ISBN: 9787115580xxx)是唯一且不变的标识符,它指向一个唯一的实体,与这个实体的属性(如名称)如何变化无关。 名称不唯一:名称可能存在重复。世界上可能有成千上万个叫“张三”的用户,或者多个同名的产品。但他们的ID(如用户ID、产品SKU)在系统内是唯一的。使用ID可以精确地定位到唯一实体,避免了歧义。

  3. 解耦与维护 (Decoupling & Maintainability) 隔离变化:这个设计遵循了“解耦”的原则。客户端只需要知道一个稳定不变的标识符(ID),而不需要关心服务端内部的数据结构。服务端可以自由地修改实体的名称、描述等其他信息,甚至重构底层数据库,只要ID保持不变,客户端就完全不受影响。如果客户端直接传递名称,服务端任何对名称的修改都会导致客户端逻辑崩溃。 安全性:有时,名称本身可能是敏感的,或者你不希望暴露内部的命名规则。直接暴露ID(尤其是非自增的、无规律的UUID)相比暴露一个具有语义的名称,通常更安全一些。

  4. 国际化 (Internationalization) 如果服务端需要支持多语言,一个产品可能对应多个名称(中文名、英文名等)。客户端很难知道应该传递哪个语言的名字来请求数据。而ID是语言无关的,客户端传递ID,服务端可以根据客户端的语言设置或请求头信息,返回对应语言的名称和其他数据。

那么接下来我们使用datacenter中的数据去替换我们之前的首页分类标签与填充上传视频界面的分类与标签即可,首页替换很简单这里我们就不再给出了,我们主要来看下上传视频界面的数据导入:

然后我们就能在视频上传界面中看到如下效果: 在这里插入图片描述

二.网络通信

2.1客户端通信模块及测试用例的实现

像数据中心的实现一样,我们这里将新建的netclient类文件放到工程目录的netclient文件夹下,而类也放到命名空间netclient中。 我们这里使用json进行序列化和反序列化,之前我们也使用过,所以我们再来回顾下相关接口: 1.QJsonObject:封装了⼀个JSON对象,即键值对的集合。 2.QJsonArray:封装了⼀个JSON数组,可以存储⼀系列的QJsonValue对象,即值的有序集合。3.QJsonValue:封装了⼀个JSON值,QJsonObject中key是字符串,value是QJsonValue对象。 4.QJsonDocument:⽤于解析和⽣产JSON⽂本,即对QJsonObject完成序列化和反序列化。 为了获取数据方便,这里我们使用http协议进行服务端与客户端的通信,客户端要想使用相关通信模块,需要在cmake中添加以下脚本:

对于http协议这里我们不再过多介绍,需要了解熟悉http协议的读者可以移步我之前写的关于http和https的博文: HTTP协议解析:Session/Cookie机制与HTTPS加密体系的技术演进(一) 当然qt中已经对相关接口进行了高度封装,我们直接拿来使用即可: QNetworkAccessManager是Qt网络模块中的⼀个核心类,用于发送请求、处理响应、管理会话等,使得开发者可以方便地发送 HTTP 请求、处理响应等。

注意因为网络传输是有延迟的,QNetworkAccessManager进行post或get之后是异步的直接返回,不会阻塞等待响应,响应到达时以finished信号通知,所以我们需要在QNetworkReply的finished信号进行接收服务端的response相关操作。 QNetworkRequest是Qt网络模块中的⼀个核心类,用于封装网络请求的所有关键信息,包括请求的 URL、HTTP 头部信息、请求体等。

利⽤ QNetworkRequest 可以完成请求的设置,之后就可以将请求适合的方式发送给服务器。 了解了以上接口,我们就可以开始进行实现了。不过在确定具体实现方案之前,我们还需要深入思考一个问题:应以何种方式组织模块间的交互。在实际运行过程中,用户界面上的多数操作都需要与底层通信模块进行数据交换。基于以往的经验,通常有两种实现思路:其一是在每一个需要网络请求的界面类中直接内嵌一个通信对象——这种做法显然不可取,不仅会导致代码冗余,更会引入高度的重复依赖。另一种方案是将通信模块设计为单例,虽然这种方式在一定程度上简化了调用,但却会使界面与通信实现紧密耦合,违背关注点分离的原则,从而严重制约后期的维护与扩展性。 在计算机领域有一个广为人知的理念:当一个复杂问题难以直接解决时,就为它引入一个中间层。如果一层不够,就再添加一层。而在我们的架构中,甚至无需额外引入——DataCenter 本身就是一个天然、完善的中间层。 在这里插入图片描述 此外,我们还需要解决另一个关键问题:如何有效区分不同的请求。解决方案其实非常直接——我们可以在每个 HTTP 请求的 Body 部分携带一个具有唯一性的标识字段,这里我们称其为requestId。通过生成 UUID(通用唯一识别码)来确保该值的全局唯一性,即可可靠地实现请求的追踪与匹配。 所以基于上面的接口的了解与实现方案的分析,我们可以得到如下的实现方式:

2.2MockServer搭建的相关接口介绍

现在客户端通信模块搭建好了,但是我们怎么去测验它是否有问题呢,这里我们就需要搭建一个假的服务器。MockServer是能够提供Mock功能的服务器,通过模拟真实的服务器,提供对来⾃客⼾端请求的真实响应。它允许用户创建模拟HTTP服务以模拟后端服务的响应,从而在开发和测试过程中,无需依赖实际的后端服务,也能够进行接口测试和功能验证。实际就是搭建⼀个HTTP服务器,构造模拟数据对客户端接口进行测试。 这里我们新建一个项目,注意是项目,名为MockServer,基于QWidget和cmake。然后我们如果想要使用qt已经封装好的httpServer模块,需要在CMakeLists.txt中添加如下脚本:

当然如果已经了解甚至熟悉了http协议,实现我们的假的服务器就很简单了,只需要了解几个qt提供的接口即可:

2.3MockServer的搭建示例

接下来我们在客户端的main.cpp函数中调用hello与ping模块运行服务端与客户端即可看到如下效果: 在这里插入图片描述 在这里插入图片描述 ok到这里我们的预备工作已经准备完毕,下一篇文章我们再来实现实际的业务逻辑。

 

 



评论(已关闭)

评论已关闭

日历

2025 年 9 月
 1
2345678
9101112131415
16171819202122
23242526272829
3031