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

项目实现:云备份服务端③(热点模块、服务端业务处理模块实现)

云备份

  • 前言
  • 热点模块
    • 热点管理类
  • 业务处理模块
    • 业务处理类
    • 断点续传功能
  • 服务端整体功能联调

前言

依旧是老话题,在开始看前,对整个项目不熟悉的老铁可以看看小编先前的博客:云备份项目介绍,看完之后再回过来当前内容会好很多。

下面不再多说,接下来直接开始进入正题:

热点模块

  • 热点管理模块功能:对服务器上备份的文件进行检测,判断哪些文件长时间没有被访问,长时间没有被访问的文件就是非热点文件

对于非热点文件的处理,就是将原文件进行压缩存储,节省磁盘空间。主要的实现思路如下:

  1. 遍历备份目录,获取所有的文件路径名
  2. 逐个遍历每个文件,获取到文件的最后一次访问时间与当前系统时间进行比较
  3. 对非热点文件进行压缩处理,删除源文件
  4. 修改数据管理模块对应的文件信息,将文件的压缩标志位修改为:true

热点管理类

  • RunModule:对非热点文件进行管理
  • HotJudge:热点文件判断

在 src 目录下创建 hot.hpp 文件,设计HotManager类:

#ifndef __MY_HOT__
#define __MY_HOT__#include "data.hpp"
#include <unistd.h>extern cloud::DataManager *_data; //用作全局namespace cloud
{// 热点管理类class HotManager{public:HotManager(){// 从配置文件获取信息Config *config = Config::GetInstance();_pack_dir = config->GetPackDir();_pack_suffix = config->GetPackFileSuffix();_back_dir = config->GetBackDir();_hot_time = config->GetHotTime();// 目录不存在直接创建FileUtil tmp1(_pack_dir);FileUtil tmp2(_back_dir);tmp1.CreateDirectory();tmp2.CreateDirectory();}bool RunModule(){//实时监测热点文件while (1){// 1.遍历备份目录,获取所有的文件路径名FileUtil fu(_back_dir);std::vector<std::string> array;// 将获取的文件名存放到arrayfu.ScanDirectory(&array);// 2.逐个判断是否为热点文件for (auto &e : array){if (HotJudge(e) == false){// 热点文件不做处理continue;}// 3.获取文件的备份信息BackupInfo bi;// 防止有备份文件的信息没有录入if (_data->GetOneByRealPath(e, &bi) == false){// 将当前文件信息进行重新设置bi.NewBackupInfo(e);}// 4.对非热点文件进行压缩处理FileUtil tmp(e);tmp.Compress(bi.pack_path); // 压缩+压缩后缀名// 5.删除源文件,修改备份信息tmp.Remove();_data->UpDate(bi); // 更新数据}//休眠,减少CUP运行usleep(1000);}}private:// 非热点文件判断:非热点true,热点falsebool HotJudge(std::string &filename){FileUtil fu(filename);// 文件最后访问时间time_t last_time = fu.LastATime();time_t now_time = time(NULL);// 当前系统时间 - 最后访问时间if (now_time - last_time > _hot_time)return true;return false;}private:std::string _pack_dir;std::string _pack_suffix; // 压缩包后缀名std::string _back_dir;size_t _hot_time; // 热点时间};
}
#endif

业务处理模块

由于云备份项目借用了 httplib 第三方库,网络通信直接通过httplib库完成。将网络通信模块和业务处理进行了合并,这里只需要着重关注如何处理业务处理即可。

业务请求处理包含以下几点:

  1. 文件上传请求:备份客户端上传的文件,响应上传成功
  2. 文件列表请求:客户端浏览器请求一个备份文件的展示页面,响应页面
  3. 文件下载请求:通过展示页面,用户点击下载,响应客户端要下载的文件数据

业务处理类

  • RunModule业务处理函数
  • UpLoad文件上传 请求处理函数
  • ListShow文件总页面展示 请求处理函数
  • DownLoad文件下载 请求处理函数

在 scr 目录下创建 server.hpp 文件,编写业务处理类代码:

#ifndef __My_SERVER__
#define __My_SERVER__#include "data.hpp"
#include "httplib.h"extern cloud::DataManager* _data;namespace cloud
{class server{public:server(){//使用配置文件进行初始化处理Config* config = Config::GetInstance();_server_port = config->GetServerPort();_server_ip = config->GetServerIP();_download_prefix = config->GetDownloadPrefix();}//业务处理函数bool RunModule(){//文件上传请求_server.Post("/upload", Upload);//文件总页面展示请求_server.Get("/listshow", ListShow);_server.Get("/", ListShow); //默认请求//文件下载请求std::string download_url = _download_prefix + "(.*)"; //(.*)是正则表达式;. 点表示任意字符,*表示前面的字符可以出现任意次数,一次或者多次_server.Get(download_url.c_str(), Download);//捕获客户端连接_server.listen(_server_ip.c_str(), _server_port);return true;}private://文件上传请求处理函数static void Upload(const httplib::Request &req, httplib::Response &rsp){//整个请求报文的正文不全为文件数据auto ret = req.has_file("file"); if(ret == false)  //判断有没有上传文件区域{rsp.status = 400;return ;}const auto& file = req.get_file_value("file"); //获取请求中的文件数据//file.filename文件名   file.content文件内容//备份文件所在的目录std::string back_dir = Config::GetInstance()->GetBackDir();//文件路径 == 备份文件所在的目录+文件名std::string realpath = back_dir + FileUtil(file.filename).FileName();FileUtil fu(realpath);//将上传文件的内容写入到realpath中fu.SetContent(file.content);//数据备份好,将数据管理起来BackupInfo info;info.NewBackupInfo(realpath); //组织备份文件的信息_data->Insert(info);//向数据管理模块插入新的备份文件信息}//时间戳转特定的时间格式static std::string TimeToStr(const time_t t){std::string tmp = std::ctime(&t);return tmp;}//文件总页面展示请求处理函数static void ListShow(const httplib::Request &req, httplib::Response &rsp){//1.获取所有的备份文件信息std::vector<BackupInfo> array;_data->GetAll(&array);//2.根据备份文件的信息,组织html文件格式std::stringstream ss;ss << "<!DOCTYPE html><html lang='en'><head><meta charset='UTF-8'>";ss << "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";ss << "<title>文件下载页面</title><style>";ss << "body {font-family: Arial, sans-serif;margin: 40px;}";ss << ".download-table{width: 100%;border-collapse: collapse; margin-top: 20px;}";ss << ".download-table th, .download-table td {border: 1px solid #ddd;padding: 8px;text-align: left;}";ss << ".download-table th { background-color: #f2f2f2;}";ss << ".download-table .date, .download-table .size {text-align: right;}";ss << ".download-link {text-decoration: none;color: #007bff; }";ss << ".download-link:hover { text-decoration: underline; }";ss << " </style> </head><body><h1>文件下载</h1>";ss << "<p>请从下面的列表中选择要下载的文件。</p> <table class='download-table'>";ss << "<thead><tr><th>文件名</th><th class='date'>日期</th> <th class='size'>大小</th></tr></thead><tbody>";for(auto& a : array){ss << "<tr><td>";std::string filename = FileUtil(a.real_path).FileName();ss << "<a href='" << a.url << "'class='download-link'>" << filename << "</a>";ss << "</td><td class='date'>" << TimeToStr(a.mtime) << "</td>"; //显示最后修改时间ss << "<td class='size'>" << a.fsize / 1024 << "k</td></tr>"; //文件大小以k显示}ss << "</tbody></table></body></html>";//将刚刚编好的html格式回应给客户端rsp.body = ss.str();//设置头部内容为html文本rsp.set_header("Content-Type", "text/html");rsp.status = 200;}//获取唯一标识符:文件名+文件大小+文件最后一次修改时间static std::string GetETag(const BackupInfo& info){FileUtil fu(info.real_path);std::string etag = fu.FileName();etag += '-';etag += std::to_string(info.fsize);etag += '-';etag += std::to_string(info.mtime); //最后一次修改时间return etag;}//文件下载请求处理函数static void Download(const httplib::Request &req, httplib::Response &rsp){/1.获取客户端请求的资源路径path   req.path//2.根据资源路径,获取文件备份信息BackupInfo info;_data->GetOneByUrl(req.path, &info); //通过资源路径获取备份文件的信息//3.文件是否被压缩?是,先进行解压;删除对应的压缩包,修改文件数据信息if(info.pack_flag == true){FileUtil fu(info.pack_path); //压缩文件所在位置//将文件解压到备份文件路径下fu.UnCompress(info.real_path);fu.Remove();//更新数据信息info.pack_flag = false;_data->UpDate(info);}//4.读取文件信息,将内容设置到rsp中FileUtil fu(info.real_path); //备份文件fu.GetContent(&rsp.body);//5.设置响应报头字段:ETag、Accept-Ranges:bytes;rsp.set_header("Accept-Ranges", "bytes");//通过特定的区间来访问资源rsp.set_header("ETag", GetETag(info)); //用于确定文件是否被修改rsp.set_header("Content-Type", "application/octet-stream"); //响应的内容被视为任意的二进制数据rsp.status = 200;}private:int16_t _server_port;std::string _server_ip;std::string _download_prefix; // 文件下载资源路径httplib::Server _server;};
}#endif

断点续传功能

当文件下载过程中,因为某种异常而中断,如果再次进行从头下载,效率较低。为了提高效率,实现断点续传功能。

  • 功能介绍:断点续传就是从上次下载断开的位置重新下载,之前已经传输过的数据将不需要在重新传输

客户端在下载文件的时候,要每次接收到数据写入文件后记录自己当前下载的数据量。当异常下载中断时,下次断点续传的时候,将要重新下载的数据区间(下载起始位置,结束位置)发送给服务器。服务器收到后,仅仅回传客户端需要的区间数据即可。

实现断点续传需要考虑这样的一个问题:如果上次下载文件之后,这个文件在服务器上被修改了,则这时候将不能重新断点续传,而是应该重新进行文件下载操作

改写文件下载请求处理函数,增加断点续传功能:

 // 文件下载请求处理函数
static void Download(const httplib::Request &req, httplib::Response &rsp)
{// 1.获取客户端请求的资源路径path   req.path// 2.根据资源路径,获取文件备份信息BackupInfo info;_data->GetOneByUrl(req.path, &info); // 通过资源路径获取备份文件的信息// 3.文件是否被压缩?是,先进行解压;删除对应的压缩包,修改文件数据信息if (info.pack_flag == true){FileUtil fu(info.pack_path); // 压缩文件所在位置// 将文件解压到备份文件路径下fu.UnCompress(info.real_path);fu.Remove();// 更新数据信息info.pack_flag = false;_data->UpDate(info);}// 4.读取文件信息,将内容设置到rsp中FileUtil fu(info.real_path); // 备份文件fu.GetContent(&rsp.body);bool retrans = false; // 用于标识是否符合断点续传std::string old_etag;if (req.has_header("If-Range")){old_etag = req.get_header_value("If-Range");// 含有If-Range字段,If-Range字段的etag值与请求的文件最新的etag一致,则符合断点续传if (old_etag == GetETag(info))retrans = true;}// 5.设置响应报头字段:ETag、Accept-Ranges:bytes;if (retrans == false) {// 不符合断点续传,直接传整个备份文件fu.GetContent(&rsp.body);rsp.set_header("Accept-Ranges", "bytes");                   // 通过特定的区间来访问资源rsp.set_header("ETag", GetETag(info));                      // 用于确定文件是否被修改rsp.set_header("Content-Type", "application/octet-stream"); // 响应的内容被视为任意的二进制数据rsp.status = 200;}else // 符合断点续传{//httplib库内部实现了断点续传的功能//在这里只需要将文件的内容读取到body,httplib内部函数会自动处理对应区间数据//从body中取出指定区间数据进行响应fu.GetContent(&rsp.body);rsp.set_header("Accept-Ranges", "bytes"); // 通过特定的区间来访问资源rsp.set_header("ETag", GetETag(info));rsp.status = 206;}
}

服务端整体功能联调

为了能够联动服务端各个模块的功能,下面采取多线程的方式分别执行热点模块、业务处理模块:

#include <thread>#include "hot.hpp"
#include "server.hpp"cloud::DataManager* _data;void HotTask()
{cloud::HotManager hot;//进行热点文件检测hot.RunModule();
}void ServerTask()
{cloud::server srv;//启动服务器进行业务处理srv.RunModule();
}int main(int argc, char *argv[])
{_data = new cloud::DataManager();//热点模块任务std::thread thread_Hot(HotTask);//业务处理模块std::thread thread_Server(ServerTask);//等待线程thread_Hot.join();thread_Server.join();return 0;
}

http://www.mrgr.cn/news/34345.html

相关文章:

  • 三线城市的女玩家们不想“谈恋爱”,小游戏掘金新蓝海
  • 【Transformers基础入门篇4】基础组件之Model
  • 干货:企业微信批量删除客户指南!
  • 13.第二阶段x86游戏实战2-动态模块地址
  • 【Go】Go语言中深拷贝和浅拷贝
  • Java详细学习路线:从入门到精通的全方位指南
  • 数字人起飞!字节Loopy对口型功能上线 可根据语境匹配表情和情绪
  • 一个可以在线制作样本册,拥有海量样本图册模板可以套用的网站
  • Vert.x,Core - Future
  • 视频无损压缩工具+预览视频生成工具
  • Java 中使用 Gson 实现深度克隆 #什么是深克隆与浅克隆?#clone方法为什么不能直接通过某个对象实例在外部类调用?
  • 我设置了路由器自动切换ip,这会让我的账号登录地址经常改变吗
  • 奔驰「进退」两难
  • Webpack 常见配置项
  • apply、call和bind的作用和区别
  • 装饰器模式
  • Vue使用Vue Router路由:开发单页应用
  • 【网络协议栈】传输层的意义 和 UDP协议结构的解析(内含逻辑图解通俗易懂)
  • yolo自动化项目实例解析(四)ui页面整理1 (1.85)
  • kafka负载均衡迁移(通过kafka eagle)