020-性能优化与故障排查

VibeCoding·九月创作之星挑战赛 10w+人浏览 1.9k人参与

020-性能优化与故障排查

学习目标

  • 掌握Poetry性能优化的核心技术和策略
  • 学会识别和解决Poetry使用中的性能瓶颈
  • 熟练运用故障排查工具和技巧
  • 建立完善的性能监控和问题诊断体系
  • 掌握Poetry在大型项目中的性能调优方法

1. 性能优化概述

1.1 性能瓶颈识别

Poetry在使用过程中可能遇到的性能问题:

# 性能分析工具
poetry run python -m cProfile -o profile.stats your_script.py

# 使用snakeviz可视化性能数据
pip install snakeviz
snakeviz profile.stats

常见性能瓶颈:

  • 依赖解析时间过长
  • 虚拟环境创建缓慢
  • 包安装速度慢
  • 锁定文件生成耗时
  • 缓存机制不当

1.2 性能监控指标

# 文件路径: scripts/performance_monitor.py
import time
import psutil
import subprocess
from pathlib import Path

class PoetryPerformanceMonitor:
    def __init__(self):
        self.metrics = {}
    
    def measure_command(self, command: str, description: str = ""):
        """测量Poetry命令执行时间和资源使用"""
        print(f"🔍 测量: {description or command}")
        
        # 记录开始状态
        start_time = time.time()
        start_memory = psutil.virtual_memory().used
        
        try:
            # 执行命令
            result = subprocess.run(
                command.split(),
                capture_output=True,
                text=True,
                timeout=300  # 5分钟超时
            )
            
            # 记录结束状态
            end_time = time.time()
            end_memory = psutil.virtual_memory().used
            
            # 计算指标
            execution_time = end_time - start_time
            memory_delta = end_memory - start_memory
            
            metrics = {
                'command': command,
                'execution_time': execution_time,
                'memory_usage': memory_delta,
                'success': result.returncode == 0,
                'stdout': result.stdout,
                'stderr': result.stderr
            }
            
            self.metrics[description or command] = metrics
            
            print(f"⏱️  执行时间: {execution_time:.2f}s")
            print(f"💾 内存变化: {memory_delta / 1024 / 1024:.2f}MB")
            print(f"✅ 状态: {'成功' if metrics['success'] else '失败'}")
            
            return metrics
            
        except subprocess.TimeoutExpired:
            print(f"⚠️  命令超时: {command}")
            return None
    
    def generate_report(self):
        """生成性能报告"""
        report = []
        report.append("# Poetry性能分析报告\n")
        
        for desc, metrics in self.metrics.items():
            report.append(f"## {desc}")
            report.append(f"- 执行时间: {metrics['execution_time']:.2f}s")
            report.append(f"- 内存使用: {metrics['memory_usage'] / 1024 / 1024:.2f}MB")
            report.append(f"- 执行状态: {'✅ 成功' if metrics['success'] else '❌ 失败'}")
            report.append("")
        
        return "\n".join(report)

# 使用示例
monitor = PoetryPerformanceMonitor()

# 测量常见操作
monitor.measure_command("poetry install", "依赖安装")
monitor.measure_command("poetry update", "依赖更新")
monitor.measure_command("poetry build", "包构建")
monitor.measure_command("poetry publish --dry-run", "发布预检")

# 生成报告
print(monitor.generate_report())

2. 依赖解析优化

2.1 解析器配置优化

# pyproject.toml - 优化依赖解析配置
[tool.poetry]
name = "optimized-project"
version = "0.1.0"

# 优化解析器设置
[tool.poetry.solver]
# 启用新的依赖解析器(Poetry 1.2+)
use-new-installer = true
# 并行解析
parallel = true
# 解析超时设置
timeout = 600

[tool.poetry.dependencies]
python = "^3.9"
# 使用精确版本减少解析时间
requests = "2.28.1"
# 使用兼容版本约束
numpy = "~1.24.0"
# 避免过于宽泛的版本约束
pandas = ">=1.5.0,<2.0.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0.0"
black = "^22.0.0"

2.2 依赖解析策略

# 文件路径: scripts/dependency_optimizer.py
import toml
from pathlib import Path
from typing import Dict, List, Tuple

class DependencyOptimizer:
    def __init__(self, pyproject_path: str = "pyproject.toml"):
        self.pyproject_path = Path(pyproject_path)
        self.config = self.load_config()
    
    def load_config(self) -> Dict:
        """加载pyproject.toml配置"""
        with open(self.pyproject_path, 'r', encoding='utf-8') as f:
            return toml.load(f)
    
    def analyze_dependencies(self) -> Dict[str, List[str]]:
        """分析依赖关系"""
        analysis = {
            'loose_constraints': [],
            'exact_versions': [],
            'compatible_versions': [],
            'range_versions': []
        }
        
        deps = self.config.get('tool', {}).get('poetry', {}).get('dependencies', {})
        
        for name, constraint in deps.items():
            if name == 'python':
                continue
                
            constraint_str = str(constraint)
            
            if constraint_str.startswith('^'):
                analysis['compatible_versions'].append(f"{name}: {constraint}")
            elif constraint_str.startswith('~'):
                analysis['compatible_versions'].append(f"{name}: {constraint}")
            elif ',' in constraint_str or '>=' in constraint_str:
                analysis['range_versions'].append(f"{name}: {constraint}")
            elif constraint_str.count('.') >= 2 and not any(op in constraint_str for op in ['^', '~', '>', '<', '=']):
                analysis['exact_versions'].append(f"{name}: {constraint}")
            else:
                analysis['loose_constraints'].append(f"{name}: {constraint}")
        
        return analysis
    
    def suggest_optimizations(self) -> List[str]:
        """建议优化方案"""
        analysis = self.analyze_dependencies()
        suggestions = []
        
        if analysis['loose_constraints']:
            suggestions.append("🔧 建议为以下依赖添加版本约束:")
            suggestions.extend([f"  - {dep}" for dep in analysis['loose_constraints']])
        
        if len(analysis['range_versions']) > 10:
            suggestions.append("⚠️  过多的范围版本约束可能导致解析缓慢")
            suggestions.append("   考虑使用更精确的版本约束")
        
        suggestions.append(f"📊 依赖分析统计:")
        suggestions.append(f"  - 精确版本: {len(analysis['exact_versions'])}")
        suggestions.append(f"  - 兼容版本: {len(analysis['compatible_versions'])}")
        suggestions.append(f"  - 范围版本: {len(analysis['range_versions'])}")
        suggestions.append(f"  - 宽松约束: {len(analysis['loose_constraints'])}")
        
        return suggestions
    
    def optimize_constraints(self) -> Dict:
        """优化版本约束"""
        optimized_config = self.config.copy()
        deps = optimized_config['tool']['poetry']['dependencies']
        
        # 优化建议的实现
        optimization_rules = {
            # 常用库的推荐版本约束
            'requests': '~2.28.0',
            'numpy': '~1.24.0',
            'pandas': '~1.5.0',
            'django': '~4.1.0',
            'flask': '~2.2.0',
            'fastapi': '~0.95.0'
        }
        
        for pkg, recommended in optimization_rules.items():
            if pkg in deps and isinstance(deps[pkg], str):
                if deps[pkg].startswith('*') or deps[pkg] == 'latest':
                    deps[pkg] = recommended
                    print(f"🔧 优化 {pkg}: {deps[pkg]} -> {recommended}")
        
        return optimized_config

# 使用示例
optimizer = DependencyOptimizer()
suggestions = optimizer.suggest_optimizations()
for suggestion in suggestions:
    print(suggestion)

2.3 缓存优化策略

# Poetry缓存管理
# 查看缓存信息
poetry cache list

# 清理特定缓存
poetry cache clear pypi --all
poetry cache clear _default_cache --all

# 配置缓存目录
export POETRY_CACHE_DIR="/path/to/custom/cache"

# 禁用缓存(调试时使用)
export POETRY_CACHE_DISABLE=1
# 文件路径: scripts/cache_manager.py
import os
import shutil
from pathlib import Path
import subprocess
from typing import Dict, List

class PoetryCacheManager:
    def __init__(self):
        self.cache_dir = self.get_cache_directory()
    
    def get_cache_directory(self) -> Path:
        """获取Poetry缓存目录"""
        result = subprocess.run(
            ['poetry', 'config', 'cache-dir'],
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return Path(result.stdout.strip())
        
        # 默认缓存目录
        if os.name == 'nt':  # Windows
            return Path.home() / 'AppData' / 'Local' / 'pypoetry' / 'Cache'
        else:  # Unix-like
            return Path.home() / '.cache' / 'pypoetry'
    
    def analyze_cache(self) -> Dict[str, any]:
        """分析缓存使用情况"""
        if not self.cache_dir.exists():
            return {'total_size': 0, 'directories': []}
        
        total_size = 0
        directories = []
        
        for item in self.cache_dir.iterdir():
            if item.is_dir():
                dir_size = sum(f.stat().st_size for f in item.rglob('*') if f.is_file())
                directories.append({
                    'name': item.name,
                    'size': dir_size,
                    'size_mb': dir_size / 1024 / 1024,
                    'file_count': len(list(item.rglob('*')))
                })
                total_size += dir_size
        
        return {
            'total_size': total_size,
            'total_size_mb': total_size / 1024 / 1024,
            'directories': sorted(directories, key=lambda x: x['size'], reverse=True)
        }
    
    def clean_cache(self, cache_type: str = 'all') -> bool:
        """清理缓存"""
        try:
            if cache_type == 'all':
                subprocess.run(['poetry', 'cache', 'clear', '.', '--all'], check=True)
            else:
                subprocess.run(['poetry', 'cache', 'clear', cache_type, '--all'], check=True)
            return True
        except subprocess.CalledProcessError:
            return False
    
    def optimize_cache(self) -> List[str]:
        """优化缓存配置"""
        optimizations = []
        
        analysis = self.analyze_cache()
        
        if analysis['total_size_mb'] > 1000:  # 超过1GB
            optimizations.append("🧹 缓存大小超过1GB,建议清理")
            optimizations.append("   运行: poetry cache clear . --all")
        
        # 检查是否有过多的缓存目录
        if len(analysis['directories']) > 50:
            optimizations.append("📁 缓存目录过多,可能影响性能")
            optimizations.append("   考虑定期清理不常用的缓存")
        
        # 建议缓存配置
        optimizations.append("⚙️  推荐缓存配置:")
        optimizations.append("   export POETRY_CACHE_DIR=/fast/ssd/path")
        optimizations.append("   poetry config virtualenvs.in-project true")
        
        return optimizations

# 使用示例
cache_manager = PoetryCacheManager()
analysis = cache_manager.analyze_cache()

print(f"📊 缓存分析:")
print(f"总大小: {analysis['total_size_mb']:.2f} MB")
print(f"目录数量: {len(analysis['directories'])}")

for opt in cache_manager.optimize_cache():
    print(opt)

3. 构建性能优化

3.1 多阶段构建优化

# 文件路径: Dockerfile.optimized
# 多阶段构建优化示例
FROM python:3.11-slim as builder

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    build-essential \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 安装Poetry
RUN pip install poetry==1.4.2

# 配置Poetry
ENV POETRY_NO_INTERACTION=1 \
    POETRY_VENV_IN_PROJECT=1 \
    POETRY_CACHE_DIR=/opt/poetry-cache

# 复制依赖文件
COPY pyproject.toml poetry.lock ./

# 安装依赖(利用Docker层缓存)
RUN poetry install --only=main && rm -rf $POETRY_CACHE_DIR

# 生产阶段
FROM python:3.11-slim as production

# 复制虚拟环境
COPY --from=builder /.venv /.venv

# 确保使用虚拟环境
ENV PATH="/.venv/bin:$PATH"

# 复制应用代码
COPY . .

# 运行应用
CMD ["python", "-m", "your_app"]

3.2 构建脚本优化

# 文件路径: scripts/build_optimizer.py
import subprocess
import time
import os
from pathlib import Path
from typing import Dict, List

class BuildOptimizer:
    def __init__(self):
        self.build_metrics = {}
    
    def optimize_build_environment(self):
        """优化构建环境"""
        optimizations = [
            # 设置并行构建
            "export MAKEFLAGS='-j$(nproc)'",
            # 优化pip配置
            "export PIP_NO_CACHE_DIR=1",
            "export PIP_DISABLE_PIP_VERSION_CHECK=1",
            # Poetry优化
            "export POETRY_NO_INTERACTION=1",
            "export POETRY_VENV_IN_PROJECT=1",
            "export POETRY_INSTALLER_PARALLEL=true"
        ]
        
        for opt in optimizations:
            print(f"🔧 {opt}")
            if opt.startswith("export"):
                key, value = opt.replace("export ", "").split("=", 1)
                os.environ[key] = value.strip("'\"")
    
    def measure_build_step(self, step_name: str, command: List[str]) -> Dict:
        """测量构建步骤性能"""
        print(f"📦 执行构建步骤: {step_name}")
        
        start_time = time.time()
        
        try:
            result = subprocess.run(
                command,
                capture_output=True,
                text=True,
                timeout=1800  # 30分钟超时
            )
            
            end_time = time.time()
            duration = end_time - start_time
            
            metrics = {
                'step': step_name,
                'duration': duration,
                'success': result.returncode == 0,
                'stdout_lines': len(result.stdout.splitlines()),
                'stderr_lines': len(result.stderr.splitlines())
            }
            
            self.build_metrics[step_name] = metrics
            
            print(f"⏱️  耗时: {duration:.2f}s")
            print(f"✅ 状态: {'成功' if metrics['success'] else '失败'}")
            
            if not metrics['success']:
                print(f"❌ 错误输出: {result.stderr[:200]}...")
            
            return metrics
            
        except subprocess.TimeoutExpired:
            print(f"⚠️  构建步骤超时: {step_name}")
            return {'step': step_name, 'duration': 1800, 'success': False}
    
    def optimized_build_sequence(self):
        """优化的构建序列"""
        # 1. 环境准备
        self.optimize_build_environment()
        
        # 2. 依赖安装(仅生产依赖)
        self.measure_build_step(
            "依赖安装",
            ["poetry", "install", "--only=main", "--no-dev"]
        )
        
        # 3. 代码检查(并行)
        self.measure_build_step(
            "代码格式检查",
            ["poetry", "run", "black", "--check", "."]
        )
        
        # 4. 类型检查
        self.measure_build_step(
            "类型检查",
            ["poetry", "run", "mypy", "."]
        )
        
        # 5. 测试(并行)
        self.measure_build_step(
            "单元测试",
            ["poetry", "run", "pytest", "-n", "auto", "--tb=short"]
        )
        
        # 6. 包构建
        self.measure_build_step(
            "包构建",
            ["poetry", "build"]
        )
        
        # 7. 生成构建报告
        self.generate_build_report()
    
    def generate_build_report(self):
        """生成构建报告"""
        total_time = sum(m['duration'] for m in self.build_metrics.values())
        
        print("\n📊 构建性能报告")
        print("=" * 50)
        print(f"总构建时间: {total_time:.2f}s")
        print()
        
        for step, metrics in self.build_metrics.items():
            status = "✅" if metrics['success'] else "❌"
            percentage = (metrics['duration'] / total_time) * 100
            print(f"{status} {step}: {metrics['duration']:.2f}s ({percentage:.1f}%)")
        
        # 优化建议
        print("\n💡 优化建议:")
        slowest_step = max(self.build_metrics.items(), key=lambda x: x[1]['duration'])
        print(f"- 最慢步骤: {slowest_step[0]} ({slowest_step[1]['duration']:.2f}s)")
        
        if slowest_step[1]['duration'] > 60:
            print("  建议: 考虑并行化或缓存优化")
        
        failed_steps = [name for name, metrics in self.build_metrics.items() if not metrics['success']]
        if failed_steps:
            print(f"- 失败步骤: {', '.join(failed_steps)}")

# 使用示例
optimizer = BuildOptimizer()
optimizer.optimized_build_sequence()

4. 故障排查工具

4.1 诊断脚本

# 文件路径: scripts/poetry_diagnostics.py
import subprocess
import sys
import os
import platform
from pathlib import Path
from typing import Dict, List, Optional

class PoetryDiagnostics:
    def __init__(self):
        self.issues = []
        self.warnings = []
        self.info = []
    
    def check_poetry_installation(self) -> Dict[str, any]:
        """检查Poetry安装状态"""
        try:
            result = subprocess.run(['poetry', '--version'], capture_output=True, text=True)
            if result.returncode == 0:
                version = result.stdout.strip()
                self.info.append(f"✅ Poetry已安装: {version}")
                return {'installed': True, 'version': version}
            else:
                self.issues.append("❌ Poetry未正确安装")
                return {'installed': False, 'version': None}
        except FileNotFoundError:
            self.issues.append("❌ Poetry命令未找到")
            return {'installed': False, 'version': None}
    
    def check_python_environment(self) -> Dict[str, any]:
        """检查Python环境"""
        python_info = {
            'version': platform.python_version(),
            'executable': sys.executable,
            'platform': platform.platform(),
            'architecture': platform.architecture()[0]
        }
        
        self.info.append(f"🐍 Python版本: {python_info['version']}")
        self.info.append(f"📍 Python路径: {python_info['executable']}")
        
        # 检查Python版本兼容性
        major, minor = map(int, python_info['version'].split('.')[:2])
        if major < 3 or (major == 3 and minor < 8):
            self.issues.append(f"⚠️  Python版本过低: {python_info['version']} (推荐3.8+)")
        
        return python_info
    
    def check_project_structure(self) -> Dict[str, any]:
        """检查项目结构"""
        project_files = {
            'pyproject.toml': Path('pyproject.toml').exists(),
            'poetry.lock': Path('poetry.lock').exists(),
            'README.md': Path('README.md').exists(),
            '.gitignore': Path('.gitignore').exists()
        }
        
        for file, exists in project_files.items():
            if exists:
                self.info.append(f"✅ 找到文件: {file}")
            else:
                if file == 'pyproject.toml':
                    self.issues.append(f"❌ 缺少必需文件: {file}")
                elif file == 'poetry.lock':
                    self.warnings.append(f"⚠️  缺少锁定文件: {file}")
                else:
                    self.warnings.append(f"💡 建议添加: {file}")
        
        return project_files
    
    def check_dependencies(self) -> Dict[str, any]:
        """检查依赖状态"""
        try:
            # 检查依赖是否同步
            result = subprocess.run(
                ['poetry', 'check'],
                capture_output=True,
                text=True
            )
            
            if result.returncode == 0:
                self.info.append("✅ 依赖配置正确")
            else:
                self.issues.append(f"❌ 依赖配置问题: {result.stderr}")
            
            # 检查过时的依赖
            result = subprocess.run(
                ['poetry', 'show', '--outdated'],
                capture_output=True,
                text=True
            )
            
            if result.stdout.strip():
                outdated_count = len(result.stdout.strip().split('\n'))
                self.warnings.append(f"📦 有 {outdated_count} 个依赖可以更新")
            else:
                self.info.append("✅ 所有依赖都是最新的")
            
            return {'check_passed': result.returncode == 0}
            
        except Exception as e:
            self.issues.append(f"❌ 依赖检查失败: {str(e)}")
            return {'check_passed': False}
    
    def check_virtual_environment(self) -> Dict[str, any]:
        """检查虚拟环境"""
        try:
            result = subprocess.run(
                ['poetry', 'env', 'info'],
                capture_output=True,
                text=True
            )
            
            if result.returncode == 0:
                env_info = result.stdout
                self.info.append("✅ 虚拟环境正常")
                
                # 解析环境信息
                lines = env_info.split('\n')
                venv_path = None
                python_version = None
                
                for line in lines:
                    if 'Path:' in line:
                        venv_path = line.split(':', 1)[1].strip()
                    elif 'Python:' in line:
                        python_version = line.split(':', 1)[1].strip()
                
                return {
                    'exists': True,
                    'path': venv_path,
                    'python_version': python_version
                }
            else:
                self.warnings.append("⚠️  虚拟环境未创建")
                return {'exists': False}
                
        except Exception as e:
            self.issues.append(f"❌ 虚拟环境检查失败: {str(e)}")
            return {'exists': False}
    
    def check_common_issues(self):
        """检查常见问题"""
        # 检查权限问题
        if os.name != 'nt':  # Unix-like系统
            cache_dir = Path.home() / '.cache' / 'pypoetry'
            if cache_dir.exists() and not os.access(cache_dir, os.W_OK):
                self.issues.append("❌ Poetry缓存目录权限不足")
        
        # 检查网络连接
        try:
            result = subprocess.run(
                ['poetry', 'source', 'show'],
                capture_output=True,
                text=True,
                timeout=10
            )
            if result.returncode == 0:
                self.info.append("✅ 包源连接正常")
            else:
                self.warnings.append("⚠️  包源连接可能有问题")
        except subprocess.TimeoutExpired:
            self.warnings.append("⚠️  包源连接超时")
        except Exception:
            self.warnings.append("⚠️  无法检查包源连接")
    
    def run_full_diagnosis(self) -> Dict[str, any]:
        """运行完整诊断"""
        print("🔍 开始Poetry环境诊断...\n")
        
        # 执行各项检查
        poetry_info = self.check_poetry_installation()
        python_info = self.check_python_environment()
        project_info = self.check_project_structure()
        deps_info = self.check_dependencies()
        venv_info = self.check_virtual_environment()
        self.check_common_issues()
        
        # 生成报告
        print("📋 诊断报告")
        print("=" * 50)
        
        if self.info:
            print("\n✅ 正常状态:")
            for item in self.info:
                print(f"  {item}")
        
        if self.warnings:
            print("\n⚠️  警告:")
            for item in self.warnings:
                print(f"  {item}")
        
        if self.issues:
            print("\n❌ 问题:")
            for item in self.issues:
                print(f"  {item}")
        
        # 生成修复建议
        self.generate_fix_suggestions()
        
        return {
            'poetry': poetry_info,
            'python': python_info,
            'project': project_info,
            'dependencies': deps_info,
            'virtual_env': venv_info,
            'issues_count': len(self.issues),
            'warnings_count': len(self.warnings)
        }
    
    def generate_fix_suggestions(self):
        """生成修复建议"""
        if not self.issues and not self.warnings:
            print("\n🎉 恭喜!没有发现问题")
            return
        
        print("\n💡 修复建议:")
        
        # 基于发现的问题提供建议
        if any("Poetry未正确安装" in issue for issue in self.issues):
            print("  1. 重新安装Poetry:")
            print("     curl -sSL https://install.python-poetry.org | python3 -")
        
        if any("pyproject.toml" in issue for issue in self.issues):
            print("  2. 初始化Poetry项目:")
            print("     poetry init")
        
        if any("虚拟环境" in warning for warning in self.warnings):
            print("  3. 创建虚拟环境:")
            print("     poetry install")
        
        if any("依赖" in issue for issue in self.issues):
            print("  4. 修复依赖问题:")
            print("     poetry lock --no-update")
            print("     poetry install")
        
        if any("权限" in issue for issue in self.issues):
            print("  5. 修复权限问题:")
            print("     sudo chown -R $USER ~/.cache/pypoetry")

# 使用示例
if __name__ == "__main__":
    diagnostics = PoetryDiagnostics()
    result = diagnostics.run_full_diagnosis()

4.2 错误处理最佳实践

# 文件路径: scripts/error_handler.py
import logging
import traceback
from typing import Dict, List, Optional, Callable
from functools import wraps

class PoetryErrorHandler:
    def __init__(self):
        self.setup_logging()
        self.error_patterns = self.load_error_patterns()
    
    def setup_logging(self):
        """设置日志记录"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler('poetry_errors.log'),
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def load_error_patterns(self) -> Dict[str, Dict]:
        """加载常见错误模式和解决方案"""
        return {
            'dependency_resolution': {
                'patterns': [
                    'Could not find a version that satisfies',
                    'No matching distribution found',
                    'dependency resolution failed'
                ],
                'solutions': [
                    '检查依赖版本约束是否过于严格',
                    '尝试更新Poetry: poetry self update',
                    '清理缓存: poetry cache clear . --all',
                    '检查Python版本兼容性'
                ]
            },
            'network_issues': {
                'patterns': [
                    'Connection timeout',
                    'SSL certificate verify failed',
                    'Could not fetch URL'
                ],
                'solutions': [
                    '检查网络连接',
                    '配置代理: poetry config http-basic.pypi username password',
                    '使用国内镜像源',
                    '检查防火墙设置'
                ]
            },
            'permission_errors': {
                'patterns': [
                    'Permission denied',
                    'Access is denied',
                    'Operation not permitted'
                ],
                'solutions': [
                    '检查文件权限: ls -la',
                    '使用sudo运行(不推荐)',
                    '修改目录权限: chmod 755',
                    '检查虚拟环境权限'
                ]
            },
            'lock_file_issues': {
                'patterns': [
                    'poetry.lock is not consistent',
                    'lock file is not up to date',
                    'Hash mismatch'
                ],
                'solutions': [
                    '重新生成锁定文件: poetry lock',
                    '删除锁定文件后重新安装: rm poetry.lock && poetry install',
                    '检查pyproject.toml语法',
                    '更新依赖: poetry update'
                ]
            }
        }
    
    def analyze_error(self, error_message: str) -> Optional[Dict]:
        """分析错误信息并提供解决方案"""
        for category, info in self.error_patterns.items():
            for pattern in info['patterns']:
                if pattern.lower() in error_message.lower():
                    return {
                        'category': category,
                        'pattern': pattern,
                        'solutions': info['solutions']
                    }
        return None
    
    def handle_poetry_error(self, func: Callable) -> Callable:
        """Poetry错误处理装饰器"""
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                error_message = str(e)
                self.logger.error(f"Poetry操作失败: {error_message}")
                
                # 分析错误
                analysis = self.analyze_error(error_message)
                
                if analysis:
                    print(f"\n🔍 错误分析:")
                    print(f"类别: {analysis['category']}")
                    print(f"模式: {analysis['pattern']}")
                    print(f"\n💡 建议解决方案:")
                    for i, solution in enumerate(analysis['solutions'], 1):
                        print(f"  {i}. {solution}")
                else:
                    print(f"\n❌ 未知错误: {error_message}")
                    print("请查看完整错误信息或寻求帮助")
                
                # 记录详细错误信息
                self.logger.error(f"完整错误信息:\n{traceback.format_exc()}")
                
                raise
        return wrapper
    
    def create_error_report(self, error_info: Dict) -> str:
        """创建错误报告"""
        report = []
        report.append("# Poetry错误报告")
        report.append(f"时间: {error_info.get('timestamp', 'Unknown')}")
        report.append(f"命令: {error_info.get('command', 'Unknown')}")
        report.append(f"错误: {error_info.get('error', 'Unknown')}")
        report.append("")
        
        if 'analysis' in error_info:
            analysis = error_info['analysis']
            report.append("## 错误分析")
            report.append(f"类别: {analysis['category']}")
            report.append(f"匹配模式: {analysis['pattern']}")
            report.append("")
            
            report.append("## 建议解决方案")
            for i, solution in enumerate(analysis['solutions'], 1):
                report.append(f"{i}. {solution}")
            report.append("")
        
        report.append("## 环境信息")
        report.append(f"Python版本: {error_info.get('python_version', 'Unknown')}")
        report.append(f"Poetry版本: {error_info.get('poetry_version', 'Unknown')}")
        report.append(f"操作系统: {error_info.get('os_info', 'Unknown')}")
        
        return "\n".join(report)

# 使用示例
error_handler = PoetryErrorHandler()

@error_handler.handle_poetry_error
def install_dependencies():
    """安装依赖的示例函数"""
    import subprocess
    result = subprocess.run(['poetry', 'install'], check=True)
    return result

# 测试错误处理
try:
    install_dependencies()
except Exception as e:
    print("错误已被处理")

5. 性能监控系统

5.1 持续性能监控

# 文件路径: scripts/performance_monitor.py
import time
import psutil
import json
from datetime import datetime
from pathlib import Path
from typing import Dict, List
import subprocess

class ContinuousPerformanceMonitor:
    def __init__(self, log_file: str = "poetry_performance.json"):
        self.log_file = Path(log_file)
        self.metrics_history = self.load_history()
    
    def load_history(self) -> List[Dict]:
        """加载历史性能数据"""
        if self.log_file.exists():
            with open(self.log_file, 'r') as f:
                return json.load(f)
        return []
    
    def save_metrics(self, metrics: Dict):
        """保存性能指标"""
        self.metrics_history.append(metrics)
        
        # 保留最近100条记录
        if len(self.metrics_history) > 100:
            self.metrics_history = self.metrics_history[-100:]
        
        with open(self.log_file, 'w') as f:
            json.dump(self.metrics_history, f, indent=2)
    
    def measure_poetry_command(self, command: List[str]) -> Dict:
        """测量Poetry命令性能"""
        start_time = time.time()
        start_memory = psutil.virtual_memory().used
        start_cpu = psutil.cpu_percent()
        
        try:
            result = subprocess.run(
                command,
                capture_output=True,
                text=True,
                timeout=600
            )
            
            end_time = time.time()
            end_memory = psutil.virtual_memory().used
            end_cpu = psutil.cpu_percent()
            
            metrics = {
                'timestamp': datetime.now().isoformat(),
                'command': ' '.join(command),
                'execution_time': end_time - start_time,
                'memory_delta': end_memory - start_memory,
                'cpu_usage': (start_cpu + end_cpu) / 2,
                'success': result.returncode == 0,
                'output_size': len(result.stdout) + len(result.stderr)
            }
            
            self.save_metrics(metrics)
            return metrics
            
        except subprocess.TimeoutExpired:
            metrics = {
                'timestamp': datetime.now().isoformat(),
                'command': ' '.join(command),
                'execution_time': 600,
                'memory_delta': 0,
                'cpu_usage': 0,
                'success': False,
                'error': 'timeout'
            }
            self.save_metrics(metrics)
            return metrics
    
    def analyze_performance_trends(self) -> Dict:
        """分析性能趋势"""
        if len(self.metrics_history) < 2:
            return {'message': '数据不足,无法分析趋势'}
        
        # 按命令分组
        command_groups = {}
        for metric in self.metrics_history:
            cmd = metric['command']
            if cmd not in command_groups:
                command_groups[cmd] = []
            command_groups[cmd].append(metric)
        
        trends = {}
        for cmd, metrics in command_groups.items():
            if len(metrics) < 2:
                continue
            
            times = [m['execution_time'] for m in metrics]
            avg_time = sum(times) / len(times)
            recent_avg = sum(times[-5:]) / min(5, len(times))
            
            trend = 'stable'
            if recent_avg > avg_time * 1.2:
                trend = 'degrading'
            elif recent_avg < avg_time * 0.8:
                trend = 'improving'
            
            trends[cmd] = {
                'average_time': avg_time,
                'recent_average': recent_avg,
                'trend': trend,
                'sample_count': len(metrics)
            }
        
        return trends
    
    def generate_performance_report(self) -> str:
        """生成性能报告"""
        trends = self.analyze_performance_trends()
        
        report = []
        report.append("# Poetry性能监控报告")
        report.append(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        report.append(f"监控记录数: {len(self.metrics_history)}")
        report.append("")
        
        if not trends or 'message' in trends:
            report.append("数据不足,无法生成详细报告")
            return "\n".join(report)
        
        report.append("## 命令性能趋势")
        for cmd, data in trends.items():
            trend_emoji = {
                'improving': '📈',
                'degrading': '📉',
                'stable': '➡️'
            }
            
            report.append(f"### {cmd}")
            report.append(f"- 平均执行时间: {data['average_time']:.2f}s")
            report.append(f"- 最近平均时间: {data['recent_average']:.2f}s")
            report.append(f"- 趋势: {trend_emoji[data['trend']]} {data['trend']}")
            report.append(f"- 样本数量: {data['sample_count']}")
            report.append("")
        
        # 性能建议
        report.append("## 性能优化建议")
        
        slow_commands = [cmd for cmd, data in trends.items() 
                        if data['average_time'] > 30]
        if slow_commands:
            report.append("### 慢速命令优化")
            for cmd in slow_commands:
                report.append(f"- {cmd}: 考虑缓存优化或并行处理")
        
        degrading_commands = [cmd for cmd, data in trends.items() 
                             if data['trend'] == 'degrading']
        if degrading_commands:
            report.append("### 性能退化警告")
            for cmd in degrading_commands:
                report.append(f"- {cmd}: 性能正在下降,需要调查原因")
        
        return "\n".join(report)

# 使用示例
monitor = ContinuousPerformanceMonitor()

# 监控常见命令
commands_to_monitor = [
    ['poetry', 'install'],
    ['poetry', 'update'],
    ['poetry', 'build'],
    ['poetry', 'check']
]

for cmd in commands_to_monitor:
    print(f"监控命令: {' '.join(cmd)}")
    metrics = monitor.measure_poetry_command(cmd)
    print(f"执行时间: {metrics['execution_time']:.2f}s")
    print(f"成功: {metrics['success']}")
    print()

# 生成报告
report = monitor.generate_performance_report()
print(report)

5.2 自动化监控脚本

#!/bin/bash
# 文件路径: scripts/auto_monitor.sh

# Poetry自动化性能监控脚本

set -e

# 配置
LOG_DIR="./logs"
PERFORMANCE_LOG="$LOG_DIR/poetry_performance.log"
ALERT_THRESHOLD=60  # 秒

# 创建日志目录
mkdir -p "$LOG_DIR"

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$PERFORMANCE_LOG"
}

# 性能测试函数
measure_command() {
    local cmd="$1"
    local description="$2"
    
    log "开始测试: $description"
    
    local start_time=$(date +%s.%N)
    
    if eval "$cmd" > /dev/null 2>&1; then
        local end_time=$(date +%s.%N)
        local duration=$(echo "$end_time - $start_time" | bc)
        
        log "✅ $description 完成: ${duration}s"
        
        # 检查是否超过阈值
        if (( $(echo "$duration > $ALERT_THRESHOLD" | bc -l) )); then
            log "⚠️  警告: $description 执行时间过长 (${duration}s > ${ALERT_THRESHOLD}s)"
            
            # 发送告警(可以集成邮件、Slack等)
            echo "Performance Alert: $description took ${duration}s" >> "$LOG_DIR/alerts.log"
        fi
        
        echo "$duration"
    else
        log "❌ $description 失败"
        echo "failed"
    fi
}

# 系统信息收集
collect_system_info() {
    log "收集系统信息"
    
    {
        echo "=== 系统信息 ==="
        echo "时间: $(date)"
        echo "Poetry版本: $(poetry --version 2>/dev/null || echo 'Not installed')"
        echo "Python版本: $(python --version 2>/dev/null || echo 'Not found')"
        echo "内存使用: $(free -h | grep '^Mem:' || echo 'N/A')"
        echo "磁盘使用: $(df -h . | tail -1 || echo 'N/A')"
        echo "CPU负载: $(uptime || echo 'N/A')"
        echo ""
    } >> "$LOG_DIR/system_info.log"
}

# 主监控循环
main_monitor() {
    log "开始Poetry性能监控"
    
    # 收集系统信息
    collect_system_info
    
    # 测试常见命令
    local commands=(
        "poetry check:配置检查"
        "poetry show:依赖列表"
        "poetry env info:环境信息"
    )
    
    for cmd_desc in "${commands[@]}"; do
        IFS=':' read -r cmd desc <<< "$cmd_desc"
        measure_command "$cmd" "$desc"
        sleep 1
    done
    
    # 如果存在pyproject.toml,进行更多测试
    if [[ -f "pyproject.toml" ]]; then
        log "检测到Poetry项目,执行项目相关测试"
        
        # 依赖安装测试(dry-run)
        measure_command "poetry install --dry-run" "依赖安装预检"
        
        # 构建测试(如果有源码)
        if [[ -d "src" ]] || [[ -f "main.py" ]]; then
            measure_command "poetry build" "包构建"
        fi
    fi
    
    log "监控周期完成"
}

# 报告生成
generate_report() {
    local report_file="$LOG_DIR/performance_report_$(date +%Y%m%d_%H%M%S).md"
    
    {
        echo "# Poetry性能监控报告"
        echo ""
        echo "生成时间: $(date)"
        echo ""
        
        echo "## 最近性能数据"
        echo '```'
        tail -20 "$PERFORMANCE_LOG"
        echo '```'
        echo ""
        
        echo "## 系统信息"
        echo '```'
        tail -10 "$LOG_DIR/system_info.log"
        echo '```'
        echo ""
        
        if [[ -f "$LOG_DIR/alerts.log" ]]; then
            echo "## 性能告警"
            echo '```'
            cat "$LOG_DIR/alerts.log"
            echo '```'
        fi
    } > "$report_file"
    
    log "报告已生成: $report_file"
}

# 清理旧日志
cleanup_logs() {
    log "清理旧日志文件"
    
    # 保留最近7天的日志
    find "$LOG_DIR" -name "*.log" -mtime +7 -delete 2>/dev/null || true
    find "$LOG_DIR" -name "*.md" -mtime +7 -delete 2>/dev/null || true
}

# 主函数
main() {
    case "${1:-monitor}" in
        "monitor")
            main_monitor
            ;;
        "report")
            generate_report
            ;;
        "cleanup")
            cleanup_logs
            ;;
        "continuous")
            log "启动连续监控模式"
            while true; do
                main_monitor
                sleep 300  # 5分钟间隔
            done
            ;;
        *)
            echo "用法: $0 [monitor|report|cleanup|continuous]"
            exit 1
            ;;
    esac
}

# 执行主函数
main "$@"

6. 实践练习

练习1:性能基准测试

创建一个脚本,对比不同Poetry配置下的性能差异:

  • 默认配置 vs 优化配置
  • 缓存启用 vs 缓存禁用
  • 并行安装 vs 串行安装

练习2:故障模拟与排查

模拟常见的Poetry故障场景:

  • 依赖冲突
  • 网络连接问题
  • 权限错误
  • 锁定文件损坏

练习3:监控系统集成

将性能监控集成到CI/CD流程中:

  • 自动性能测试
  • 性能回归检测
  • 告警通知机制

总结

本章节详细介绍了Poetry的性能优化和故障排查技术:

核心要点

  1. 性能优化策略:依赖解析、缓存管理、构建优化
  2. 故障排查工具:诊断脚本、错误分析、修复建议
  3. 监控系统:持续监控、性能趋势分析、自动化报告
  4. 最佳实践:预防性维护、性能基准、问题预警

实践建议

  • 建立性能基准和监控体系
  • 定期进行性能优化和故障排查
  • 自动化常见的维护任务
  • 建立完善的错误处理机制

下一步

恭喜完成Poetry教程"实战应用篇"的学习!接下来将进入"专家级篇",深入学习Poetry的源码解析、插件开发、企业级部署等高级主题。


💡 提示: 性能优化是一个持续的过程,需要根据项目的具体情况和发展阶段不断调整策略。建议建立完善的监控和告警机制,及时发现和解决性能问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvjesus

码力充电

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值