当前位置: 首页 > news >正文

【云备份】服务端数据管理模块设计与实现

目录

一、要管理的数据

二、如何管理数据

三.数据管理类的设计

3.1.数据信息结构体的设计

3.2.数据管理类的设计

四.数据管理类实现

4.1.数据信息结构体的数据填充

4.2.数据管理类的实现

五. 源代码+测试 


 

数据管理模块:需要管理的数据有哪些,而我们管理数据是因为后期会用到这些数据。

一、要管理的数据

  1. 文件实际存储路径
    • 描述:当客户端要下载文件时,则从这个路径中读取数据进行响应。
  2. 文件是否被压缩标志
    • 描述:用于判断文件是否已经被压缩。
  3. 文件压缩包存储路径名
    • 描述:如果一个文件是非热点文件,则会被压缩,此路径即为压缩包存储的位置。
    • 如果客户端要下载文件,则需要先解压缩,然后读取解压后的文件数据。
  4. 文件属性信息
    • 文件大小:记录文件的大小。
    • 文件最后一次修改时间:记录文件最后一次被修改的时间。
    • 文件最后一次访问时间:记录文件最后一次被访问的时间。
    • 文件访问 URL 中资源路径(path):记录文件在访问 URL 中的资源路径。比如说/download/a.txt,这个就类似于下载链接,必须得管理好

二、如何管理数据

  • 2.1.用于数据信息访问

方法:使用哈希表(hash 表)在内存中管理数据。

哈希表的键(key):文件访问 URL。

哈希表的值(val):数据信息结构。

优点:访问速度快。因为哈希表的查询时间复杂度都在O(1)。

以文件访问 URL 作为 key,是因为客户端浏览器下载文件时总是以 URL 作为请求。

  • 2.2.用于持久化存储管理

方法:使用 JSON 序列化将所有数据保存到文件中。

三.数据管理类的设计

3.1.数据信息结构体的设计

数据管理类:管理服务端系统中会用到的数据。

数据信息结构体:struct BackupInfo结构中包含要管理的文件各项属性信息;

typedef struct BackupInfo
{bool pack_flag; // 文件是否被压缩标志size_t fsize; // 文件大小time_t mtime; // 文件最后一次修改时间time_t atime; // 文件最后一次访问时间std::string real_path; // 文件实际存储路径std::string pack_path; // 压缩包存储路径std::string url; // 文件访问URL
}BackupInfo;

3.2.数据管理类的设计

数据管理类负责将数据信息管理起来。

数据化管理DataManger中包含以下成员:

class DataManager{public:DataManager();~DataManager();//持久化存储的接口bool Storage(); // 每次数据更新或者新增都要持久化存储,避免数据丢失bool InitLoad(); // 初始化加载,在每次系统启动前都要加载以前的数据bool Insert(const BackupInfo &info); // 新增bool Updata(const BackupInfo &info); // 更新//数据信息访问的接口// 通过url获取文件信息,所有的文件信息都存放到info里面去bool GetOneByURL(const std::string &url, BackupInfo* info); // 通过实际路径获取文件信息,这些文件信息都存放到info里面去bool GetOneByRealpath(const std::string &realpath, BackupInfo* info); // 获取所有文件信息bool GetAll(std::vector<BackupInfo>* array); private:std::string _backup_file; // 备份信息持久化存储,从配置文件里面获取pthread_rwlock_t _rwlock; //读写锁,读写锁是读共享,写互斥,多个线程可以同时读取数据,但不能同时写// 因为我们是使用多线程的,所以需要注意线程安全std::unordered_map<std::string, BackupInfo> _table // 哈希表数据信息组织,key是文件访问url,值是数据信息结构;};

四.数据管理类实现

4.1.数据信息结构体的数据填充

我们创建一个data.hpp文件,用来数据管理。

首先我们首先需要对 struct BackupInfo结构进行填充,这个需要一个构造函数 NewBackUpInfo。

 NewBackUpInfo负责将传递进来文件各项属性信息组织起来;

本质就是根据配置文件的内容+本文件的属性来更新BackupInfo结构中这些属性。

data.hpp

#ifndef _MY_DATA_
#define _MY_DATA_
#include"util.hpp"
#include<unordered_map>
#include<pthread.h>
#include"conf.hpp"namespace cloud{typedef struct BackupInfo{bool pack_flag; // 文件是否被压缩标志size_t fsize; // 文件大小time_t mtime; // 文件最后一次修改时间time_t atime; // 文件最后一次访问时间std::string real_path; // 文件实际存储路径std::string pack_path; // 压缩包存储路径std::string url; // 文件访问URLbool NewBackupInfo(const std::string &realpath)//填充上面这些数据{//当前文件的信息的获取FileUtil fu(realpath);//文件管理类,方便我们管理文件,获取文件属性if(fu.Exists() == false)//当前文件不存在{std::cout << "new backupinfo: file not exit" << std::endl;return false;}//配置文件信息的获取Config* config = Config::GetInstance();//构造我们的配置文件对象std::string packdir = config->GetPackDir();//获取配置文件里的压缩路径std::string packsuffix = config->GetPackFileSuffix();//获取配置文件里面的压缩文件的后缀名std::string download_prefix = config->GetDownloadPrefix();//获取配置文件里面的下载的url前缀pack_flag = false;//文件没有被压缩fsize = fu.FileSize();mtime = fu.LastModtime();atime = fu.LastAcctime();real_path = realpath;//我们传入的参数就是//注意我们的文件被压缩时文件路径会从./backdir/a.txt变成./packdir/a.txt.lz,这需要压缩路径,文件名,压缩后缀两个信息,这些都在配置文件里面pack_path = packdir + fu.FileName() + packsuffix;//我们这个url是从./backdir/a.txt变成./download/a.txt,这就需要下载的url前缀,文件名,这些都在配置文件里面url = download_prefix + fu.FileName();return true;}}BackupInfo;
};
#endif

为了检测我们这个代码写的对不对,我们需要来测试一下

cloud.cc

#include"util.hpp"
#include"conf.hpp"
#include"data.hpp"void DataTest(const std::string & filename)
{cloud::BackupInfo info;info.NewBackupInfo(filename);std::cout<<info.pack_flag<<std::endl;std::cout<<info.fsize<<std::endl;std::cout<<info.mtime<<std::endl;std::cout<<info.atime<<std::endl;std::cout<<info.real_path<<std::endl;std::cout<<info.pack_path<<std::endl;std::cout<<info.url<<std::endl;
}
int main(int argc,char*argv[])
{DataTest(argv[1]);return 0;
}

我们编译一下,由于我们编译的时候这个bundle头文件啥的太慢了,而且还老是报警,这很不舒服,所以我们将bundle.cpp生成一个静态库好了

g++ -c bundle.cpp -o bundle.o
ar -cr libbundle.a bundle.o
rm -rf bundle.cpp bundle.o

接着修改我们的makefile

cloud:cloud.cc util.hpp g++ $^ -o $@ -L./lib -lpthread -lstdc++fs -ljsoncpp -lbundle
.PHONY:clean
clean:rm -f cloud

 这样子就好很多了,编译的很快

 

都没有问题啊!!


这个时候我们git一下

4.2.数据管理类的实现

  • 新增,修改,获取文件属性的接口

我们啥也不多说,直接看代码好吧!!!

class DataManager{public:DataManager(){_backup_file = Config::GetInstance()->GetBackupFile(); // 获取备份信息持久化存储的文件,也就是获取配置文件里面的内容pthread_rwlock_init(&_rwlock, NULL);				   // 初始化读写锁}~DataManager(){pthread_rwlock_destroy(&_rwlock); // 释放读写锁}bool Insert(const BackupInfo &info)//新增{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//key值是url,值是BackupInfo结构pthread_rwlock_unlock(&_rwlock);return true;}bool Updata(const BackupInfo &info)//修改,对于目前来说和新增没有任何区别{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//当key值相同时,会覆盖之前的文件pthread_rwlock_unlock(&_rwlock);return true;}//数据信息访问的接口// 通过url获取文件信息,所有的文件信息都存放到info里面去//现在传进去的url是哈希表的key值bool GetOneByURL(const std::string &url, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock);//要访问哈希表,必须加锁auto it = _table.find(url);//在哈希表里面寻找匹配的上的url,通过匹配key值来找到BackupInfo结构if (it == _table.end())//没找到{pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return false;}*info = it->second;//用info来获取BackupInfo结构pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}// 通过实际路径获取文件信息,这些文件信息都存放到info里面去//现在传进去的realpath不是哈希表的key值bool GetOneByRealpath(const std::string &realpath, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock);//要访问哈希表,必须加锁auto it = _table.begin();for (; it != _table.end(); ++it){if (it->second.real_path == realpath)//通过匹配哈希表里面每个BackupInfo结构里的real_path来找到BackupInfo结构{*info = it->second;//用info来获取BackupInfo结构pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}}pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return false;//没找到}// 获取所有文件信息bool GetAll(std::vector<BackupInfo> *array){pthread_rwlock_wrlock(&_rwlock);//加锁auto it = _table.begin();for (; it != _table.end(); ++it){array->push_back(it->second);//直接填充}pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}private:std::string _backup_file;							// 备份信息持久化存储pthread_rwlock_t _rwlock;							// 读写锁std::unordered_map<std::string, BackupInfo> _table; // 数据信息组织——用哈希表存储};

现在我们来测试一下

data.hpp

#ifndef _MY_DATA_
#define _MY_DATA_
#include "util.hpp"
#include <unordered_map>
#include <pthread.h>
#include "conf.hpp"namespace cloud
{typedef struct BackupInfo{bool pack_flag;									// 文件是否被压缩标志size_t fsize;									// 文件大小time_t mtime;									// 文件最后一次修改时间time_t atime;									// 文件最后一次访问时间std::string real_path;							// 文件实际存储路径std::string pack_path;							// 压缩包存储路径std::string url;								// 文件访问URLbool NewBackupInfo(const std::string &realpath) // 填充上面这些数据{// 当前文件的信息的获取FileUtil fu(realpath);	  // 文件管理类,方便我们管理文件,获取文件属性if (fu.Exists() == false) // 当前文件不存在{std::cout << "new backupinfo: file not exit" << std::endl;return false;}// 配置文件信息的获取Config *config = Config::GetInstance();					   // 构造我们的配置文件对象std::string packdir = config->GetPackDir();				   // 获取配置文件里的压缩路径std::string packsuffix = config->GetPackFileSuffix();	   // 获取配置文件里面的压缩文件的后缀名std::string download_prefix = config->GetDownloadPrefix(); // 获取配置文件里面的下载的url前缀pack_flag = false; // 文件没有被压缩fsize = fu.FileSize();mtime = fu.LastModtime();atime = fu.LastAcctime();real_path = realpath; // 我们传入的参数就是// 注意我们的文件被压缩时文件路径会从./backdir/a.txt变成./packdir/a.txt.lz,这需要压缩路径,文件名,压缩后缀两个信息,这些都在配置文件里面pack_path = packdir + fu.FileName() + packsuffix;// 我们这个url是从./backdir/a.txt变成./download/a.txt,这就需要下载的url前缀,文件名,这些都在配置文件里面url = download_prefix + fu.FileName();return true;}} BackupInfo;class DataManager{public:DataManager(){_backup_file = Config::GetInstance()->GetBackupFile(); // 获取备份信息持久化存储的文件,也就是获取配置文件里面的内容pthread_rwlock_init(&_rwlock, NULL);				   // 初始化读写锁}~DataManager(){pthread_rwlock_destroy(&_rwlock); // 释放读写锁}bool Insert(const BackupInfo &info)//新增{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//key值是url,值是BackupInfo结构pthread_rwlock_unlock(&_rwlock);return true;}bool Updata(const BackupInfo &info)//修改,对于目前来说和新增没有任何区别{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//当key值相同时,会覆盖之前的文件pthread_rwlock_unlock(&_rwlock);return true;}//数据信息访问的接口// 通过url获取文件信息,所有的文件信息都存放到info里面去//现在传进去的url是哈希表的key值bool GetOneByURL(const std::string &url, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock);//要访问哈希表,必须加锁auto it = _table.find(url);//在哈希表里面寻找匹配的上的url,通过匹配key值来找到BackupInfo结构if (it == _table.end())//没找到{pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return false;}*info = it->second;//用info来获取BackupInfo结构pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}// 通过实际路径获取文件信息,这些文件信息都存放到info里面去//现在传进去的realpath不是哈希表的key值bool GetOneByRealpath(const std::string &realpath, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock);//要访问哈希表,必须加锁auto it = _table.begin();for (; it != _table.end(); ++it){if (it->second.real_path == realpath)//通过匹配哈希表里面每个BackupInfo结构里的real_path来找到BackupInfo结构{*info = it->second;//用info来获取BackupInfo结构pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}}pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return false;//没找到}// 获取所有文件信息bool GetAll(std::vector<BackupInfo> *array){pthread_rwlock_wrlock(&_rwlock);//加锁auto it = _table.begin();for (; it != _table.end(); ++it){array->push_back(it->second);//直接填充}pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}private:std::string _backup_file;							// 备份信息持久化存储pthread_rwlock_t _rwlock;							// 读写锁std::unordered_map<std::string, BackupInfo> _table; // 数据信息组织——用哈希表存储};};
#endif

 cloud.cc测试版本1

我们这个函数目前是只测试了那些信息访问的接口,具体接口看下面 

#include "util.hpp"
#include "conf.hpp"
#include "data.hpp"void DataTest(const std::string &filename)
{cloud::BackupInfo info;info.NewBackupInfo(filename);cloud::DataManager data;std::cout<<"---------Insert and GetOneByURL---------"<<std::endl;data.Insert(info);cloud::BackupInfo tmp;data.GetOneByURL("/download/bundle.h", &tmp);std::cout << tmp.pack_flag << std::endl;std::cout << tmp.fsize << std::endl;std::cout << tmp.mtime << std::endl;std::cout << tmp.atime << std::endl;std::cout << tmp.real_path << std::endl;std::cout << tmp.pack_path << std::endl;std::cout << tmp.url << std::endl;std::cout<<"---------update and GetAll---------"<<std::endl;info.pack_flag = true;data.Updata(info);std::vector<cloud::BackupInfo> arry;//获取所有文件data.GetAll(&arry);for (auto &a : arry){std::cout << a.pack_flag << std::endl;std::cout << a.fsize << std::endl;std::cout << a.mtime << std::endl;std::cout << a.atime << std::endl;std::cout << a.real_path << std::endl;std::cout << a.pack_path << std::endl;std::cout << a.url << std::endl;}//根据实际路径进行查询std::cout<<"---------GetOneByRealpath---------"<<std::endl;data.GetOneByRealpath(filename,&tmp);std::cout << tmp.pack_flag << std::endl;std::cout << tmp.fsize << std::endl;std::cout << tmp.mtime << std::endl;std::cout << tmp.atime << std::endl;std::cout << tmp.real_path << std::endl;std::cout << tmp.pack_path << std::endl;std::cout << tmp.url << std::endl;
}
int main(int argc, char *argv[])
{DataTest(argv[1]);return 0;
}

 我们编译一下

这个版本很完美


  • 数据持久化存储的接口

其实需要修改的也就是在类里添加或者修改下面三个函数

//用于持久化存储的接口bool Storage()//每次数据更新或者新增都要持久化存储,避免数据丢失{// 1.获取所有文件的数据std::vector<BackupInfo> array;GetAll(&array);// 2.添加到Json::Value中Json::Value root;//将需要序列化的信息都放到root里面for (int i = 0; i < array.size(); i++){Json::Value item;item["pack_flag"] = array[i].pack_flag;item["fsize"] = (Json::Int64)array[i].fsize;item["atime"] = (Json::Int64)array[i].atime;item["mtime"] = (Json::Int64)array[i].mtime;item["real_path"] = array[i].real_path;item["pack_path"] = array[i].pack_path;item["url"] = array[i].url;root.append(item); // 添加数组元素}// 3.对Json::Value序列化std::string body;JsonUtil::Serialize(root, &body);// 4.写文件FileUtil fu(_backup_file);fu.SetContent(body);return true;}bool Insert(const BackupInfo &info)//新增{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//key值是url,值是BackupInfo结构pthread_rwlock_unlock(&_rwlock);Storage();//新增是需要保存起来的return true;}bool Updata(const BackupInfo &info)//修改,对于目前来说和新增没有任何区别{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//当key值相同时,会覆盖之前的文件pthread_rwlock_unlock(&_rwlock);Storage();//修改后的也是需要做持久化存储的return true;}

 现在所有的信息都存储在这个_backup_file文件里面里面了


  • 初始化加载函数
DataManager(){_backup_file = Config::GetInstance()->GetBackupFile(); // 获取备份信息持久化存储的文件,也就是获取配置文件里面的内容pthread_rwlock_init(&_rwlock, NULL);				   // 初始化读写锁InitLoad();// 初始化加载,将json文件里面的数据读取出来}bool InitLoad()// 初始化加载,在每次系统启动前都要加载以前的数据{// 1.将数据从文件中读取出来FileUtil fu(_backup_file);if (fu.Exists() == false)//如果文件不存在{return true;}std::string body;fu.GetContent(&body);//获取文件里面的数据// 2.反序列化Json::Value root;JsonUtil::Unserialize(body, &root);// 3.将反序列化得到的Json::Value中的数据添加到table中for (int i = 0; i < root.size(); i++){BackupInfo info;info.pack_flag = root[i]["pack_flag"].asBool();info.fsize = root[i]["fsize"].asInt64();info.atime = root[i]["atime"].asInt64();info.mtime = root[i]["mtime"].asInt64();info.pack_path = root[i]["pack_path"].asString();info.real_path = root[i]["real_path"].asString();info.url = root[i]["url"].asString();Insert(info);//将数据添加进哈希表里面去}return true;}

五. 源代码+测试 

data.hpp

#ifndef _MY_DATA_
#define _MY_DATA_
#include "util.hpp"
#include <unordered_map>
#include <pthread.h>
#include "conf.hpp"namespace cloud
{typedef struct BackupInfo{bool pack_flag;									// 文件是否被压缩标志size_t fsize;									// 文件大小time_t mtime;									// 文件最后一次修改时间time_t atime;									// 文件最后一次访问时间std::string real_path;							// 文件实际存储路径std::string pack_path;							// 压缩包存储路径std::string url;								// 文件访问URLbool NewBackupInfo(const std::string &realpath) // 填充上面这些数据{// 当前文件的信息的获取FileUtil fu(realpath);	  // 文件管理类,方便我们管理文件,获取文件属性if (fu.Exists() == false) // 当前文件不存在{std::cout << "new backupinfo: file not exit" << std::endl;return false;}// 配置文件信息的获取Config *config = Config::GetInstance();					   // 构造我们的配置文件对象std::string packdir = config->GetPackDir();				   // 获取配置文件里的压缩路径std::string packsuffix = config->GetPackFileSuffix();	   // 获取配置文件里面的压缩文件的后缀名std::string download_prefix = config->GetDownloadPrefix(); // 获取配置文件里面的下载的url前缀pack_flag = false; // 文件没有被压缩fsize = fu.FileSize();mtime = fu.LastModtime();atime = fu.LastAcctime();real_path = realpath; // 我们传入的参数就是// 注意我们的文件被压缩时文件路径会从./backdir/a.txt变成./packdir/a.txt.lz,这需要压缩路径,文件名,压缩后缀两个信息,这些都在配置文件里面pack_path = packdir + fu.FileName() + packsuffix;// 我们这个url是从./backdir/a.txt变成./download/a.txt,这就需要下载的url前缀,文件名,这些都在配置文件里面url = download_prefix + fu.FileName();return true;}} BackupInfo;class DataManager{public:DataManager(){_backup_file = Config::GetInstance()->GetBackupFile(); // 获取备份信息持久化存储的文件,也就是获取配置文件里面的内容pthread_rwlock_init(&_rwlock, NULL);				   // 初始化读写锁InitLoad();// 初始化加载,将json文件里面的数据读取出来}~DataManager(){pthread_rwlock_destroy(&_rwlock); // 释放读写锁}//用于持久化存储的接口bool Storage()//每次数据更新或者新增都要持久化存储,避免数据丢失{// 1.获取所有文件的数据std::vector<BackupInfo> array;GetAll(&array);// 2.添加到Json::Value中Json::Value root;//将需要序列化的信息都放到root里面for (int i = 0; i < array.size(); i++){Json::Value item;item["pack_flag"] = array[i].pack_flag;item["fsize"] = (Json::Int64)array[i].fsize;item["atime"] = (Json::Int64)array[i].atime;item["mtime"] = (Json::Int64)array[i].mtime;item["real_path"] = array[i].real_path;item["pack_path"] = array[i].pack_path;item["url"] = array[i].url;root.append(item); // 添加数组元素}// 3.对Json::Value序列化std::string body;JsonUtil::Serialize(root, &body);// 4.写文件——把序列化后的写进这个_backup_file文件里面FileUtil fu(_backup_file);fu.SetContent(body);return true;}bool InitLoad()// 初始化加载,在每次系统启动前都要加载以前的数据{// 1.将数据从文件中读取出来FileUtil fu(_backup_file);if (fu.Exists() == false)//如果文件不存在{return true;}std::string body;fu.GetContent(&body);//获取文件里面的数据// 2.反序列化Json::Value root;JsonUtil::Unserialize(body, &root);// 3.将反序列化得到的Json::Value中的数据添加到table中for (int i = 0; i < root.size(); i++){BackupInfo info;info.pack_flag = root[i]["pack_flag"].asBool();info.fsize = root[i]["fsize"].asInt64();info.atime = root[i]["atime"].asInt64();info.mtime = root[i]["mtime"].asInt64();info.pack_path = root[i]["pack_path"].asString();info.real_path = root[i]["real_path"].asString();info.url = root[i]["url"].asString();Insert(info);//将数据添加进哈希表里面去}return true;}bool Insert(const BackupInfo &info)//新增{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//key值是url,值是BackupInfo结构pthread_rwlock_unlock(&_rwlock);Storage();//新增是需要保存起来的return true;}bool Updata(const BackupInfo &info)//修改,对于目前来说和新增没有任何区别{pthread_rwlock_wrlock(&_rwlock);_table[info.url] = info;//当key值相同时,会覆盖之前的文件pthread_rwlock_unlock(&_rwlock);Storage();//修改后的也是需要做持久化存储的return true;}//数据信息访问的接口// 通过url获取文件信息,所有的文件信息都存放到info里面去//现在传进去的url是哈希表的key值bool GetOneByURL(const std::string &url, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock);//要访问哈希表,必须加锁auto it = _table.find(url);//在哈希表里面寻找匹配的上的url,通过匹配key值来找到BackupInfo结构if (it == _table.end())//没找到{pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return false;}*info = it->second;//用info来获取BackupInfo结构pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}// 通过实际路径获取文件信息,这些文件信息都存放到info里面去//现在传进去的realpath不是哈希表的key值bool GetOneByRealpath(const std::string &realpath, BackupInfo *info){pthread_rwlock_wrlock(&_rwlock);//要访问哈希表,必须加锁auto it = _table.begin();for (; it != _table.end(); ++it){if (it->second.real_path == realpath)//通过匹配哈希表里面每个BackupInfo结构里的real_path来找到BackupInfo结构{*info = it->second;//用info来获取BackupInfo结构pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}}pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return false;//没找到}// 获取所有文件信息bool GetAll(std::vector<BackupInfo> *array){pthread_rwlock_wrlock(&_rwlock);//加锁auto it = _table.begin();for (; it != _table.end(); ++it){array->push_back(it->second);//直接填充}pthread_rwlock_unlock(&_rwlock);//不要忘记解锁return true;}private:std::string _backup_file;							// 备份信息持久化存储pthread_rwlock_t _rwlock;							// 读写锁std::unordered_map<std::string, BackupInfo> _table; // 数据信息组织——用哈希表存储};};
#endif

 cloud.cc

#include "util.hpp"
#include "conf.hpp"
#include "data.hpp"void DataTest(const std::string &filename)
{cloud::DataManager data;//构造之后会默认自动化加载std::vector<cloud::BackupInfo> arry;//获取所有文件data.GetAll(&arry);for (auto &a : arry){std::cout << a.pack_flag << std::endl;std::cout << a.fsize << std::endl;std::cout << a.mtime << std::endl;std::cout << a.atime << std::endl;std::cout << a.real_path << std::endl;std::cout << a.pack_path << std::endl;std::cout << a.url << std::endl;}
}
int main(int argc, char *argv[])
{DataTest(argv[1]);return 0;
}

现在就是很完美了 


我们git一下

 

相关文章:

【云备份】服务端数据管理模块设计与实现

目录 一、要管理的数据 二、如何管理数据 三.数据管理类的设计 3.1.数据信息结构体的设计 3.2.数据管理类的设计 四.数据管理类实现 4.1.数据信息结构体的数据填充 4.2.数据管理类的实现 五. 源代码测试 数据管理模块&#xff1a;需要管理的数据有哪些&#xff0c;而…...

Qt开发:按钮类的介绍和使用

文章目录 一、命令按钮类 &#xff08;QPushButton&#xff09;1.1 基本功能介绍1.2 常用构造函数1.3 常用方法1.4 信号与槽使用示例1.5 开关按钮&#xff08;Checkable&#xff09;1.6 创建出菜单按钮&#xff08;Menu Button&#xff09; 二、工具按钮类 &#xff08;QToolBu…...

Clickhouse基于breakpad生成minidump文件,方便问题定位

背景 breakpad能够在程序崩溃的时候自动生成一个mini的core文件&#xff0c;能够帮助进行问题定位&#xff0c;但是clickhouse对于集成breappad的难度较大 查看github发现之前有大佬基于以前的分支有个MR&#xff0c;但是一直没有合并到社区&#xff0c;想来是有什么其他的原因…...

华为云Astro轻应用自定义连接器对接OBS云对象存储操作指南

操作难点图例(详细见下文详细操作步骤) 操作成功图例 说明:以下是通过自定义连接器创建新的OBS桶的图例 说明:以下是通过自定义连接器将数据内容嵌入创建新的OBS桶的图例 操作难点图例 说明:连接器编排需要注意的是动作选择、输入参数的设置等...

C# 运算符重载深度解析:从基础到高阶实践

运算符重载是 C# 中一项强大的特性&#xff0c;它允许开发者为用户自定义类型定义运算符的行为&#xff0c;使得代码更直观、更符合领域逻辑。本文将通过理论解析与实战示例&#xff0c;全面讲解运算符重载的实现规则、适用场景及注意事项。 一、运算符重载的核心概念 1. 什么…...

面试现场“震”情百态:HashMap扩容记

&#xff08;以下为符合要求的文章内容&#xff09; 【场景】 2024年秋招季某互联网大厂会议室 面试官&#xff1a;张工&#xff08;P8级架构师&#xff09; 求职者&#xff1a;马小帅&#xff08;双非二本应届生&#xff09; 第一轮提问&#xff08;Java基础篇&#xff09; …...

SCAU18124--N皇后问题

18124 N皇后问题 时间限制:5000MS 代码长度限制:10KB 提交次数:0 通过次数:0 题型: 编程题 语言: G;GCC;VC Description 有N*N的国际象棋棋盘&#xff0c;要求在上面放N个皇后&#xff0c;要求任意两个皇后不会互杀&#xff0c;有多少种不同的放法&#xff1f; 输入格式 …...

MySQL 分库分表

对于使用 MySQL 作为数据库管理系统的应用来说&#xff0c;当数据量达到一定规模时&#xff0c;单库单表的架构会面临性能瓶颈&#xff0c;如查询缓慢、写入性能下降等问题。为了解决这些问题&#xff0c;可以使用分库分表技术。 二、为什么需要分库分表 2.1 单库单表的局限性…...

滑动窗口leetcode 904

代码&#xff1a; class Solution { public:int totalFruit(vector<int>& fruits) {int n fruits.size();unordered_map<int,int> window_type_count;int left 0;int ans 0;for(int right 0; right <n;right){while(window_type_count.size() 2 &&…...

用Maven定位和解决依赖冲突

用Maven定位和解决依赖冲突 一、依赖冲突的常见表现二、定位冲突依赖的4种方法2.1 使用Maven命令分析依赖树2.2 使用IDE可视化工具2.3 使用Maven Enforcer插件2.4 运行时分析 三、解决依赖冲突的5种方案3.1 排除特定传递依赖3.2 统一指定版本&#xff08;推荐&#xff09;3.3 使…...

八大排序之选择排序

本篇文章将带你详细了解八大基本排序中的选择排序 目录 &#xff08;一&#xff09;选择排序的时间复杂度和空间复杂度及稳定性分析 &#xff08;二&#xff09;代码实现 (三)输出结果 选择排序的基本原理是&#xff1a;每次从待排序的数组中找出最大值和最小值。具体流程是…...

SVM实战:从理论到鸢尾花数据集的分类可视化

SVM实战&#xff1a;从理论到鸢尾花数据集的分类可视化 在机器学习的广阔领域中&#xff0c;支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;作为一种经典且强大的分类算法&#xff0c;备受瞩目。它凭借独特的思想和卓越的性能&#xff0c;在模式识…...

深入解析 Stacking:集成学习的“超级英雄联盟

在机器学习的世界里&#xff0c;我们常常面临一个挑战&#xff1a;单一模型往往难以完美地解决复杂问题。就像漫威电影中的超级英雄们一样&#xff0c;每个模型都有自己的独特能力&#xff0c;但也有局限性。那么&#xff0c;如何让这些模型“联手”发挥更大的力量呢&#xff1…...

C# 编程核心:控制流与方法调用详解

在编程中&#xff0c;控制流和方法调用是构建程序逻辑的两大基石。它们决定了代码的执行顺序和模块化协作方式。本文将从基础概念出发&#xff0c;结合代码示例&#xff0c;深入解析这两部分内容。 控制流&#xff1a;程序执行的指挥棒 控制流决定了代码的执行路径&#xff0…...

PyTorch_张量基本运算

基本运算中&#xff0c;包括add, sub, mul, div, neg等函数&#xff0c;以及这些函数的带下划线的版本add_, sub_, mul_, div_, neg_, 其中带下划线的版本为修改原数据。 代码 import torch import numpy as np # 不修改原数据的计算 def test01():data torch.randint(0, 10…...

C++负载均衡远程调用学习之订阅功能与发布功能

目录 1.lars-DnsV0.1回顾 2.Lars-DnsV0.2-订阅功能的订阅模块分析 3.Lars-DnsV0.2-订阅模块的类的单例创建及方法属性初始化 4.Lars-DnsV0.2-发布功能的实现 5.Lars-DnsV0.2-发布功能的总结 6.Lars-DnsV0.2-订阅流程复习 7.Lars-DnsV0.2-订阅模块的集成 8.Lars-DnsV0.2订…...

接口测试的核心思维(基础篇)

1.为什么会进行接口测试&#xff1f; 早期发现问题&#xff0c;降低修复成本 当我们服务端已经完成&#xff0c;而前端还未进行开发的时候。我们可以通过接口测试避免前端的交互直接进行服务端的测试。 接口测试也能够更早介入项目的测试&#xff0c;降低修复成本。 提高测试…...

给文件内容加行号

题目&#xff1a; 给定一个文件&#xff0c;通过文件读写&#xff0c;给文件内容加行号。 行号形式如: 1:) xxXXXX 2:) xxxxXX 要求&#xff1a; 使用缓冲流操作。 思路分析&#xff1a; 程序定义了两个字符串变量 inputFile 和 outputFile&#xff0c;分别存储输入文件的路径…...

【计算机视觉】三维重建: MVSNet:基于深度学习的多视图立体视觉重建框架

MVSNet&#xff1a;基于深度学习的多视图立体视觉重建框架 技术架构与核心算法1. 算法流程2. 关键创新 环境配置与实战指南硬件要求安装步骤数据准备&#xff08;DTU数据集&#xff09; 实战流程1. 模型训练2. 深度图推断3. 点云生成 常见问题与解决方案1. CUDA内存不足2. 特征…...

终端与环境变量

一、我的电脑是Win10的&#xff0c;首先打开终端如下&#xff1a; 此时终端来到C:\Users\lenovo的目录下&#xff0c;可以访问该目录下的所有文件&#xff08;夹&#xff09;。另外&#xff0c;除了这个之外&#xff0c;终端还可以访问环境变量中的所有文件&#xff08;夹&…...

使用线性表实现通讯录管理

目录 &#x1f680;前言&#x1f99c;任务目标&#x1f31f;顺序表实现&#x1f40d;链表实现 &#x1f680;前言 大家好&#xff01;我是 EnigmaCoder。 本文介绍线性表的实验&#xff0c;使用顺序表和链表实现通讯录管理&#xff0c;包含初始化、插入、删除、查询、输出。 &a…...

机器学习常用评价指标

1. 指标说明 (1) AccuracyClassification&#xff08;准确率&#xff09; • 计算方式&#xff1a;accuracy_score(y_true, y_pred) • 作用&#xff1a; 衡量模型正确预测的样本比例&#xff08;包括所有类别&#xff09;。 公式&#xff1a; Accuracy TP TN TP TN FP…...

基于ArduinoIDE的任意型号单片机 + GPS北斗BDS卫星定位

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1.1 器件选择1.2 接线方案 二、驱动实现2.1 核心代码解析&#xff08;arduino/ESP32-S3&#xff09; 三、坐标解析代码四、典型问题排查总结 前言 北斗卫星导航…...

CGAL:创建点、线、三角形及其距离、关系

CGAL&#xff08;Computational Geometry Algorithms Library&#xff0c;计算几何算法库&#xff09;是一个强大的开源库&#xff0c;为众多几何计算问题提供了高效的解决方案&#xff0c;在计算几何领域应用广泛。以下将基于提供的代码示例&#xff0c;详细介绍如何利用 CGAL…...

STM32基础教程——软件I2C

目录 前言 I2C MPU6050 技术实现 原理图 连线图 代码实现 技术要点 I2C初始化 SCL输出和SDA输入输出控制 起始信号 停止信号 发送一个字节 读取一个字节 发送应答位 接收应答位 MPU6050初始化 指定地址写 指定地址读 读取数据寄存器 问题记录 前言 I2C …...

Xilinx FPGA | 管脚约束 / 时序约束 / 问题解析

注&#xff1a;本文为 “Xilinx FPGA | 管脚约束 / 时序约束 / 问题解析” 相关文章合辑。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 Xilinx FPGA 管脚 XDC 约束之&#xff1a;物理约束 FPGA技术实战 于 2020-02-04 17:14:53 发布 说明&#x…...

应用层自定义协议序列与反序列化

目录 一、网络版计算器 二、网络版本计算器实现 2.1源代码 2.2测试结果 一、网络版计算器 应用层定义的协议&#xff1a; 应用层进行网络通信能否使用如下的协议进行通信呢&#xff1f; 在操作系统内核中是以这种协议进行通信的&#xff0c;但是在应用层禁止以这种协议进行…...

大数据:数字时代的驱动力

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 随着互联网和技术的迅猛发展,数据已经成为现代社会最宝贵的资源之一。大数据(Big Data)作为一种全新的信息资源,正以前所未有的方式改变着各个行业的运作模式,推动着社会的进步与创新。无论是金…...

java学习之数据结构:二、链表

本节介绍链表 目录 1.什么是链表 1.1链表定义 1.2链表分类 2.链表实现 2.1创建链表 1&#xff09;手动创建 2&#xff09;创建链表类进行管理链表的相关操作 2.2添加元素 1&#xff09;头插法 2&#xff09;尾插法 3&#xff09;任意位置插入 2.3删除 2.4查找 1&…...

评估动态化烹饪工艺与营养实训室建设要点

在全民健康意识显著提升、健康饮食理念深度普及的时代背景下&#xff0c;烹饪工艺与营养实训室建设要点已不再局限于传统单一的技能训练模式。需以行业需求为导向&#xff0c;通过功能集成化设计推动革新 —— 将食品安全防控、营养科学分析、智能烹饪技术、餐饮运营管理等多元…...

Java学习手册:关系型数据库基础

一、关系型数据库概述 关系型数据库是一种基于关系模型的数据库&#xff0c;它将数据组织成一个或多个表&#xff08;或称为关系&#xff09;&#xff0c;每个表由行和列组成。每一列都有一个唯一的名字&#xff0c;称为属性&#xff0c;表中的每一行是一个元组&#xff0c;代…...

吾爱出品 [Windows] EndNote 21.5.18513 汉化补丁

[Windows] EndNote 链接&#xff1a;https://pan.xunlei.com/s/VOPLLs6DqKNz-EoBSWVRTSmGA1?pwd9isc# Thomson Scientific公司推出了2025&#xff0c;本次的endnote21大概率是最后一个版本啦&#xff0c;现已决定进行更新。 本次采用的环境为python3.12&#xff0c;win11&am…...

Sentinel学习

sentinel是阿里巴巴研发的一款微服务组件&#xff0c;主要为用户提供服务保护&#xff0c;包括限流熔断等措施 &#xff08;一&#xff09;主要功能 流量控制&#xff08;限流&#xff09;&#xff1a;比如限制1s内有多少请求能到达服务器&#xff0c;防止大量请求打崩服务器…...

【中间件】brpc_基础_execution_queue

execution_queue 源码 1 简介 execution_queue.h 是 Apache BRPC 中实现 高性能异步任务执行队列 的核心组件&#xff0c;主要用于在用户态线程&#xff08;bthread&#xff09;中实现任务的 异步提交、有序执行和高效调度。 该模块通过解耦任务提交与执行过程&#xff0c;提…...

Servlet(二)

软件架构 1. C/S 客户端/服务器端 2. B/S 浏览器/服务器端&#xff1a; 客户端零维护&#xff0c;开发快 资源分类 1. 静态资源 所有用户看到相同的部分&#xff0c;如&#xff1a;html,css,js 2. 动态资源 用户访问相同资源后得到的结果可能不一致&#xff0c;如&#xff1a;s…...

如何提升个人的思维能力?

提升个人的逻辑思维能力是一个系统性工程&#xff0c;需要长期训练和科学方法。以下是分阶段、可操作的详细建议&#xff0c;涵盖理论基础、日常训练和实战应用&#xff1a; 一、构建逻辑基础认知 1. 学习逻辑学核心理论 入门读物&#xff1a;《简单的逻辑学》麦克伦尼&am…...

[UVM]UVM中reg_map的作用及多个rem_map的使用案例

UVM中reg_map的作用及多个rem_map的使用案例 摘要:在 UVM (Universal Verification Methodology) 中,寄存器模型是用于验证 DUT (Design Under Test) 寄存器行为的重要工具。UVM 寄存器模型中的 uvm_reg_map(简称 reg_map)是寄存器模型的核心组成部分之一,用于定义…...

重新构想E-E-A-T:提升销售与搜索可见性的SEO策略

在2025年的数字营销环境中&#xff0c;谷歌的E-E-A-T&#xff08;经验、专业性、权威性、可信度&#xff09;已成为SEO和内容营销的核心支柱。传统的E-E-A-T优化方法通常聚焦于展示作者资质或获取反向链接&#xff0c;但这些策略可能不足以应对AI驱动的搜索和日益挑剔的用户需求…...

AI 采用金字塔(Sohn‘s AI Adoption Pyramid)

这张图是 Sohn 的 AI 采用金字塔&#xff08;Sohn’s AI Adoption Pyramid&#xff09; &#xff0c;用于描述不同程度的 AI 应用层次&#xff0c;各层次意义如下&#xff1a; 金字塔层级 Level 1&#xff1a;业务角色由人类主导&#xff0c;AI 起辅助作用&#xff0c;如 AI …...

影刀RPA中新增自己的自定义指令

入门到实战明细 1. 影刀RPA自定义指令概述 1.1 定义与作用 影刀RPA的自定义指令是一种强大的功能&#xff0c;旨在提高流程复用率&#xff0c;让用户能够个性化定制指令&#xff0c;实现流程在不同应用之间的相互调用。通过自定义指令&#xff0c;用户可以将常用的、具有独立…...

驱动总裁v2.19(含离线版)驱动工具软件下载及安装教程

1.软件名称&#xff1a;驱动总裁 2.软件版本&#xff1a;2.19 3.软件大小&#xff1a;602 MB 4.安装环境&#xff1a;win7/win10/win11 5.下载地址&#xff1a; https://www.kdocs.cn/l/cdZMwizD2ZL1?RL1MvMTM%3D 提示&#xff1a;先转存后下载&#xff0c;防止资源丢失&am…...

SQL经典实例

第1章 检索记录 1.1 检索所有行和列 知识点&#xff1a;使用SELECT *快速检索表中所有列&#xff1b;显式列出列名&#xff08;如SELECT col1, col2&#xff09;提高可读性和可控性&#xff0c;尤其在编程场景中更清晰。 1.2 筛选行 知识点&#xff1a;通过WHERE子句过滤符合条…...

2025深圳杯(东三省)数学建模竞赛D题完整分析论文(共36页)(含模型、可运行代码、数据结果)

2025深圳杯数学建模竞赛D题完整分析论文 目录 摘 要 一、问题重述 二、问题分析 三、模型假设 四、符号定义 五、问题一模型的建立与求解 5.1 问题一模型的建立 5.1.1 问题建模背景 5.1.2 特征工程设计 5.1.3 分类模型结构与数学表达 5.2 问题一模型的求…...

大数据技术:从趋势到变革的全景探索

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 在数字化时代的浪潮下,大数据已经不再是一个陌生的概念。从日常生活中的社交媒体,到企业决策支持系统,再到公共管理的大数据应用,它正在改变着我们的工作和生活方式。随着技术的进步,传统的数据…...

C++【内存管理】

C语言中的动态内存管理 int main() { int* p2(int*)calloc(4,sizeof(int)); int* p3(int*)realloc(p2,sizeof(int)*10); free(p3); }这里因为扩容数据小,所以是原地扩容,p2p3地址一样,不用free(p2) 如果扩容空间大就不是原地扩容,而是新找一块空间,然后拷贝 C内存管理方式 n…...

【Go类库分享】mcp-go Go搭建MCP服务

【Go类库分享】mcp-go Go搭建MCP服务 介绍 目前Go 生态圈有两个知名的开发 MCP 的库&#xff0c;一个是mark3labs/mcp-go,另一个是metoro-io/mcp-golang。 在介绍常用库之前&#xff0c;先来简单介绍一下mcp协议&#xff1a; MCP全称Model Context Protocol 模型上下文协议&a…...

人工智能发展史 — 物理学诺奖之 Hopfield 联想和记忆神经网络模型

目录 文章目录 目录1982 年&#xff0c;Hopfield 联想和记忆神经网络模型背景知识历史&#xff1a;霍普菲尔德简介神经学&#xff1a;大脑的联想记忆机制物理学&#xff1a;磁性材料的自旋玻璃理论和能量最小值函数 Hopfield 神经网络基本原理记忆存储&#xff08;训练&#xf…...

Docker —— 技术架构的演进

Docker —— 技术架构的演进 技术架构演进总结单机架构优点缺点总结 应用数据分离架构优点缺点总结 应用服务集群架构1. Nginx2. HAProxy3. LVS&#xff08;Linux Virtual Server&#xff09;4. F5 BIG-IP对比总结选型建议 读写分离/主从分离架构1. MyCat简介 2. TDDL&#xff…...

Docker与WSL2如何清理

文章目录 Docker与WSL2如何清理一、docker占据磁盘空间核心原因分析1. WSL2 虚拟磁盘的动态扩展特性2. Docker 镜像分层缓存与未清理资源 二、解决方案步骤 1&#xff1a;清理 Docker 未使用的资源步骤 2&#xff1a;手动压缩 WSL2 虚拟磁盘1. 关闭 WSL2 和 Docker Desktop2. 定…...

单片机嵌入式按键库

kw_btn库说明 本库主要满足嵌入式按键需求&#xff0c;集成了常用的按键响应事件&#xff1a;高电平、低电平、上升沿、下降沿、单击、双击、长按键事件。可以裸机运行&#xff0c;也可以配合实时操作系统运行。 本库开源连接地址&#xff1a;gitee连接 实现思路 本库采用C语…...