一.前言
最近server服务隔个两天就出现"卡死"的现象, 必须要手动重启服务, 才能正常使用; 后来各种查日志, 查百度,可以复现问题, 主要原因是Hikari数据库连接池中的连接用完了, 其他Client 访问Server中的API时候, 一直在等待Hikari连接, 出现了超时的现象;
只能通过暴力重启server服务,释放连接,API接口才能正常访问; 百度了一圈,解决这种问题方案总结如下:
1.增加服务器硬件配置, 从而增大Hikari pool size , 优化Hikari参数配置, 参考HikariCP 连接池配多大合适
2.Server API接口优化, 优化sql语句,限制sql语句查询条件,比如time, limit,等; 限制接口频繁调用等等;
3.Server服务负载均衡,并发优化等(好高端,目前还没有涉及到)
4.服务器自己检测自己API接口,出现超时,自动重启服务器
接下来这篇文章,就讲讲JAVA服务怎么使用linux命令重启自身服务,中间遇到了多少坑,流下了多少猿泪!
二.linux执行重启java服务脚本
由于对linux脚本不是很熟悉, 在网上加了一个qq群, 感谢群里的"小科"大神, 帮我写了一个重启脚本, 还帮我分析了一下午问题,最后按照"小科"大神提供的线索"进程"问题, 顺藤摸瓜, 终于解决问题! 在此感谢素昧相识的"小科"大神, 好人一生平安!
将此脚本文件放在服务器文件夹中, 最好放在你程序的目录中, 这样不会出现环境,路径的问题;
restart.sh
#!/bin/bash
#Time: 2018-12-21 13:36:19
PID=`netstat -nlpt|grep -w "8082"|awk '{print $7}'|grep -oE "[0-9]+"`
if [ -z ${PID} ];then
echo "进程不存在!开始重新启动。。"
nohup java -Dlog4j.configurationFile=log4j2.xml -Dvertx.disableDnsResolver=true -Dio.vertx.ext.auth.prng.algorithm=NativePRNGNonBlocking -jar mx-appserver-1.1-fat.jar -conf config.json & 2>&1
else
echo "开始结束${PID}进程,重新启动。。。"
kill -9 ${PID}
nohup java -Dlog4j.configurationFile=log4j2.xml -Dvertx.disableDnsResolver=true -Dio.vertx.ext.auth.prng.algorithm=NativePRNGNonBlocking -jar mx-appserver-1.1-fat.jar -conf config.json & 2>&1
fi
三.Java程序中执行linux命令,调用restart.sh脚本,重启自身
参考链接在java程序中开启另一个java程序
参考链接Java执行shell遇到的各种问题
方法一:可以正常执行linux restart.sh脚本文件 ,但是日志文件不能持续输出和保存
主要运用 Runtime.getRuntime().exec(command) 执行linux命令
private void executeCmd() {
try {
String cmd = "restart.sh";
//String[] command = {"/bin/sh", "-c", cmd};
String[] command = {"/bin/sh", cmd};
//String[] command = {"/bin/nohup","sh", cmd, "&"};
System.out.println("sout服务器重启response data:" +Arrays.toString(command));
_LOG.info("服务器重启request data:{}" , Arrays.toString(command));
Process ps = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
String result = sb.toString();
System.out.println("sout服务器重启response data:" + result);
_LOG.error("服务器重启response data:" + result);
} catch (IOException e) {
_LOG.error("execute(Tuple, BasicOutputCollector)", e);
e.printStackTrace();
}
}
方法二: 可以正常执行linux restart.sh脚本文件 ,但是日志文件不能持续输出和保存
主要运用 ProcessBuilder pb = new ProcessBuilder("nohup","sh","restart.sh","&");执行linux命令
private void executeCmd2() {
try {
String cmd = "restart.sh";
//String[] command = {"/bin/sh", "-c", cmd};
String[] command = {"/bin/sh", cmd};
//String[] command = {"/bin/nohup","sh", cmd, "&"};
System.out.println("sout服务器重启response data:" +Arrays.toString(command));
_LOG.info("服务器重启request data:{}" , Arrays.toString(command));
ProcessBuilder pb = new ProcessBuilder("nohup","sh","restart.sh","&");
pb.start();
} catch (IOException e) {
_LOG.error("execute(Tuple, BasicOutputCollector)", e);
e.printStackTrace();
}
}
方法三: 可以正常执行linux restart.sh脚本文件 ,日志文件nohup.out可以正常输出和保存
主要涉及到linux程序进程问题 File是你的日志文件
private void executeCmd3() {
// 不使用Runtime.getRuntime().exec(command)的方式,因为无法设置以下特性
// Java执行本地命令是启用一个子进程处理,默认情况下子进程与父进程I/O通过管道相连(默认ProcessBuilder.Redirect.PIPE)
// 当服务执行自身重启的命令时,父进程关闭导致管道连接中断,将导致子进程也崩溃,从而无法完成后续的启动
// 解决方式,(1)设置子进程IO输出重定向到指定文件;(2)设置属性子进程的I/O源或目标将与当前进程的相同,两者相互独立
try {
File file = null;
ProcessBuilder pb = new ProcessBuilder("sh","restart.sh");
if (file == null || !file.exists()) {
// 设置属性子进程的I/O源或目标将与当前进程的相同,两者相互独立
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
} else {
// 设置子进程IO输出重定向到指定文件
// 错误输出与标准输出,输出到一块
pb.redirectErrorStream(true);
// 设置输出日志
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(file));
}
// 执行命令进程
pb.start();
} catch (IOException e) {
e.printStackTrace();
}
}
最后采取的是方法三,可以正常重启自己,并且日志正常写入和输出!
每个项目的环境及需求不一样,所以此篇文章只是记录我的环境下的解决方法, 大家可以参考, 万变不离其宗!