修复程序使用脚本启动后的控制台交互问题。
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
#!/bin/bash
|
||||
# Termux专属启动脚本 编译+启动 解决exit后卡空行/需手动^C/参数误判问题
|
||||
# 优化:分离日志输出、保留终端交互、增强异常处理、适配新版参数解析
|
||||
# 优化:移除命令行日志重定向,完全依赖Java LogUtils输出日志、保留终端交互、增强异常处理
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")" || { echo "目录切换失败"; exit 1; }
|
||||
BASE_DIR=$(cd .. && pwd)
|
||||
LOG_DIR="$BASE_DIR/logs"
|
||||
MAIN_LOG="$LOG_DIR/main.log"
|
||||
|
||||
# 1. 前置准备:创建日志目录
|
||||
# 1. 前置准备:创建日志目录(仅给Java LogUtils用,脚本不再写日志)
|
||||
mkdir -p "$LOG_DIR" || { echo "日志目录创建失败"; exit 1; }
|
||||
|
||||
# 2. 编译校验
|
||||
@@ -17,10 +17,9 @@ if [ ! -f "$BUILD_SH" ]; then
|
||||
fi
|
||||
bash "$BUILD_SH" || { echo "编译失败退出"; exit 1; }
|
||||
|
||||
# 3. 类路径配置(修复:指定完整包名主类)
|
||||
# 3. 类路径配置(指定完整包名主类)
|
||||
CLASSPATH="$BASE_DIR/runtime"
|
||||
[ -d "$BASE_DIR/libs" ] && CLASSPATH="$CLASSPATH:$BASE_DIR/libs/*"
|
||||
# 关键:Main类带完整包名,避免类找不到或参数解析异常
|
||||
MAIN_CLASS="cc.winboll.Main"
|
||||
|
||||
# 4. JVM优化配置(适配Termux低内存环境)
|
||||
@@ -39,24 +38,23 @@ on_exit() {
|
||||
kill -9 $JAVA_PID 2>/dev/null
|
||||
fi
|
||||
stty sane
|
||||
echo -e "✅ 服务已停止,日志文件:$MAIN_LOG"
|
||||
echo -e "✅ 服务已停止,Java日志文件:$MAIN_LOG"
|
||||
exit 1
|
||||
}
|
||||
# 捕获更多退出信号
|
||||
trap 'on_exit' SIGINT SIGTERM SIGABRT SIGSEGV SIGILL
|
||||
|
||||
# 6. 启动服务(核心修改:分离日志,保留终端stdin)
|
||||
# 6. 启动服务(核心修改:移除所有日志重定向,stdout/stderr直接走终端)
|
||||
stty -echoctl # 屏蔽^C视觉印记
|
||||
echo -e "🚀 服务启动中,输入help查指令,输入exit优雅停机"
|
||||
echo -e "📝 日志输出路径:$MAIN_LOG\n"
|
||||
echo -e "📝 Java日志输出路径:$MAIN_LOG\n"
|
||||
|
||||
# 关键:stdout/stderr 追加到日志,但保留stdin给程序交互
|
||||
java -cp "$CLASSPATH" "$MAIN_CLASS" -v -log:ALL >> "$MAIN_LOG" 2>&1 &
|
||||
# 关键:无任何重定向,让Java的LogUtils负责写日志,终端保留完整交互能力
|
||||
java -cp "$CLASSPATH" "$MAIN_CLASS" -v -log:ALL
|
||||
JAVA_PID=$!
|
||||
EXIT_CODE=$?
|
||||
|
||||
# 7. 等待Java进程结束(解决卡空行问题)
|
||||
wait $JAVA_PID
|
||||
EXIT_CODE=$?
|
||||
|
||||
# 8. 后置处理
|
||||
stty sane
|
||||
@@ -64,7 +62,8 @@ if [ $STOP_SIGNAL -eq 0 ]; then
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "\n✅ 服务优雅停机完成"
|
||||
else
|
||||
echo -e "\n❌ 服务异常退出,退出码:$EXIT_CODE,查看日志:$MAIN_LOG"
|
||||
echo -e "\n❌ 服务异常退出,退出码:$EXIT_CODE,查看Java日志:$MAIN_LOG"
|
||||
fi
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
|
||||
|
||||
@@ -1,40 +1,69 @@
|
||||
#!/bin/bash
|
||||
# Termux专属启动脚本 编译+启动 解决exit后卡空行/需手动^C问题
|
||||
# 优化:增加JVM内存限制、进程异常捕获、日志输出
|
||||
# Termux专属启动脚本 编译+启动 解决exit后卡空行/需手动^C/参数误判问题
|
||||
# 优化:移除命令行日志重定向,完全依赖Java LogUtils输出日志、保留终端交互、增强异常处理
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")" || { echo "目录切换失败"; exit 1; }
|
||||
BASE_DIR=$(cd .. && pwd)
|
||||
LOG_DIR="$BASE_DIR/logs"
|
||||
MAIN_LOG="$LOG_DIR/main.log"
|
||||
|
||||
# 1. 编译校验
|
||||
# 1. 前置准备:创建日志目录(仅给Java LogUtils用,脚本不再写日志)
|
||||
mkdir -p "$LOG_DIR" || { echo "日志目录创建失败"; exit 1; }
|
||||
|
||||
# 2. 编译校验
|
||||
BUILD_SH="$BASE_DIR/bash/build_class.sh"
|
||||
if [ ! -f "$BUILD_SH" ]; then
|
||||
echo "编译脚本不存在:$BUILD_SH"
|
||||
exit 1
|
||||
fi
|
||||
bash "$BUILD_SH" || { echo "编译失败退出"; exit 1; }
|
||||
|
||||
# 2. 类路径配置
|
||||
# 3. 类路径配置(指定完整包名主类)
|
||||
CLASSPATH="$BASE_DIR/runtime"
|
||||
[ -d "$BASE_DIR/libs" ] && CLASSPATH="$CLASSPATH:$BASE_DIR/libs/*"
|
||||
MAIN_CLASS="Main"
|
||||
MAIN_CLASS="cc.winboll.Main"
|
||||
|
||||
# 3. 关键优化:JVM内存限制(避免内存耗尽)+ 异常日志输出
|
||||
export _JAVA_OPTIONS="-Xmx128m -Xms64m" # 限制最大堆内存128M,初始堆64M,适配Termux
|
||||
# 4. JVM优化配置(适配Termux低内存环境)
|
||||
export _JAVA_OPTIONS="-Xmx128m -Xms64m -Dfile.encoding=UTF-8"
|
||||
STOP_SIGNAL=0
|
||||
JAVA_PID=0
|
||||
|
||||
# 捕获进程异常终止信号
|
||||
# 5. 信号捕获:优雅停机+终端重置
|
||||
on_exit() {
|
||||
STOP_SIGNAL=1
|
||||
echo -e "\n⚠️ 服务异常终止,可查看日志排查问题"
|
||||
echo -e "\n⚠️ 收到停止信号,正在优雅停机..."
|
||||
# 向Java进程发送中断信号
|
||||
if [ $JAVA_PID -ne 0 ]; then
|
||||
kill -INT $JAVA_PID 2>/dev/null
|
||||
sleep 1
|
||||
kill -9 $JAVA_PID 2>/dev/null
|
||||
fi
|
||||
stty sane
|
||||
echo -e "✅ 服务已停止,Java日志文件:$MAIN_LOG"
|
||||
exit 1
|
||||
}
|
||||
trap 'on_exit' SIGABRT SIGSEGV SIGILL SIGTERM
|
||||
trap 'on_exit' SIGINT SIGTERM SIGABRT SIGSEGV SIGILL
|
||||
|
||||
# 4. 启动服务+日志输出
|
||||
# 6. 启动服务(核心修改:移除所有日志重定向,stdout/stderr直接走终端)
|
||||
stty -echoctl # 屏蔽^C视觉印记
|
||||
echo -e "\n🚀 服务启动中,输入help查指令,输入exit优雅停机"
|
||||
java -cp "$CLASSPATH" "$MAIN_CLASS" -v -log:INFO &> "$BASE_DIR/logs/main.log"
|
||||
echo -e "🚀 服务启动中,输入help查指令,输入exit优雅停机"
|
||||
echo -e "📝 Java日志输出路径:$MAIN_LOG\n"
|
||||
|
||||
# 5. Java退出后 强制重置终端+脚本退出,彻底无残留
|
||||
if [ $STOP_SIGNAL -eq 0 ]; then
|
||||
echo -e "\n✅ 服务优雅停机"
|
||||
fi
|
||||
# 关键:无任何重定向,让Java的LogUtils负责写日志,终端保留完整交互能力
|
||||
java -cp "$CLASSPATH" "$MAIN_CLASS" -v -log:INFO
|
||||
JAVA_PID=$!
|
||||
EXIT_CODE=$?
|
||||
|
||||
# 7. 等待Java进程结束(解决卡空行问题)
|
||||
wait $JAVA_PID
|
||||
|
||||
# 8. 后置处理
|
||||
stty sane
|
||||
exit $?
|
||||
if [ $STOP_SIGNAL -eq 0 ]; then
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "\n✅ 服务优雅停机完成"
|
||||
else
|
||||
echo -e "\n❌ 服务异常退出,退出码:$EXIT_CODE,查看Java日志:$MAIN_LOG"
|
||||
fi
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.logging.Level;
|
||||
* 支持控制台输入交互,输入help查帮助、输入exit退出、其他指令提示未识别
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 00:00:00
|
||||
* @LastEditTime 2026/01/22 17:00:00
|
||||
* @LastEditTime 2026/01/23 15:00:00
|
||||
*/
|
||||
public class Main {
|
||||
private static final String TAG = "AuthCenterMain";
|
||||
@@ -32,7 +32,7 @@ public class Main {
|
||||
private static int httpPort;
|
||||
// 核心状态:volatile保证多线程可见性,false=运行,true=退出
|
||||
private static volatile boolean isExit = false;
|
||||
// 新增:固定安卓兜底根目录
|
||||
// 固定安卓兜底根目录
|
||||
public static final String DEFAULT_ROOT = "/sdcard/WinBoLLStudio/Sources/AuthCenterConsoleApp";
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -51,7 +51,7 @@ public class Main {
|
||||
// 5. 输出启动参数与日志级别信息,此时日志级别已生效
|
||||
LogUtils.d(TAG, "【函数调用】main(),传入参数:" + MainUtils.arrayToString(args));
|
||||
LogUtils.i(TAG, "当前全局日志级别已设置为:" + logLevel.getName());
|
||||
// 新增:打印-v参数解析状态,验证是否生效
|
||||
// 打印-v参数解析状态,验证是否生效
|
||||
boolean isVerbose = MainUtils.isVerbose();
|
||||
LogUtils.i(TAG, "终端日志同步输出状态:" + (isVerbose ? "✅ 开启" : "❌ 关闭"));
|
||||
|
||||
@@ -88,7 +88,7 @@ public class Main {
|
||||
|
||||
// 10. 资源释放与程序退出:新增扫描器关闭,保证资源完整回收
|
||||
ConsoleInputUtils.closeConsoleScanner();
|
||||
// 新增:兜底发送停止通知,覆盖控制台exit指令场景
|
||||
// 兜底发送停止通知,覆盖控制台exit指令场景
|
||||
AdminUtils.getInstance().sendServiceStopNotify();
|
||||
MainUtils.releaseAllResources(httpService);
|
||||
LogUtils.i(TAG, "【函数结束】main(),程序正常退出");
|
||||
@@ -115,9 +115,8 @@ public class Main {
|
||||
|
||||
try {
|
||||
httpService.start();
|
||||
// ========== 新增代码 ==========
|
||||
// 绑定HTTP服务实例到控制台工具类,支持restart指令
|
||||
ConsoleInputUtils.setHttpService(httpService);
|
||||
// =============================
|
||||
System.out.println("\nAuthCenterHttpService 启动成功,监听端口:" + httpPort);
|
||||
System.out.println("可访问接口:GET http://localhost:" + httpPort + "/authcenter/ping");
|
||||
LogUtils.i(TAG, "HTTP服务启动成功,监听端口[" + httpPort + "]");
|
||||
@@ -135,7 +134,7 @@ public class Main {
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务常驻阻塞逻辑(核心修复:持续调用checkConsoleCommand,实现控制台输入的永久监听)
|
||||
* 服务常驻阻塞逻辑(优化:解决CPU高占用+确保指令通知执行)
|
||||
*/
|
||||
private static void consoleBlockForService() {
|
||||
LogUtils.d(TAG, "【函数调用】consoleBlockForService(),开始注册关闭钩子与常驻阻塞");
|
||||
@@ -146,9 +145,8 @@ public class Main {
|
||||
LogUtils.i(TAG, "收到JVM停止信号,执行兜底停机流程");
|
||||
isExit = true;
|
||||
ConsoleInputUtils.forceExit();
|
||||
// ========== 新增:发送服务停止通知 ==========
|
||||
// 发送服务停止通知
|
||||
AdminUtils.getInstance().sendServiceStopNotify();
|
||||
// ==========================================
|
||||
if (httpService != null && httpService.isRunning()) {
|
||||
httpService.stop();
|
||||
LogUtils.i(TAG, "HTTP服务已通过关闭钩子停止");
|
||||
@@ -157,10 +155,21 @@ public class Main {
|
||||
}));
|
||||
|
||||
System.out.println("\n服务已常驻运行,输入exit可退出服务,或执行kill 对应Java进程号停机");
|
||||
// 核心修复:循环持续调用checkConsoleCommand
|
||||
// 利用checkConsoleCommand内部的scanner.hasNextLine()实现阻塞,无输入时不占用CPU
|
||||
// 核心优化:利用scanner.hasNextLine()阻塞特性,避免CPU空转
|
||||
while (!isExit) {
|
||||
ConsoleInputUtils.checkConsoleCommand();
|
||||
try {
|
||||
boolean needExit = ConsoleInputUtils.checkConsoleCommand();
|
||||
if (needExit) {
|
||||
isExit = true;
|
||||
LogUtils.i(TAG, "检测到退出指令,触发服务停机流程");
|
||||
break;
|
||||
}
|
||||
// 兼容特殊终端环境,降低CPU占用
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.w(TAG, "控制台监听线程被中断", e);
|
||||
isExit = true;
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "【函数结束】consoleBlockForService(),常驻阻塞流程退出");
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ import java.util.logging.Level;
|
||||
* 控制台输入工具类,封装输入监听、指令处理、资源释放逻辑
|
||||
* 适配Java7语言规范,兼容Android Termux与标准JVM环境
|
||||
* 支持help/exit/selftestmail/clearverifycode/testserver/status/restart/loglevel/env 9大指令
|
||||
* 新增:每处理一个指令,通过AdminUtils发送临时邮件通知
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 00:00:00
|
||||
* @LastEditTime 2026/01/22 16:00:00
|
||||
* @LastEditTime 2026/01/23 10:00:00
|
||||
*/
|
||||
public class ConsoleInputUtils {
|
||||
private static final String TAG = "ConsoleInputUtils";
|
||||
@@ -90,24 +91,34 @@ public class ConsoleInputUtils {
|
||||
// 指令分发-包含新增运维指令
|
||||
if (CMD_HELP.equalsIgnoreCase(input)) {
|
||||
handleHelpCmd();
|
||||
sendCmdOperateNotify(input, "成功展示帮助信息");
|
||||
} else if (CMD_EXIT.equalsIgnoreCase(input)) {
|
||||
handleExitCmd();
|
||||
sendCmdOperateNotify(input, "已触发服务优雅停机流程");
|
||||
} else if (CMD_SELF_TEST_MAIL.equalsIgnoreCase(input)) {
|
||||
handleSelfTestMailCmd();
|
||||
sendCmdOperateNotify(input, "邮件自测流程执行完成");
|
||||
} else if (CMD_CLEAR_VERIFY_CODE.equalsIgnoreCase(input)) {
|
||||
handleClearVerifyCodeCmd();
|
||||
sendCmdOperateNotify(input, "已清空所有验证码缓存记录");
|
||||
} else if (CMD_TEST_SERVER.equalsIgnoreCase(input)) {
|
||||
handleTestServerCmd();
|
||||
sendCmdOperateNotify(input, "服务器连通性测试执行完成");
|
||||
} else if (CMD_STATUS.equalsIgnoreCase(input)) {
|
||||
handleStatusCmd();
|
||||
sendCmdOperateNotify(input, "已查询并展示服务状态信息");
|
||||
} else if (input.startsWith(CMD_RESTART)) {
|
||||
handleRestartCmd(input);
|
||||
sendCmdOperateNotify(input, "HTTP服务重启操作执行完成");
|
||||
} else if (input.startsWith(CMD_LOG_LEVEL)) {
|
||||
handleLogLevelCmd(input);
|
||||
sendCmdOperateNotify(input, "日志级别调整操作执行完成");
|
||||
} else if (CMD_ENV.equalsIgnoreCase(input)) {
|
||||
handleEnvCmd();
|
||||
sendCmdOperateNotify(input, "已查询并展示系统环境信息");
|
||||
} else if (!input.isEmpty()) {
|
||||
handleCustomCmd(input);
|
||||
sendCmdOperateNotify(input, "未识别指令,已提示用户");
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
@@ -121,6 +132,24 @@ public class ConsoleInputUtils {
|
||||
return Main.isExit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增:指令操作通知发送工具方法
|
||||
* @param cmd 执行的指令内容
|
||||
* @param desc 指令执行结果描述
|
||||
*/
|
||||
private static void sendCmdOperateNotify(String cmd, String desc) {
|
||||
try {
|
||||
String notifyTitle = "【AuthCenter 指令操作通知】";
|
||||
String notifyContent = String.format("操作时间:%s\n执行指令:%s\n操作结果:%s",
|
||||
MainUtils.getCurrentTime(), cmd, desc);
|
||||
// 调用AdminUtils临时邮件通知函数
|
||||
AdminUtils.getInstance().sendTemporaryNotify(notifyTitle, notifyContent);
|
||||
LogUtils.d(TAG, "【通知发送】指令操作邮件通知已发送:" + cmd);
|
||||
} catch (Exception e) {
|
||||
LogUtils.w(TAG, "【通知异常】指令操作邮件通知发送失败:" + cmd, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理help指令:调用帮助信息工具类
|
||||
*/
|
||||
@@ -139,12 +168,14 @@ public class ConsoleInputUtils {
|
||||
*/
|
||||
public static void handleExitCmd() {
|
||||
LogUtils.d(TAG, "【指令处理】执行exit指令,标记服务退出状态");
|
||||
// ========== 新增:发送服务停止通知 ==========
|
||||
AdminUtils.getInstance().sendServiceStopNotify();
|
||||
Main.setExit(true);
|
||||
System.out.println("\n📢 已接收退出指令,正在执行优雅停机...");
|
||||
System.out.flush();
|
||||
// 新增日志:验证方法执行到末尾
|
||||
LogUtils.i(TAG, "【exit指令】handleExitCmd执行完毕,准备发送通知邮件");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理邮件自测指令,自动生成测试码,发送到发送方邮箱
|
||||
|
||||
Reference in New Issue
Block a user