一,数据信息管理模块设计
该模块主要用于服务端对备份的文件进行数据信息管理,以便于业务处理模块或者热点管理模随用随取,以及在网页上迅速的显示备份文件列表,因此该模块需要管理的信息也是根据后续业务处理模块以及热点管理模块确定的
根据后续需要我们要管理的信息大概有以下几种
1. 文件的实际存储路径:当用户发出下载请求时,会从该路径读取文件进行响应
2. 文件的压缩包存放路径名:当用户要下载的文件为非热点文件时,需要到该路径下找到目标文件压缩包进行解压,再响应
3. 文件是否压缩标志位:判断文件是否已经被压缩
4. 文件大小
5. 文件最后一次修改时间
6. 文件最后一次访问时间
7. 文件访问中URL的资源路径:/download/a.txt
那么我们如何进行数据管理呢?
1.首先对于数据的访问,最常用的就是查找,如当插入时需要查找资源是否存在,要获取某个备份文件时需要查找资源是否存在,当要更新备份文件时也需要迅速找到目标文件位置;因此我们选择用hash表在内存中管理数据,以URL的下载路径作为key值,对应的信息结构体作为value
2. 持久化存储管理:当服务器关闭或者重启时,我们的文件信息也需要保存到文件中,当服务器再次开始,信息管理模块直接从文件中读取信息即可,因此我们选择用Json序列化将信息保存起来,取出时反序列化即可
数据管理类提供的主要接口如下所示:
#ifndef _MY_MANAGER_
#define _MY_MANAGER_
#include "util.hpp"
#include "config.hpp"
#include <unordered_map>
namespace mjw_cloud
{
struct BackupInfo
{
BackupInfo(const std::string real_path);
BackupInfo& operator=(const BackupInfo& data);//DataManager中的接口需要
bool _pack_flag; // 判断文件是否压缩
size_t _fsize; // 文件大小
time_t _atime; // 文件最后一次访问时间
time_t _mtime; // 文件最后一次修改时间
std::string _real_path; // 文件真实存储路径 ./packdir/
std::string _pack_path; // 文件压缩包存储路径 ./backdir/
std::string _url_path; // 文件访问中url的资源路径 /download/文件名
};
class DataManager
{
public:
DataManager();
~DataManager();
// 存储
bool Storage();
// 从文件中读取文件信息管理表
bool InitTable();
// 表中插入数据
bool Insert(const BackupInfo &data);
// 修改更新表中数据
bool Updata(const BackupInfo &data);
// 根据url获取对应文件信息,用户根据url请求下载文件时需要
bool GetoneByURL(const std::string &url_path, BackupInfo *data);
// 根据文件存放路径获取文件信息,服务端检测备份文件时要用
bool GetoneByRp(const std::string &real_path, BackupInfo *data);
// 获取所有的文件信息,向用户提供一个备份文件表时需要
bool GetAll(std::vector<BackupInfo> *arry);
private:
std::unordered_map<std::string, BackupInfo> _table; // 文件信息管理表
std::string _backup_file; // 持久化存储文件信息管理表
pthread_rwlock_t _rwlock; // 读写锁,读共享,写互斥
};
}
#endif
二,代码实现
代码:
#ifndef _MY_MANAGER_
#define _MY_MANAGER_
#include "util.hpp"
#include "config.hpp"
#include <unordered_map>
#include <pthread.h>
namespace mjw_cloud
{
struct BackupInfo
{
// 后面数据管理类的InitTable需要用
BackupInfo() {}
BackupInfo(const std::string real_path)
{
FileUtil fu(real_path);
if (fu.Exists() == false)
{
std::cout << "文件不存在" << std::endl;
return;
}
_pack_flag = 0;
_fsize = fu.FileSize();
_atime = fu.LastATime();
_mtime = fu.LastMTime();
_real_path = real_path;
//"./packdir/a.out" -> "./backdir/a.out.lz"
_pack_path = Config::GetInstance().GetPackDir() + fu.FileName() + Config::GetInstance().GetLzPrefix();
_url_path = Config::GetInstance().GetUrlPrefix() + fu.FileName();
}
BackupInfo &operator=(const BackupInfo &data) // DataManager中的接口需要
{
_pack_flag = data._pack_flag;
_fsize = data._fsize;
_atime = data._atime;
_mtime = data._mtime;
_real_path = data._real_path;
_pack_path = data._pack_path;
_url_path = data._url_path;
return *this;
}
bool _pack_flag; // 判断文件是否压缩
size_t _fsize; // 文件大小
time_t _atime; // 文件最后一次访问时间
time_t _mtime; // 文件最后一次修改时间
std::string _real_path; // 文件真实存储路径名称 ./packdir/a.out
std::string _pack_path; // 文件压缩包存储路径名称 ./backdir/a.out.lz
std::string _url_path; // 文件访问中url的资源路径 /download/文件名
};
class DataManager
{
public:
DataManager()
{
pthread_rwlock_init(&_rwlock, nullptr);
_backup_file = Config::GetInstance().GetManagerFile();
// 初始化_table
InitTable();
}
~DataManager()
{
pthread_rwlock_destroy(&_rwlock);
}
// 存储
bool Storage()
{
// 1. 将table Json序列化
Json::Value root;
for (auto t : _table)
{
Json::Value val;
val["_pack_flag"] = t.second._pack_flag;
// Json支持内置类型,因此size_t和time_t都需强转
val["_fsize"] = (Json::Int64)t.second._fsize;
val["_atime"] = (Json::Int64)t.second._atime;
val["_mtime"] = (Json::Int64)t.second._mtime;
val["_real_path"] = t.second._real_path;
val["_pack_path"] = t.second._pack_path;
val["_url_path"] = t.second._url_path;
root.append(val);
}
std::string str;
JsonUtil::Serialize(root, &str);
// 2. 写入存储文件
FileUtil fu(_backup_file);
if (fu.SetContent(str) == false)
{
std::cout << "storage failed" << std::endl;
return false;
}
return true;
}
// 从文件中读取文件信息管理表
bool InitTable()
{
// 读取信息
FileUtil fu(_backup_file);
if (fu.Exists() == false)
return true;
std::string str;
if (fu.GetContent(&str) == false)
{
std::cout << "InitTable failed" << std::endl;
return false;
}
// 反序列化
Json::Value root;
JsonUtil::UnSerialize(str, &root);
for (int i = 0; i < root.size(); i++)
{
BackupInfo info;
info._atime = root[i]["_atime"].asInt();
info._mtime = root[i]["_mtime"].asInt();
info._fsize = root[i]["_fsize"].asInt();
info._pack_flag = root[i]["_pack_flag"].asBool();
info._pack_path = root[i]["_pack_path"].asString();
info._real_path = root[i]["_real_path"].asString();
info._url_path = root[i]["_url_path"].asString();
Insert(info);
}
return true;
}
// 表中插入数据
bool Insert(const BackupInfo &data)
{
pthread_rwlock_wrlock(&_rwlock);
_table[data._url_path] = data;
pthread_rwlock_unlock(&_rwlock);
//更新数据,持久化存储
Storage();
return true;
}
// 修改更新表中数据
bool Updata(const BackupInfo &data)
{
pthread_rwlock_wrlock(&_rwlock);
_table[data._url_path] = data;
pthread_rwlock_unlock(&_rwlock);
Storage();
return true;
}
// 根据url获取对应文件信息,用户根据url请求下载文件时需要
bool GetoneByURL(const std::string &url_path, BackupInfo *data)
{
pthread_rwlock_rdlock(&_rwlock);
auto it = _table.find(url_path);
if (it == _table.end())
{
std::cout << "file unexist" << std::endl;
pthread_rwlock_unlock(&_rwlock);
return false;
}
*data = _table[url_path];
pthread_rwlock_unlock(&_rwlock);
return true;
}
// 根据文件存放路径获取文件信息,服务端检测备份文件时要用
bool GetoneByRp(const std::string &real_path, BackupInfo *data)
{
// 遍历_table,一一比对
pthread_rwlock_rdlock(&_rwlock);
for (auto t : _table)
{
if (t.second._real_path == real_path)
{
*data = t.second;
pthread_rwlock_unlock(&_rwlock);
return true;
}
}
pthread_rwlock_unlock(&_rwlock);
return false;
}
// 获取所有的文件信息,向用户提供一个备份文件表时需要
bool GetAll(std::vector<BackupInfo> *arry)
{
// 遍历_table,全部插入arry
pthread_rwlock_rdlock(&_rwlock);
for (auto t : _table)
{
arry->push_back(t.second);
}
pthread_rwlock_unlock(&_rwlock);
return true;
}
private:
std::unordered_map<std::string, BackupInfo> _table; // 文件信息管理表
std::string _backup_file; // 持久化存储文件信息管理表
pthread_rwlock_t _rwlock; // 读写锁,读共享,写互斥
};
}
#endif
测试:
在测试之前我们先将bundle.cpp文件打包成库,因为每次联合编译都很慢,打包成库后编译速度会加快不少。
g++ -c bundle.cpp -o bundle.o
ar -cr libbundle.a bundle.o
mkdir lib//创建lib库
mv libbundle.a lib//将libbnudle.a移动到lib中
//makefile编译指令
g++ -o $@ $^ -L./lib -std=c++11 -lpthread -ljsoncpp -lbundle
测试代码如下
void Managertest(const std::string &real_path)
{
std::cout << "-------------BackupInfo test----------------" << std::endl;
mjw_cloud::BackupInfo info(real_path);
std::cout << info._pack_flag << std::endl;
std::cout << info._fsize << std::endl;
std::cout << info._atime << std::endl;
std::cout << info._mtime << std::endl;
std::cout << info._pack_path << std::endl;
std::cout << info._real_path << std::endl;
std::cout << info._url_path << std::endl;
std::cout << std::endl;
std::cout << "-------------DataManager test---------------" << std::endl;
mjw_cloud::DataManager dm;
dm.Insert(info);
mjw_cloud::BackupInfo ex1;
dm.GetoneByURL(info._url_path, &ex1);
std::cout << ex1._pack_flag << std::endl;
std::cout << ex1._fsize << std::endl;
std::cout << ex1._atime << std::endl;
std::cout << ex1._mtime << std::endl;
std::cout << ex1._pack_path << std::endl;
std::cout << ex1._real_path << std::endl;
std::cout << ex1._url_path << std::endl;
std::cout << std::endl;
mjw_cloud::BackupInfo ex2;
dm.GetoneByRp(info._real_path, &ex2);
std::cout << ex2._pack_flag << std::endl;
std::cout << ex2._fsize << std::endl;
std::cout << ex2._atime << std::endl;
std::cout << ex2._mtime << std::endl;
std::cout << ex2._pack_path << std::endl;
std::cout << ex2._real_path << std::endl;
std::cout << ex2._url_path << std::endl;
std::cout << std::endl;
std::vector<mjw_cloud::BackupInfo> arry;
dm.GetAll(&arry);
for (auto a : arry)
{
std::cout << a._pack_flag << std::endl;
std::cout << a._fsize << std::endl;
std::cout << a._atime << std::endl;
std::cout << a._mtime << std::endl;
std::cout << a._pack_path << std::endl;
std::cout << a._real_path << std::endl;
std::cout << a._url_path << std::endl;
std::cout << std::endl;
}
}
结果如下