Java通过Runtime类对系统命令的调用,备份数据库和图片示例

来源:互联网 发布:京东数据开放平台 编辑:程序博客网 时间:2024/05/16 02:24

  • Java调用系统命令
  • 示例备份数据库和文件夹


Java调用系统命令

我尝试过两种系统命令调用方式:一种是JNI,可以对系统接口进行对接,有点像Java→C++→Windows API,更底层,但这里不赘述;一种是通过Runtime调用系统命令。

先看一下Runtime的初始化源码

public class Runtime {    private static Runtime currentRuntime = new Runtime();    …………    public static Runtime getRuntime() {        return currentRuntime;    }}

通过上述源码可以发现Runtime是很典型的饿汉单例,通过getRuntime()获取唯一的Runtime实例:Runtime runtime = Runtime.getRuntime(),通过runtime.exec()执行相关命令。

runtime.exec()本身可能抛出IOException异常,而且参数很杂:
runtime .exec()
一般的,第一个参数是命令,第二个参数是变量或命令参数,第三个变量是要执行命令的目录。
文档:https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html

举例:
执行一个简单的命令,比如zip压缩:

runtime.exec("zip -r compressed.zip /www/picture");

执行带有参数的命令,比如备份数据库到文件

String[] paramArr = {" > /www/backup/myblog.sql"};runtime.exec("/usr/bin/mysqldump -u root -p pwd dbname", paramArr);

在某个目录执行带有参数的命令

String[] paramArr = {" > /www/backup/myblog.sql"};runtime.exec("mysqldump -u root -p pwd dbname", paramArr, new File("/usr/bin/"));

runtime.exec()会返回Process对象,用于对该命令所产生进程的控制。
文档:https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html


示例备份数据库和文件夹

mysql和mysqldump在5.6后更新了有关密码的命令行策略,凡是含有明文密码的命令全部给出警告。通过runtime.exec()的第二个参数可以设置后续输入的非明文密码,但如果密码中存在像”#”这类的特殊字符,可能会提示错误密码错误。所以建议将mysql的密码写在my.cnf中。

通过mysqldump --help命令可知cnf文件的优先级分别为:
/etc/my.cnf、/etc/mysql/my.cnf、/usr/etc/my.cnf、~/.my.cnf
所以在/etc/my.cnf的最后添加以下字段:

[mysqldump]user=rootpassword='pwd'

主要代码如下,省略了部分属性:

import java.io.File;import java.io.IOException;import java.util.concurrent.TimeUnit;public class BackupService {    private Thread mysqlBackup;    private Thread pictureBackup;    //内部类对Runnable接口进行继承,并重写run(),方便以后扩展    private class MysqlRunnable implements Runnable {        @Override        public void run() {            Runtime runtime = Runtime.getRuntime();            //备份数据库语句,由于在my.cnf中设置了用户名和密码,所以在命令中无需输入            String command = "mysqldump dbname --result-file=/www/backup/db.sql";            try {                Process process = runtime.exec(command);                /*Process的waitFor的作用是阻塞当前进程,直到process的进程运行完毕,可能会抛出InterruptedException异常。                waitFor有两种方法,一种是不带超时设置的,一种是像下面这样带超时设置的。                第一个参数是超时时间,第二个参数是TimeUnit的时间单位。                超时后,waitFor会停止阻塞并返回false */                process.waitFor(Long.parseLong(maxTimeStr), TimeUnit.SECONDS);            } catch (IOException | InterruptedException e) {                e.printStackTrace();            }        }    }    private class PictureRunnable implements Runnable {        @Override        public void run() {            Runtime runtime = Runtime.getRuntime();            //使用zip压缩图片文件夹            String command = "zip -r compressed.zip /www/picture";            try {                Process process = runtime.exec(command);                process.waitFor(Long.parseLong(maxTimeStr), TimeUnit.SECONDS);            } catch (IOException | InterruptedException e) {                e.printStackTrace();            }        }    }    //加了锁,配合thread.isAlive(),防止线程多开    public synchronized Boolean backupMysql() {        if (this.mysqlBackup == null || !this.mysqlBackup.isAlive()) {            //备份属于小业务,多余的线程就让GC去处理吧            this.mysqlBackup = new Thread(new MysqlRunnable());            //判断并创建备份文件的路径上的未创建的文件夹            File directory = new File(backupPath + mysqlBackupFileName).getParentFile();            if (directory.exists() || directory.mkdirs()) {                this.mysqlBackup.start();            }        }        //返回当前线程状态。除非父子线程的优先级差距很大,否则一般都是true        return this.mysqlBackup.isAlive();    }    public synchronized Boolean backupPicture() {        if (this.pictureBackup == null || !this.pictureBackup.isAlive()) {            this.pictureBackup = new Thread(new PictureRunnable());            File directory = new File(backupPath + pictureBackupFileName).getParentFile();            if (directory.exists() || directory.mkdirs()) {                this.pictureBackup.start();            }        }        return this.pictureBackup.isAlive();    }}
阅读全文
0 0