211 lines
6.6 KiB
Python
211 lines
6.6 KiB
Python
|
|
"""
|
|||
|
|
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 环境初始化完成")
|