Json-Python-Server/app/services/linux_adapter.py

211 lines
6.6 KiB
Python
Raw Permalink Normal View History

2026-01-29 18:18:32 +08:00
"""
Linux 系统适配模块
处理 Linux 特有的路径权限环境变量等问题
"""
import os
import sys
import logging
from pathlib import Path
from typing import Optional
logger = logging.getLogger(__name__)
class LinuxAdapter:
"""Linux 系统适配器"""
@staticmethod
def is_linux() -> bool:
"""检查是否运行在 Linux 系统上"""
return sys.platform.startswith('linux')
@staticmethod
def normalize_path(path: str) -> Path:
"""
规范化路径 - 适配不同操作系统
Args:
path: 路径字符串可能混合了不同分隔符
Returns:
规范化后的 Path 对象
"""
# 替换反斜杠为正斜杠
path = path.replace('\\', '/')
# 创建 Path 对象,会根据系统自动转换
return Path(path).resolve()
@staticmethod
def ensure_directory_writable(dir_path: Path) -> bool:
"""
确保目录可写
Args:
dir_path: 目录路径
Returns:
是否成功
"""
try:
dir_path = Path(dir_path)
dir_path.mkdir(parents=True, exist_ok=True)
# 检查写入权限
test_file = dir_path / '.test_write'
test_file.touch()
test_file.unlink()
logger.info(f"✓ 目录可写: {dir_path}")
return True
except PermissionError:
logger.error(f"✗ 没有写入权限: {dir_path}")
logger.error(f" 建议: sudo chmod 755 {dir_path}")
return False
except Exception as e:
logger.error(f"✗ 目录检查失败: {dir_path} - {e}")
return False
@staticmethod
def get_recommended_upload_dir() -> Path:
"""
获取 Linux 上推荐的上传目录
Returns:
推荐的上传目录路径
"""
# 优先级:
# 1. 环境变量指定的目录
# 2. 项目相对路径
# 3. /tmp (临时目录)
if upload_dir := os.getenv('UPLOAD_DIR'):
return Path(upload_dir)
project_upload = Path(__file__).parent.parent.parent / 'uploads'
if project_upload.exists() and os.access(project_upload, os.W_OK):
return project_upload
logger.warning("使用系统临时目录进行上传存储")
return Path('/tmp/lazy_fjh_uploads')
@staticmethod
def setup_signal_handlers():
"""
设置 Linux 信号处理器
确保优雅关闭
"""
import signal
def signal_handler(sig, frame):
logger.info(f"收到信号 {sig},开始优雅关闭...")
sys.exit(0)
if LinuxAdapter.is_linux():
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
logger.info("✓ Linux 信号处理器已注册")
@staticmethod
def get_process_info() -> dict:
"""
获取当前进程信息
Returns:
进程信息字典
"""
import psutil
process = psutil.Process(os.getpid())
return {
'pid': os.getpid(),
'user': os.getlogin() if LinuxAdapter.is_linux() else 'unknown',
'memory_mb': process.memory_info().rss / 1024 / 1024,
'cpu_percent': process.cpu_percent(interval=1),
'num_threads': process.num_threads()
}
@staticmethod
def check_system_resources() -> dict:
"""
检查系统资源
Returns:
系统资源信息
"""
import psutil
return {
'cpu_count': psutil.cpu_count(),
'total_memory_gb': psutil.virtual_memory().total / (1024**3),
'available_memory_gb': psutil.virtual_memory().available / (1024**3),
'disk_usage_percent': psutil.disk_usage('/').percent
}
@staticmethod
def optimize_for_linux():
"""
针对 Linux 系统进行优化
"""
if not LinuxAdapter.is_linux():
return
logger.info("应用 Linux 系统优化...")
# 1. 增加文件描述符限制
try:
import resource
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))
logger.info(f"✓ 设置文件描述符限制: {hard}")
except Exception as e:
logger.warning(f"⚠ 无法设置文件描述符: {e}")
# 2. 可选内存限制(默认跳过,避免在 WSL/容器中因 RLIMIT_AS 过低触发 MemoryError
mem_limit_env = os.getenv('LINUX_RLIMIT_AS_MB') or os.getenv('LINUX_MEMORY_LIMIT_MB')
if mem_limit_env:
try:
import resource
limit_mb = int(mem_limit_env)
limit_bytes = limit_mb * 1024**2
resource.setrlimit(resource.RLIMIT_AS, (limit_bytes, limit_bytes))
logger.info(f"✓ 设置虚拟内存限制: {limit_mb}MB")
except Exception as e:
logger.warning(f"⚠ 无法设置虚拟内存限制: {e}")
else:
logger.info("跳过虚拟内存限制 (未设置 LINUX_RLIMIT_AS_MB / LINUX_MEMORY_LIMIT_MB)")
# 3. 注册信号处理
LinuxAdapter.setup_signal_handlers()
logger.info("Linux 系统优化完成")
def init_linux_environment():
"""
初始化 Linux 环境
在应用启动时调用
"""
if not LinuxAdapter.is_linux():
logger.info("非 Linux 系统,跳过 Linux 特定初始化")
return
logger.info("=" * 60)
logger.info("初始化 Linux 环境...")
logger.info("=" * 60)
# 应用优化
LinuxAdapter.optimize_for_linux()
# 检查系统资源
resources = LinuxAdapter.check_system_resources()
logger.info(f"系统资源: {resources}")
# 检查上传目录
upload_dir = LinuxAdapter.get_recommended_upload_dir()
if not LinuxAdapter.ensure_directory_writable(upload_dir):
logger.warning(f"上传目录 {upload_dir} 可能不可写")
logger.info("Linux 环境初始化完成")