上一章我们分析了gstore安全机制系列的源码,接下来将解析gstore的备份与恢复机制。
1.1 简介
gstore的备份和恢复可通过两种方式进行:第一种是通过本地命令执行;第二种就是调用API接口,基于API接口的方式我们可以很方便的实现定时备份的功能,在gstore可视化管理工具workbench中就集成了此功能;接下来会逐一进行解析。
1.2 备份
数据库的备份是针对具体的某一个库进行全量的数据备份,备份的原理就是复制当前数据库文件夹到备份目录下,并重命名为带时间戳后缀的文件夹。
1.2.1 本地备份
本地备份的执行命令和参数说明如下:
$ bin/gbackup -db dbname -p backupfilepath
# dbname:数据库名称
# backupfilepath(optional):备份数据库路径(相对路径,默认为gStore目录下的./backups)
通过以上命令可知,在执行本地命令时需要指定备份的数据库名称,默认备份路径为gStore目录下的backups,在进行本地数据备份时建议先关闭所有API Server;,部分关键代码在Main/gbackup.cpp中,gbackup::Main函数的实现如下所示:
int main(int argc, char * argv[]) {Util util;string db_name, backup_path;// 判断参数是否合法if (argc < 2) {cout << "Invalid arguments! Input \"bin/gbackup -h\" for help." << endl;return 0;} else if (argc == 2) {// 当本地命令输入为bin/gbackup -h[--help],则输出帮助信息......} else {// 读取参数-db或--databasedb_name = Util::getArgValue(argc, argv, "db", "database");// 读取参数-p或--pathbackup_path= Util::getArgValue(argc, argv, "p", "path");// 如果备份路径参数为空,则赋值为默认备份路径./backupsif (backup_path.empty()) {backup_path = DEFALUT_BACKUP_PATH;}int len = db_name.length();// 校验数据库名称是否合法if (db_name.length() > 3 && db_name.substr(len - 3, 3) == ".db") {cout<<"your database name can not end with .db! Input \"bin/gbackup -h\" for help."<<endl;return -1;}// 判断为系统库时返回错误信息if (db_name == "system") {cout << "Your database's name can not be system." << endl;return -1;}// 校验备份路径的合法性if (backup_path == "." || backup_path == "./") {cout << "Backup Path Can not be root, Backup Failed!" << endl;return 0;}if (backup_path[backup_path.length() - 1] == '/')backup_path = backup_path.substr(0, backup_path.length() - 1);// 拼装数据库源路径:db_name.dbstring db_path = db_name + ".db";if (backup_path == "") backup_path = DEFALUT_BACKUP_PATH;long tv_begin = Util::get_cur_time();// 执行复制int ret = copy(db_path, backup_path);if (ret == 1) {cout << "Database Name Error, Backup Failed!" << endl;} else {// 复制成功后,重命名为db_name.db_时间戳string timestamp = Util::get_timestamp();backup_path = backup_path + "/" + db_path;string _backup_path = backup_path + "_" + timestamp;string sys_cmd = "mv " + backup_path + " " + _backup_path;system(sys_cmd.c_str());long tv_end = Util::get_cur_time();cout << "DB:" << db_name << " Backup Successfully! Used " << (tv_end - tv_begin) << " ms"<<endl;}return 0;}}
Main函数中调用的gbackup::copy函数如下:
/*** copy dir* @param src_path* @param dest_path* @return int*/int copy(string src_path, string dest_path){string sys_cmd;// 校验源路径是否存在,不存在则返回if(!boost::filesystem::exists(src_path)){//check the source pathcout << "Source Path Error, Please Check It Again!" << endl;return 1;}// 判断目标路径是否存在,不存在则创建改路径if(!boost::filesystem::exists(dest_path)){//check the destnation pathsys_cmd = "mkdir -p ./" + dest_path;system(sys_cmd.c_str());}// 执行复制命令sys_cmd = "cp -r " + src_path + " ./" + dest_path ;system(sys_cmd.c_str());return 0;// success}
1.2.2 接口备份
接口备份的前提需要先启动API Server,我们以ghttp服务为例,接口请求参数如下所示:
{"operation":"backup", // 操作名称"username": "root", // 用户名"password": "123456", //密码"db_name": "demo", //备份数据库名称"backup_path": "" //备份目录,若不传或为空时,默认为gStore根目录下的backups目录}
ghttp服务在接口收到备份请求时,处理逻辑过程与本地备份类似,部分核心代码为ghttp::backup_thread_new函数:
/*** @description: backup a database* @Author:liwenjie* @param {const} shared_ptr* @param {string} db_name:the operation database name* @param {string} backup_path: the backup path* @return {*}*/void backup_thread_new(const shared_ptr<HttpServer::Response>& response,string db_name,string backup_path,string ip) {string error="";string operation="backup";// 校验参数db_name是否合法error=checkparamValue("db_name",db_name);if (error.empty() == false) {sendResponseMsg(1003, error, response,ip,operation);return;}// 校验数据库是否存在if (checkdbexist(db_name) == false){error = "the database [" + db_name + "] not built yet.";sendResponseMsg(1004, error, response,ip,operation);return;}......//begin backup databasestring path=backup_path;// 如果备份路径为空,则赋值为默认备份路径./backupsif(path == "") path = BACKUP_PATH;// 校验备份路径是否合法if(path == "." ){cout << "Backup Path Can not be root or empty, Backup Failed!" << endl;string error = "Failed to backup the database. Backup Path Can not be root or empty.";sendResponseMsg(1003,error,response,ip,operation);pthread_rwlock_unlock(&(it_already_build->second->db_lock));return;}if(path[path.length() - 1] == '/') path = path.substr(0, path.length() - 1);cout<<"backup path:"<<path<<endl;string db_path = db_name + ".db";pthread_rwlock_wrlock(&databases_map_lock);// 复制文件int ret = copy(db_path, path);pthread_rwlock_unlock(&databases_map_lock);string timestamp="";if(ret == 1){cout << "Database Folder Misssing, Backup Failed!" << endl;string error = "Failed to backup the database. Database Folder Misssing.";sendResponseMsg(1005,error,response,ip,operation);pthread_rwlock_unlock(&(it_already_build->second->db_lock));return;} else{string time = Util::get_date_time();timestamp = Util::get_timestamp();cout << "Time:" + time << endl;cout << "DB:" + db_name + " Backup done!" << endl;}// 复制成功后,重命名为db_name.db_时间戳path = path + "/" + db_path;string _path = path + "_" + timestamp;string sys_cmd = "mv " + path + " " + _path;cout<<"backup command:"<<sys_cmd<<endl;system(sys_cmd.c_str());cout << "database backup done." << endl;string success = "Database backup successfully.";//返回数据......*response << "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: " << resJson.length() << "\r\n\r\n" << resJson;pthread_rwlock_unlock(&(it_already_build->second->db_lock));// 记录操作日志writeIpAccessLog(ip,operation,"backup "+db_name+" database successfully!",0);
1.3 还原
数据库的还原也是针对具体的某一个库进行数据还原,其原理是从备份路径下复制完整的文件到gStore根目录,在删除当前数据库文件后重命名备份文件为数据库文件。
1.3.1 本地还原
还原的执行命令和参数说明如下:
$ bin/grestore -db dbname -p path
# dbname: 数据库名称(不需要带.db)
# path: 备份文件路径(相对gStore根目录而言的相对路径,且需要到文件夹)
通过以上命令可知,在执行本地命令时需要指定还原的数据库名称,备份文件路径;部分关键代码在Main/grestore.cpp中,grestore::Main函数的实现如下所示:
int main(int argc, char * argv[]) {Util util;string db_name, backup_path, db_path, path;if (argc < 2) {cout << "Invalid arguments! Input \"bin/grestore -h\" for help." << endl;return 0;} else if (argc == 2) {// 当本地命令输入为bin/grestore -h[--help],则输出帮助信息......} else {// 读取参数-db或--database,并校验参数的合法性db_name = Util::getArgValue(argc, argv, "db", "database");......// 读取参数-p或--path,并校验参数的合法性backup_path = Util::getArgValue(argc, argv, "p", "path");......// 加载system库并询问是否存在待还原的库,若不存在则向system库中增加还原库的信息Database system_db("system");system_db.load();string sparql = "ASK WHERE{<" + db_name + "> <database_status> \"already_built\".}";......// 复制备份文件到gStore根目录int ret = copy(backup_path, DEFALUT_BUILD_PATH);......// 删除当前库文件db_path = db_name + ".db";string sys_cmd = "rm -rf " + db_path;system(sys_cmd.c_str());// 重命名备份库文件为db_name.dbpath = Util::get_folder_name(backup_path, db_name);sys_cmd = "mv " + path + ' ' + db_path;system(sys_cmd.c_str());return 0;}}
1.3.2 接口还原
接口还原的前提也需要先启动API Server,我们以ghttp服务为例,接口请求参数如下所示:
ghttp服务在接口收到还原请求时,处理逻辑与本地还原类似,部分核心代码为ghttp::restore_thread_new函数: {"operation":"restore", // 操作名称"username": "root", // 用户名"password": "123456", //密码"db_name": "demo", //还原数据库名称"backup_path": "" //备份文件的路径}
/*** @description:restore the database* @Author:liwenjie* @param {const} shared_ptr* @param {string} db_name the operation database name* @param {string} backup_path the backup path* @param {string} username the operation username* @param {string} ip the request ip address* @return {*}*/void restore_thread_new(const shared_ptr<HttpServer::Response>& response,string db_name,string backup_path,string username,string ip) {string error="";string operation="restore";// 校验参数db_name的合法性error=checkparamValue("db_name",db_name);......// 校验参数backup_path的合法性string path=backup_path;......string database=db_name;cout << "restore "<< database << endl;pthread_rwlock_rdlock(&already_build_map_lock);std::map<std::string, struct DBInfo *>::iterator it_already_build = already_build.find(db_name);// 判断还原的数据库是否存在,若不存在则向system库中增加还原库的信息if(it_already_build == already_build.end()){......}......// 删除当前库文件string sys_cmd = "rm -rf " + db_name + ".db";system(sys_cmd.c_str());// 复制备份文件到gStore根目录pthread_rwlock_wrlock(&databases_map_lock);int ret = copy(path, DB_PATH);pthread_rwlock_unlock(&databases_map_lock);// 判断复制是否成功if (ret == 1) {string error = "Failed to restore the database. Backup Path Error";sendResponseMsg(1005,error,response,ip,operation);cout << "Backup Path Error, Restore Failed!" << endl;pthread_rwlock_unlock(&(it_already_build->second->db_lock));return;} else {string time = Util::get_date_time();cout << "Time:" + time << endl;cout << "DB:" + db_name + " Restore done!" << endl;}// 重命名为db_name.dbpthread_rwlock_unlock(&(it_already_build->second->db_lock));path = Util::get_folder_name(path, db_name);sys_cmd = "mv " + path + " " + db_name + ".db";system(sys_cmd.c_str());// 返回数据cout << "database restore done." << endl;string success = "Database ["+db_name+"] restore successfully.";sendResponseMsg(0,success,response,ip,operation);}
1.4 小结
本章节介绍了备份与恢复的机制和两种实现方式,建议在阅读的同时结合源码Main/gbackup.cpp、Main/grestore.cpp、Main/ghttp.cpp一起分析,会更容易理解。接下来我们拟将分析gstore的RDF解析机制。






