数据库备份实现
This commit is contained in:
parent
cefc65f4f8
commit
bbb64e94bc
11
build.txt
11
build.txt
|
|
@ -1,6 +1,9 @@
|
|||
cd src
|
||||
pyinstaller --onefile --add-data "install/Common.py:install" .\install\InstallMariaDB.py
|
||||
pyinstaller --onefile --add-data "install/Common.py:install" --add-data "datas/electromagnetic.jar:datas" --add-data "datas/init.sql:datas" --add-data "datas/ManagerService.exe:datas" --hidden-import win32timezone .\install\InstallOrUpgradeComacDB.py
|
||||
pyinstaller --onefile --add-data "common/Common.py:common" --hidden-import loguru .\install\InstallMariaDB.py
|
||||
pyinstaller --onefile --add-data "common/Common.py:common" --add-data "datas/electromagnetic.jar:datas" --add-data "datas/init.sql:datas" --add-data "datas/ManagerService.exe:datas" --hidden-import loguru --hidden-import win32timezone .\install\InstallOrUpgradeComacDB.py
|
||||
pyinstaller --onefile .\install\Uninstall.py
|
||||
pyinstaller --onefile .\install\SetFile.py
|
||||
pyinstaller --onefile --add-data "install/Common.py:install" --hidden-import win32timezone .\install\ManagerService.py
|
||||
pyinstaller --onefile --add-data "common/Common.py:common" --hidden-import loguru --hidden-import win32timezone .\install\ManagerService.py
|
||||
|
||||
数据库备份
|
||||
pyinstaller --onefile --add-data "common/Common.py:common" --add-data "datas/ComacDBBackupService.exe:datas" --add-data "datas/electromagnetic-backup.jar:datas" --hidden-import win32timezone --hidden-import loguru .\backup\InstallOrUpgradeComacDBBackup.py
|
||||
pyinstaller --onefile --add-data "common/Common.py:common" --hidden-import win32timezone --hidden-import loguru .\backup\ComacDBBackupService.py
|
||||
|
|
|
|||
|
|
@ -0,0 +1,192 @@
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
import subprocess
|
||||
import servicemanager
|
||||
import win32event
|
||||
import win32service
|
||||
import win32serviceutil
|
||||
import threading
|
||||
import time
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from common.Common import ensure_dir, create_file_if_not_exist
|
||||
|
||||
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
|
||||
os.path.abspath(__file__))
|
||||
service_name = "ComacDatabaseBackup"
|
||||
service_description = "Comac数据库备份组件服务"
|
||||
java_exe = os.path.join(current_dir, "jdk", "bin", "java.exe")
|
||||
jar_path = os.path.join(current_dir, "electromagnetic-backup.jar")
|
||||
app_log_dir = os.path.join(current_dir, "logs")
|
||||
application_properties_path = current_dir + "/application.json"
|
||||
|
||||
class ComacDBBackupService(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"--ele.backup.winPrefix={current_dir}",
|
||||
f"--file.enc.passwd=adknfhkj87654knd",
|
||||
f"--logging.file.path={app_log_dir}",
|
||||
f"--logging.file.name={app_log_dir}/app_{formatted_time}.log",
|
||||
f"--server.port={config.get('app.run.port', 12491)}",
|
||||
f"--winPrefix={current_dir}",
|
||||
]
|
||||
# 添加完整的错误处理
|
||||
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(ComacDBBackupService)
|
||||
servicemanager.StartServiceCtrlDispatcher()
|
||||
else:
|
||||
# 处理 install/update/remove 等命令
|
||||
win32serviceutil.HandleCommandLine(ComacDBBackupService)
|
||||
if "install" in sys.argv:
|
||||
win32serviceutil.ChangeServiceConfig(
|
||||
pythonClassString=win32serviceutil.GetServiceClassString(ComacDBBackupService),
|
||||
serviceName=service_name,
|
||||
startType=win32service.SERVICE_AUTO_START,
|
||||
)
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from common.Common import get_resource_path, ensure_dir
|
||||
|
||||
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
|
||||
os.path.abspath(__file__))
|
||||
manage_service_exe = os.path.join(current_dir, "ComacDBBackupService.exe")
|
||||
app_log_dir = os.path.join(current_dir, "logs")
|
||||
sql_path = get_resource_path(os.path.join("datas", "init.sql"))
|
||||
jar_path = get_resource_path(os.path.join("datas", "electromagnetic-backup.jar"))
|
||||
manage_service_path = get_resource_path(os.path.join("datas", "ComacDBBackupService.exe"))
|
||||
|
||||
|
||||
def set_jar():
|
||||
dest_jar_path = os.path.join(current_dir, "electromagnetic-backup.jar")
|
||||
shutil.copy(jar_path, dest_jar_path)
|
||||
logger.info("jar设置成功")
|
||||
pass
|
||||
|
||||
|
||||
def update_service(cmd):
|
||||
|
||||
if not os.path.exists(manage_service_exe):
|
||||
logger.warning("manage service path not exist")
|
||||
return
|
||||
|
||||
command = [manage_service_exe, cmd]
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.info(result.stdout)
|
||||
logger.warning(f"操作失败,cmd is {cmd}")
|
||||
logger.error(result.stderr)
|
||||
else:
|
||||
logger.info(f"操作成功,cmd is {cmd}")
|
||||
pass
|
||||
|
||||
def upgrade_service():
|
||||
shutil.copy(manage_service_path, manage_service_exe)
|
||||
logger.info("service 设置成功")
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ensure_dir(app_log_dir)
|
||||
logger.add(
|
||||
sink=os.path.join(app_log_dir, "InstallOrUpgradeComacDB.log"), # 文件路径模板
|
||||
rotation="10 MB", # 文件大小达到10MB时轮转
|
||||
retention="1 days", # 保留最近1天的日志
|
||||
compression="zip", # 压缩旧日志节省空间
|
||||
enqueue=True, # 线程安全写入
|
||||
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}" # 自定义格式
|
||||
)
|
||||
|
||||
update_service("stop")
|
||||
update_service("remove")
|
||||
set_jar()
|
||||
upgrade_service()
|
||||
update_service("install")
|
||||
update_service("start")
|
||||
|
||||
pass
|
||||
|
|
@ -2,6 +2,7 @@ import os
|
|||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
|
@ -119,3 +120,8 @@ def delete_old_files(directory, days=2):
|
|||
logger.info(f"\n操作完成!共删除 {deleted_count} 个日志文件。")
|
||||
except Exception as e:
|
||||
logger.info(f"遍历目录时出错: {str(e)}", file=sys.stderr)
|
||||
|
||||
def create_file_if_not_exist(path : str, content=''):
|
||||
file_path = Path(path)
|
||||
if not file_path.exists():
|
||||
file_path.write_text(content)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,4 +1,4 @@
|
|||
from Common import *
|
||||
from common.Common import *
|
||||
|
||||
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
|
||||
os.path.abspath(__file__))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import shutil
|
||||
|
||||
from Common import *
|
||||
from common.Common import *
|
||||
|
||||
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
|
||||
os.path.abspath(__file__))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
from datetime import datetime
|
||||
|
||||
import servicemanager
|
||||
|
|
@ -5,7 +6,7 @@ import win32event
|
|||
import win32service
|
||||
import win32serviceutil
|
||||
import threading
|
||||
from Common import *
|
||||
from common.Common import *
|
||||
|
||||
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
|
||||
os.path.abspath(__file__))
|
||||
|
|
@ -14,7 +15,7 @@ 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 # 服务名称
|
||||
|
|
@ -29,6 +30,9 @@ class ComacDBService(win32serviceutil.ServiceFramework):
|
|||
|
||||
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")
|
||||
|
|
@ -44,6 +48,8 @@ class ComacDBService(win32serviceutil.ServiceFramework):
|
|||
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"--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"
|
||||
]
|
||||
# 添加完整的错误处理
|
||||
|
|
@ -158,6 +164,7 @@ class ComacDBService(win32serviceutil.ServiceFramework):
|
|||
|
||||
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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue