Json-Python-Server/app/services/linux_adapter.py
2026-01-29 18:18:32 +08:00

211 lines
6.6 KiB
Python
Raw Permalink 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.

"""
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 环境初始化完成")