前言
目前需要做一个功能,就是通过java进行mysqldump备份,但是mysql时运行在容器中的时候,
已知Java目前只有一个Runtime.getRuntime()一种方式能获得当前linux运行时环境的方式
所以接下来就是通过这个方式实现这个功能
遇到的问题一:process.waitFor()一直阻塞不返回
代码:
下面是实现代码:
public void backup(HttpServletResponse reponse){
BufferedReader reader;
try{
String cmdStr = "docker exec mysql sh -c 'mysqldump -h 127.0.0.1 -uroot -p123456 database --ser-charset=UTF-8'";
// 回去结果
int exitCode=process.waitFor();
Process exec = Runtime.getRuntime.exec(cmdStr);
reader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line=null;
while((line=reader.readLine())!=null){
System.out.println(line);
}
if (exitCode != 0){
log.error("错误")
}
}
}
原因:
process.waitFor()方法和exitValue()方法很像,
但是exitValue()是直接获得一个返回结果,但是有可能命令还在执行中,因此结果不一定正确;
而waitFor()方法是等待进程结束后才会有返回值。
因此waitfor方法是会被阻塞的,同时因为因为输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流出现失败,当缓冲区满之后将无法继续写入数据,则可能导致子进程阻塞
因此我们需要不断的将缓冲区的内容读取出来就了以防止阻塞,所以process.waitFor()应该换个位置
改进代码如下:
public void backup(HttpServletResponse reponse){
BufferedReader reader;
try{
String cmdStr = "docker exec mysql sh -c 'mysqldump -h 127.0.0.1 -uroot -p123456 database --ser-charset=UTF-8'";
Process exec = Runtime.getRuntime.exec(cmdStr);
reader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line=null;
while((line=reader.readLine())!=null){
System.out.println(line);
}
// 回去结果
int exitCode=process.waitFor();
if (exitCode != 0){
log.error("错误")
}
}
}
遇到的问题二:process.waitFor()返回值为127
查明原因127对应的应该是没有环境导致的,
因此在使用Process 的过程中推荐使用绝对路径,就算docker也一样
代码改进如下:
public void backup(HttpServletResponse reponse){
BufferedReader reader;
try{
String cmdStr = "/usr/bin/docker exec mysql sh -c 'mysqldump -h 127.0.0.1 -uroot -p123456 database --ser-charset=UTF-8'";
Process exec = Runtime.getRuntime.exec(cmdStr);
reader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line=null;
while((line=reader.readLine())!=null){
System.out.println(line);
}
// 回去结果
int exitCode=process.waitFor();
if (exitCode != 0){
log.error("错误")
}
}
}
遇到的问题三:process.waitFor()返回值为1
返回值为1的原因大概是因为执行的语句有问题,但是我将该语句放到linux环境中执行却能过得到正常结果
后面简单的百度一下并且看了一下源码:
发现Runtime.exec()是存在两种字符参数的
分别是:
- 1、Runtime.exec(String command)
- 2、Runtime.exec(String[] cmdarray)
但是第1种方法的本质是Runtime.exec(command.split(" "))
看下代码,可以见到我们通过linux执行docker命令,在通过docker 执行容器命令,简单的空格已经不能支持这么复杂的逻辑了,其实一些包含特殊字符的也不能进行执行,所以我们需要使用第二种方式,手动将命令分层
改进代码如下:
public void backup(HttpServletResponse reponse){
BufferedReader reader;
try{
//String cmdStr = " mysqldump -h 127.0.0.1 -uroot -p123456 database --ser-charset=UTF-8";
//String cmdStr = "docker exec mysql sh -c 'mysqldump -h 127.0.0.1 -uroot -p123456 database --ser-charset=UTF-8'";
String[] cmdStr = {"/usr/bin/docker","exec","mysql","sh","-c","/usr/bin/mysqldump -h 127.0.0.1 -uroot -p123456 database --ser-charset=UTF-8"}
Process exec = Runtime.getRuntime.exec(cmdStr);
reader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line=null;
while((line=reader.readLine())!=null){
System.out.println(line);
}
int exitCode=process.waitFor();
if (exitCode != 0){
log.error("错误")
}
}
}