系统备份还原

在很多时候,我们需要系统数据进行备份还原。我们这里就使用MySql的备份还原命令实现系统备份还原的功能。

新建工程

新建一个maven项目,并添加相关依赖,可以用Spring boot脚手架生成。

新建 kitty-bakcup 工程,这是一个独立运行于后台系统的应用程序,可以分开部署。

pom.xml 文件添加相关依赖。

        <!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>

添加Spring boot启动类。

package com.louis.kitty.backup;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication(scanBasePackages={"com.louis.kitty"})
public class KittyBackupApplication { public static void main(String[] args) {
SpringApplication.run(KittyBackupApplication.class, args);
}
}

添加配置

创建项目配置文件,添加备份还原数据源配置。

resources/application.yml

# backup datasource
spring:
backup:
datasource:
host: localhost
userName: root
password: 123456
database: kitty

添加配置属性读取配置类。

BackupDataSourceProperties.java

package com.louis.kitty.backup.datasource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; @Component
@ConfigurationProperties(prefix = "spring.backup.datasource")
public class BackupDataSourceProperties { private String host;
private String userName;
private String password;
private String database;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
} }

添加swagger配置类,用于测试备份还原接口。

package com.louis.kitty.backup.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration
@EnableSwagger2
public class SwaggerConfig { @Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).build();
} }

添加跨域配置类,因为前后端分离,跨域肯定是要支持的。

package com.louis.kitty.backup.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration
public class CorsConfig implements WebMvcConfigurer { @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许跨域访问的路径
.allowedOrigins("*") // 允许跨域访问的源
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") // 允许请求方法
.maxAge(168000) // 预检间隔时间
.allowedHeaders("*") // 允许头部设置
.allowCredentials(true); // 是否发送cookie
}
}

备份还原逻辑

备份还原逻辑封装在一个工具类中,可以单独从项目中提取出来,方便复用。

内部main方法提供简单实用示例,可以方便快速的知晓实用方法。

备份还原功能主要是借助命令行执行MySql的数据备份还原命令实现的。

package com.louis.kitty.backup.util;

import java.io.File;
import java.io.IOException; /**
* MySQL备份还原工具类
* @author Louis
* @date Sep 21, 2018
*/
public class MySqlBackupRestoreUtils { /**
* 备份数据库
* @param host host地址,可以是本机也可以是远程
* @param userName 数据库的用户名
* @param password 数据库的密码
* @param savePath 备份的路径
* @param fileName 备份的文件名
* @param databaseName 需要备份的数据库的名称
* @return
* @throws IOException
*/
public static boolean backup(String host, String userName, String password, String backupFolderPath, String fileName,
String database) throws Exception {
File backupFolderFile = new File(backupFolderPath);
if (!backupFolderFile.exists()) {
// 如果目录不存在则创建
backupFolderFile.mkdirs();
}
if (!backupFolderPath.endsWith(File.separator) || !backupFolderPath.endsWith("/")) {
backupFolderPath = backupFolderPath + File.separator;
}
// 拼接命令行的命令
String backupFilePath = backupFolderPath + fileName;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("mysqldump --opt ").append(" --add-drop-database ").append(" --add-drop-table ");
stringBuilder.append(" -h").append(host).append(" -u").append(userName).append(" -p").append(password);
stringBuilder.append(" --result-file=").append(backupFilePath).append(" --default-character-set=utf8 ").append(database);
// 调用外部执行 exe 文件的 Java API
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
if (process.waitFor() == 0) {
// 0 表示线程正常终止
System.out.println("数据已经备份到 " +backupFilePath + " 文件中");
return true;
}
return false;
} /**
* 还原数据库
* @param restoreFilePath 数据库备份的脚本路径
* @param host IP地址
* @param database 数据库名称
* @param userName 用户名
* @param password 密码
* @return
*/
public static boolean restore(String restoreFilePath, String host, String userName, String password, String database)
throws Exception {
File restoreFile = new File(restoreFilePath);
if (restoreFile.isDirectory()) {
for (File file : restoreFile.listFiles()) {
if (file.exists() && file.getPath().endsWith(".sql")) {
restoreFilePath = file.getAbsolutePath();
break;
}
}
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("mysql -h").append(host).append(" -u").append(userName).append(" -p").append(password);
stringBuilder.append(" ").append(database).append(" < ").append(restoreFilePath);
try {
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
if (process.waitFor() == 0) {
System.out.println("数据已从 " + restoreFilePath + " 导入到数据库中");
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
} private static String[] getCommand(String command) {
String os = System.getProperty("os.name");
String shell = "/bin/sh";
if(os.toLowerCase().startsWith("win")){
shell = "cmd";
}
String[] cmd = { shell, "/c", command };
return cmd;
} public static void main(String[] args) throws Exception {
String host = "localhost";
String userName = "root";
String password = "123456";
String database = "kitty"; System.out.println("开始备份");
String backupFolderPath = "c:/dev/";
String fileName = "kitty.sql";
backup(host, userName, password, backupFolderPath, fileName, database);
System.out.println("备份成功"); System.out.println("开始还原");
String restoreFilePath = "c:/dev/kitty.sql";
restore(restoreFilePath, host, userName, password, database);
System.out.println("还原成功"); }
}

备份还原服务

备份还原服务通过调用工具类实现备份还原功能。

MysqlBackupService.java

package com.louis.kitty.backup.service;

import java.io.IOException;

/**
* MySql命令行备份恢复服务
* @author Louis
* @date Sep 20, 2018
*/
public interface MysqlBackupService { /**
* 备份数据库
* @param host host地址,可以是本机也可以是远程
* @param userName 数据库的用户名
* @param password 数据库的密码
* @param savePath 备份的路径
* @param fileName 备份的文件名
* @param databaseName 需要备份的数据库的名称
* @return
* @throws IOException
*/
boolean backup(String host, String userName, String password, String backupFolderPath, String fileName, String database) throws Exception; /**
* 恢复数据库
* @param restoreFilePath 数据库备份的脚本路径
* @param host IP地址
* @param database 数据库名称
* @param userName 用户名
* @param password 密码
* @return
*/
boolean restore(String restoreFilePath, String host, String userName, String password, String database) throws Exception; }

MysqlBackupServiceImpl.java

package com.louis.kitty.backup.service.impl;

import org.springframework.stereotype.Service;

import com.louis.kitty.backup.service.MysqlBackupService;
import com.louis.kitty.backup.util.MySqlBackupRestoreUtils; @Service
public class MysqlBackupServiceImpl implements MysqlBackupService { @Override
public boolean backup(String host, String userName, String password, String backupFolderPath, String fileName,
String database) throws Exception {
return MySqlBackupRestoreUtils.backup(host, userName, password, backupFolderPath, fileName, database);
} @Override
public boolean restore(String restoreFilePath, String host, String userName, String password, String database)
throws Exception {
return MySqlBackupRestoreUtils.restore(restoreFilePath, host, userName, password, database);
} }

备份还原接口

备份还原服务通过调用服务类实现备份还原的REST接口。

提供备份查询、创建备份、版本还原、删除备份的功能。

MySqlBackupController.java

package com.louis.kitty.backup.controller;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import com.louis.kitty.backup.constants.BackupConstants;
import com.louis.kitty.backup.datasource.BackupDataSourceProperties;
import com.louis.kitty.backup.service.MysqlBackupService;
import com.louis.kitty.backup.util.FileUtils;
import com.louis.kitty.backup.util.HttpResult; /**
* 系统数据备份还原
* 采用MYSQL备份还原命令
* @author Louis
* @date Sep 20, 2018
*/
@RestController
@RequestMapping("/backup")
public class MySqlBackupController { @Autowired
MysqlBackupService mysqlBackupService;
@Autowired
BackupDataSourceProperties properties; @GetMapping("/backup")
public HttpResult backup() {
String host = properties.getHost();
String userName = properties.getUserName();
String password = properties.getPassword();
String database = properties.getDatabase();
String backupFodlerName = BackupConstants.DEFAULT_BACKUP_NAME+ "_" + (new SimpleDateFormat(BackupConstants.DATE_FORMAT)).format(new Date());
String backupFolderPath = BackupConstants.BACKUP_FOLDER + backupFodlerName + File.separator;
String fileName = BackupConstants.BACKUP_FILE_NAME;
try {
mysqlBackupService.backup(host, userName, password, backupFolderPath, fileName, database);
} catch (Exception e) {
return HttpResult.error(500, e.getMessage());
}
return HttpResult.ok();
} @GetMapping("/restore")
public HttpResult restore(@RequestParam String name) {
String host = properties.getHost();
String userName = properties.getUserName();
String password = properties.getPassword();
String database = properties.getDatabase();
String restoreFilePath = BackupConstants.RESTORE_FOLDER + name;
try {
mysqlBackupService.restore(restoreFilePath, host, userName, password, database);
} catch (Exception e) {
return HttpResult.error(500, e.getMessage());
}
return HttpResult.ok();
} @GetMapping("/findRecords")
public HttpResult findBackupRecords() {
List<Map<String, Object>> backupRecords = new ArrayList<>();
File restoreFolderFile = new File(BackupConstants.RESTORE_FOLDER);
if(restoreFolderFile.exists()) {
for(File file:restoreFolderFile.listFiles()) {
Map<String, Object> bean = new HashMap<>();
bean.put("name", file.getName());
bean.put("title", file.getName());
if(BackupConstants.DEFAULT_BACKUP_NAME.equals(file.getName())) {
bean.put("title", "系统默认备份");
}
backupRecords.add(bean);
}
}
return HttpResult.ok(backupRecords);
} @GetMapping("/delete")
public HttpResult deleteBackupRecord(@RequestParam String name) {
if(BackupConstants.DEFAULT_BACKUP_NAME.equals(name)) {
return HttpResult.error("系统默认备份无法删除!");
}
String restoreFilePath = BackupConstants.RESTORE_FOLDER + name;
try {
FileUtils.deleteFile(new File(restoreFilePath));
} catch (Exception e) {
return HttpResult.error(500, e.getMessage());
}
return HttpResult.ok();
} }

接口测试

启动应用,访问 http://localhost:8080/swagger-ui.html#/ ,测试Swagger接口。

创建备份

调用备份接口,生成备份。

备份创建成功之后,会在_backup目录下生成以时间戳相关的备份目录,目录下包含数据库备份SQL文件。

为防止所有备份被删除,backup目录下提供系统默认备份,放置系统初始化数据,通过删除接口,不可删除。

查找备份

通过备份查询接口,可以查询所有备份记录,显示在前台,用于提供备份的还原和删除。

备份查询接口返回如下数据结构,name为操作标识,title用于前台显示备份信息。

{
"code": 200,
"msg": null,
"data": [
{
"name": "backup",
"title": "系统默认备份"
},
{
"name": "backup_2018-09-22_103504",
"title": "backup_2018-09-22_103504"
},
{
"name": "backup_2018-09-22_103506",
"title": "backup_2018-09-22_103506"
},
{
"name": "backup_2018-09-22_103508",
"title": "backup_2018-09-22_103508"
}
]
}

删除备份

根据查询结果,传入备份的name作为参数,即可调用删除接口删除备份。

还原备份

根据查询结果,传入备份的name作为参数,即可调用还原接口还原备份。

前台测试

结合前台页面操作,我们可以以界面的方式查询、创建、删除和还原备份。

我们在前台页面添加备份还原操作入口,如下图所示。

在系统备份还原操作界面,提供查询、创建、删除、还原备份的功能。

前台页面的实现参考前台篇章教程。

规则调整

由于我们用的是Spring Boot,在实际部署的时候,很可能采用的是jar包部署方式,那么我们原先定的以类路径来定位读写备份目录和文件的方式受到了限制,所以这里对备份目录存放的位置重新制定。新的存放路径规则:System.getProperty("user.home") + /backup_时间戳/kitty.sql ,user.home 在windows对应C:\Users\xxxx\,在Linux上对应/root/。

注意:

此版本数据库备份还原功能依赖于服务器本地MySQL的备份还原工具,所以部署服务器必须先安装MySQL数据库。

且要保证在命令行能够成功执行MySQL的备份还原数据库命令,这是系统数据备份还原功能的前提条件。

如果是在Linux环境,且执行MySQL相关命令报出以下错误,可参考下面解决方案:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

这是因为本地MySQL工具一本都是通过Socket方式连接数据库的,出现以上错误是没有为相应工具指定Socker文件。

可以通过修改MySQL的 my.cnf 配置文件,在其中指定客户端工具的socket文件 , 默认是 /var/lib/mysql/mysql.sock。

my.cnf 添加如下内容:

[client]
socket=/var/lib/mysql.sock

源码下载

后端:https://gitee.com/liuge1988/kitty

前端:https://gitee.com/liuge1988/kitty-ui.git


作者:朝雨忆轻尘
出处:https://www.cnblogs.com/xifengxiaoma/
版权所有,欢迎转载,转载请注明原文作者及出处。

Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十三):系统备份还原的更多相关文章

  1. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(一):Kitty 系统介绍

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 温馨提示: 有在演示环境删除数据的童鞋们,如果可以的话,麻烦动动小指,右键头像 ...

  2. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十五):Spring Security 版本

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 技术背景 到目前为止,我们使用的权限认证框架是 Shiro,虽然 Shiro ...

  3. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十三):配置中心(Config、Bus)

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 技术背景 如今微服务架构盛行,在分布式系统中,项目日益庞大,子项目日益增多,每 ...

  4. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十):服务熔断(Hystrix、Turbine)

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 雪崩效应 在微服务架构中,由于服务众多,通常会涉及多个服务层级的调用,而一旦基 ...

  5. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十九):服务消费(Ribbon、Feign)

    技术背景 上一篇教程中,我们利用Consul注册中心,实现了服务的注册和发现功能,这一篇我们来聊聊服务的调用.单体应用中,代码可以直接依赖,在代码中直接调用即可,但在微服务架构是分布式架构,服务都运行 ...

  6. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十八):注册中心(Spring Cloud Consul)

    什么是 Consul Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与 ...

  7. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十六):容器部署项目

    容器部署项目 这一章我们引入docker,采用docker容器的方式部署我们的项目. 首先需要有一个linux环境,并且安装 java 和 maven 以及 docker 环境,这个教程多如牛毛,不再 ...

  8. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十五):系统服务监控

    系统服务监控 新建监控工程 新建Spring Boot项目,取名 kitty-monitor,结构如下. 添加项目依赖 添加 spring boot admin 的相关依赖. pom.xml < ...

  9. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十四):项目打包部署

    项目打包部署 安装MySQL镜像 注意:如果使用docker镜像安装MySQL,也需要在前端部署主机安装MySQL,因为备份还原功能是使用MySQL的本地命令进行操作的. 下载镜像 执行以下命令,拉取 ...

随机推荐

  1. Linux驱动之中断处理体系结构简析

    S3C2440中的中断处理最终是通过IRQ实现的,在Linux驱动之异常处理体系结构简析已经介绍了IRQ异常的处理过程,最终分析到了一个C函数asm_do_IRQ,接下来继续分析asm_do_IRQ, ...

  2. Linux top命令中CPU信息的详解(转)

    add by zhj: 下面的文章解释的很好了,这里再说明一下top命令中wa的含义,我们知道,当IO阻塞时,操作系统会把进程改为阻塞态,将CPU调度到运行其它进程. CPU在空闲状态下,会检查是否有 ...

  3. 使用虚拟机VM12安装REHL7

    转载https://blog.csdn.net/qq_19467623/article/details/52869108 转载http://www.07net01.com/2016/03/141198 ...

  4. google protobuf VC下的使用笔记

    1 使用protobuf 2.x 下载地址(3.x 在c++11 vs2017下报错) 源码 https://github.com/google/protobuf 或者直接下载 二进制文件 2 如果下 ...

  5. selenium实现淘宝的商品爬取

    一.问题 本次利用selenium自动化测试,完成对淘宝的爬取,这样可以避免一些反爬的措施,也是一种爬虫常用的手段.本次实战的难点: 1.如何利用selenium绕过淘宝的登录界面 2.获取淘宝的页面 ...

  6. mui.fire()触发自定义事件

    导读:添加自定义事件监听操作和标准js事件监听类似,可直接通过window对象添加,通过mui.fire()方法可触发目标窗口的自定义事件. 监听自定义事件 添加自定义事件监听操作和标准js事件监听类 ...

  7. 2019.02.21 bzoj2829: 信用卡凸包(凸包)

    传送门 题意:给nnn个A∗BA*BA∗B的矩形,其中每个矩形的四个角被改造成了半径为rrr的四分之一 圆,问这些矩形的凸包周长. 思路:考虑求出圆心的凸包周长然后加上一个整圆的周长,证明很简单,略掉 ...

  8. 字符串相似度算法——Levenshtein Distance算法

    Levenshtein Distance 算法,又叫 Edit Distance 算法,是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一 ...

  9. 源码解读Linux的limits.conf文件

    目录 目录 1 1. 前言 1 2. PAM 2 3. pam_limits 2 4. limits.conf的由来 3 5. 模块入口函数 4 6. 解析limits.conf 6 7. 生效lim ...

  10. QT在Linux下的安装

    QT是一个跨平台的C++开发库,设计思想是同样的,C++无需修改就可以在windows.linux.macOS等平台上使用,他使开发更专注于构建软件的核心价值,而不是维护API.作为面向对象的框架,它 ...