2025-08-08 14:24:06 +08:00
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
import servicemanager
|
|
|
|
|
|
import win32event
|
2025-08-08 15:32:48 +08:00
|
|
|
|
import win32service
|
2025-08-08 14:24:06 +08:00
|
|
|
|
import win32serviceutil
|
|
|
|
|
|
from Common import *
|
|
|
|
|
|
|
|
|
|
|
|
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
|
|
|
|
|
|
os.path.abspath(__file__))
|
|
|
|
|
|
service_name = "ComacDatabase"
|
|
|
|
|
|
service_description = "Comac数据库组件服务"
|
|
|
|
|
|
java_exe = os.path.join(current_dir, "jdk", "bin", "java.exe")
|
|
|
|
|
|
jar_path = os.path.join(current_dir, "electromagnetic.jar")
|
|
|
|
|
|
app_log_dir = os.path.join(current_dir, "logs")
|
|
|
|
|
|
|
|
|
|
|
|
class ComacDBService(win32serviceutil.ServiceFramework):
|
|
|
|
|
|
_svc_name_ = service_name # 服务名称
|
|
|
|
|
|
_svc_display_name_ = service_name # 显示名称
|
|
|
|
|
|
_svc_description_ = service_description # 服务描述
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, args):
|
|
|
|
|
|
win32serviceutil.ServiceFramework.__init__(self, args)
|
|
|
|
|
|
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
|
|
|
|
|
|
self.process = None
|
|
|
|
|
|
self.service_ready = False
|
|
|
|
|
|
|
|
|
|
|
|
def SvcDoRun(self):
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 报告服务正在启动
|
|
|
|
|
|
self.ReportServiceStatus(win32service.SERVICE_START_PENDING, waitHint=10000)
|
|
|
|
|
|
formatted_time = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
|
|
|
|
cmd = [
|
|
|
|
|
|
java_exe,
|
|
|
|
|
|
f"-jar",
|
|
|
|
|
|
jar_path,
|
|
|
|
|
|
f"--logging.file.path={app_log_dir}",
|
|
|
|
|
|
f"--logging.file.name={app_log_dir}/app_{formatted_time}.log",
|
|
|
|
|
|
f"--winPrefix={current_dir}",
|
|
|
|
|
|
f"--spring.datasource.username={mariadb_user}",
|
|
|
|
|
|
f"--spring.datasource.password={mariadb_passowrd}",
|
|
|
|
|
|
f"--server.port={comac_db_port}",
|
|
|
|
|
|
f"--spring.datasource.url=jdbc:mariadb://127.0.0.1:{mariadb_port}/{mariadb_init_schema}?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&sslModel=true&serverTimezone=GMT%2B8&allowMultiQueries=true&rewriteBatchedStatements=true"
|
|
|
|
|
|
]
|
|
|
|
|
|
# 添加完整的错误处理
|
|
|
|
|
|
env = os.environ.copy()
|
|
|
|
|
|
startupinfo = subprocess.STARTUPINFO()
|
|
|
|
|
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
|
|
|
|
startupinfo.wShowWindow = 0 # 隐藏窗口
|
|
|
|
|
|
|
|
|
|
|
|
# 启动Java进程(使用subprocess.Popen捕获输出)
|
|
|
|
|
|
try:
|
|
|
|
|
|
self.process = subprocess.Popen(
|
|
|
|
|
|
cmd,
|
|
|
|
|
|
cwd=app_log_dir,
|
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
|
|
stdin=subprocess.PIPE,
|
|
|
|
|
|
creationflags=subprocess.CREATE_NO_WINDOW,
|
|
|
|
|
|
env=env,
|
|
|
|
|
|
startupinfo=startupinfo
|
|
|
|
|
|
)
|
|
|
|
|
|
logger.info(f"Java process started with PID: {self.process.pid}")
|
|
|
|
|
|
|
|
|
|
|
|
# 检查进程是否存活
|
|
|
|
|
|
time.sleep(2) # 给Java启动时间
|
|
|
|
|
|
if self.process.poll() is not None:
|
|
|
|
|
|
# 读取输出以诊断问题
|
|
|
|
|
|
output, _ = self.process.communicate(timeout=2)
|
|
|
|
|
|
output = output.decode('gbk', errors='ignore')
|
|
|
|
|
|
logger.error(f"Java process exited prematurely. Output: {output}")
|
|
|
|
|
|
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
|
|
|
|
|
|
return
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"Failed to start Java process: {str(e)}")
|
|
|
|
|
|
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 服务完全启动
|
|
|
|
|
|
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
|
|
|
|
|
|
self.service_ready = True
|
|
|
|
|
|
logger.info("Service fully started and running")
|
|
|
|
|
|
|
|
|
|
|
|
# 启动线程监控Java进程
|
|
|
|
|
|
self.monitor_java_process()
|
|
|
|
|
|
|
|
|
|
|
|
# 等待停止信号
|
|
|
|
|
|
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"Service failed: {str(e)}", exc_info=True)
|
|
|
|
|
|
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
|
|
|
|
|
|
finally:
|
|
|
|
|
|
# 确保清理资源
|
|
|
|
|
|
if self.process and self.process.poll() is None:
|
|
|
|
|
|
self.process.terminate()
|
|
|
|
|
|
self.process = None
|
|
|
|
|
|
|
|
|
|
|
|
def monitor_java_process(self):
|
|
|
|
|
|
"""在后台线程中监控Java进程"""
|
|
|
|
|
|
import threading
|
|
|
|
|
|
|
|
|
|
|
|
def monitor():
|
|
|
|
|
|
try:
|
|
|
|
|
|
if self.process:
|
|
|
|
|
|
# 持续读取输出
|
|
|
|
|
|
while self.process.poll() is None and self.service_ready:
|
|
|
|
|
|
line = self.process.stdout.readline()
|
|
|
|
|
|
if line:
|
|
|
|
|
|
line = line.decode('gbk', errors='ignore').strip()
|
|
|
|
|
|
logger.info(f"JAVA: {line}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
|
|
|
|
|
|
# 检查进程退出原因
|
|
|
|
|
|
if self.process.poll() is not None:
|
|
|
|
|
|
logger.warning(f"Java process exited with code: {self.process.poll()}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"Monitor thread error: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
# 启动监控线程
|
|
|
|
|
|
threading.Thread(target=monitor, daemon=True).start()
|
|
|
|
|
|
|
|
|
|
|
|
def SvcStop(self):
|
|
|
|
|
|
# 报告服务正在停止
|
|
|
|
|
|
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
|
|
|
|
|
|
|
|
|
|
|
# 停止Java进程(如果存在)
|
|
|
|
|
|
if self.process:
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 更优雅的关闭方式
|
|
|
|
|
|
logger.info("Terminating Java process...")
|
|
|
|
|
|
self.process.terminate()
|
|
|
|
|
|
|
|
|
|
|
|
# 等待最多10秒
|
|
|
|
|
|
for _ in range(10):
|
|
|
|
|
|
if self.process.poll() is not None:
|
|
|
|
|
|
break
|
|
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.warning("Java process did not exit, using force kill")
|
|
|
|
|
|
self.process.kill()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"Error stopping Java process: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
# 通知主线程停止
|
|
|
|
|
|
win32event.SetEvent(self.hWaitStop)
|
|
|
|
|
|
self.service_ready = False
|
|
|
|
|
|
|
|
|
|
|
|
# 报告服务已停止
|
|
|
|
|
|
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
|
|
|
|
|
|
logger.info("Service stopped successfully")
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-08-08 15:15:47 +08:00
|
|
|
|
if __name__ == '__main__':
|
2025-08-08 14:24:06 +08:00
|
|
|
|
ensure_dir(app_log_dir)
|
|
|
|
|
|
logger.add(
|
2025-08-08 15:15:47 +08:00
|
|
|
|
sink=os.path.join(app_log_dir, "ManagerService.log"),
|
|
|
|
|
|
rotation="10 MB",
|
|
|
|
|
|
retention="1 days",
|
|
|
|
|
|
compression="zip",
|
|
|
|
|
|
enqueue=True,
|
|
|
|
|
|
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
|
2025-08-08 14:24:06 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if len(sys.argv) == 1:
|
|
|
|
|
|
servicemanager.Initialize()
|
|
|
|
|
|
servicemanager.PrepareToHostSingle(ComacDBService)
|
|
|
|
|
|
servicemanager.StartServiceCtrlDispatcher()
|
|
|
|
|
|
else:
|
2025-08-08 15:15:47 +08:00
|
|
|
|
# 处理 install/update/remove 等命令
|
|
|
|
|
|
win32serviceutil.HandleCommandLine(ComacDBService)
|
|
|
|
|
|
if "install" in sys.argv:
|
|
|
|
|
|
win32serviceutil.ChangeServiceConfig(
|
|
|
|
|
|
pythonClassString=win32serviceutil.GetServiceClassString(ComacDBService),
|
|
|
|
|
|
serviceName=service_name,
|
|
|
|
|
|
startType=win32service.SERVICE_AUTO_START,
|
|
|
|
|
|
)
|