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


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=root
password='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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值