database-win-pkg/src/install/ManagerService.py

193 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
from datetime import datetime
import servicemanager
import win32event
import win32service
import win32serviceutil
import threading
from common.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")
application_properties_path = current_dir + "/application.json"
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:
#解析配置文件
with open(application_properties_path, 'r', encoding='utf-8') as f:
config = json.load(f)
# 报告服务正在启动
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"--file.enc.passwd=adknfhkj87654knd",
f"--login.enc.passwd=5JKRGV0QO4WK1WCWVK55YEU0A1NPOXOP",
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"--backup.remote.host={config.get('backup.remote.host', '127.0.0.1')}",
f"--backup.remote.port={config.get('backup.remote.port', 12491)}",
f"--backup.interval.mins={config.get('backup.interval.mins', 1440)}",
f"--access.log.clear.hrs={config.get('access.log.clear.hrs', 2)}",
f"--app.user.operation.log.dir={app_log_dir}",
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进程"""
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
if __name__ == '__main__':
ensure_dir(app_log_dir)
create_file_if_not_exist(application_properties_path, content="{}")
logger.add(
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}"
)
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(ComacDBService)
servicemanager.StartServiceCtrlDispatcher()
else:
# 处理 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,
)