Files
kami_apple_exchage/backend/app/core/config.py
danial 8ad2a5366a refactor(backend): 将Celery替换为Arq进行协程任务处理
本次提交将后端的任务队列系统从Celery迁移到了Arq,以支持基于协程的任务处理。主要改动包括:
- 更新文档和配置文件,反映架构变化。
- 修改健康检查和服务初始化逻辑,以适应Arq的使用。
- 移除与Celery相关的代码,并添加Arq任务定义和调度器。
- 更新Dockerfile和相关脚本,确保Arq worker能够正确运行。
- 调整API和业务服务中的任务处理逻辑,移除对Celery的依赖。

这些改动旨在提高系统的异步处理能力和整体性能。
2025-09-18 16:02:05 +08:00

262 lines
9.5 KiB
Python

"""
应用配置管理
支持多环境配置和动态配置更新
"""
from enum import Enum
from typing import Any, Callable
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings
class Environment(str, Enum):
"""环境类型枚举"""
LOCAL = "local"
DEVELOPMENT = "development"
PRODUCTION = "production"
class Settings(BaseSettings):
"""应用配置类"""
# 基础配置
APP_NAME: str = Field(default="Apple Gift Card Exchange", description="应用名称")
APP_VERSION: str = Field(default="2.0.0", description="应用版本")
ENVIRONMENT: Environment = Field(default=Environment.LOCAL, description="运行环境")
DEBUG: bool = Field(default=False, description="调试模式")
# 服务配置
HOST: str = Field(default="0.0.0.0", description="服务监听地址")
PORT: int = Field(default=8000, description="服务端口")
WORKERS: int = Field(default=1, description="工作进程数")
# 数据库配置
DATABASE_URL: str = Field(
default="sqlite:///./data/kami_data.db", description="数据库连接URL"
)
DATABASE_POOL_SIZE: int = Field(default=10, description="数据库连接池大小")
DATABASE_MAX_OVERFLOW: int = Field(default=20, description="数据库连接池最大溢出")
DATABASE_TIMEOUT: int = Field(default=30, description="数据库连接超时时间")
# Redis配置
REDIS_URL: str = Field(
default="redis://localhost:6379/0", description="Redis连接URL"
)
REDIS_PASSWORD: str | None = Field(default=None, description="Redis密码")
REDIS_DB: int = Field(default=0, description="Redis数据库编号")
# 线程池配置
MAX_THREADS: int = Field(default=3, description="最大并发线程数")
THREAD_TIMEOUT: int = Field(default=300, description="线程超时时间(秒)")
# 超时配置
REQUEST_TIMEOUT: int = Field(default=30, description="请求超时时间(秒)")
PLAYWRIGHT_TIMEOUT: int = Field(
default=60, description="Playwright操作超时时间(秒)"
)
NAVIGATION_TIMEOUT: int = Field(default=30, description="页面导航超时时间(秒)")
# 安全配置
SECRET_KEY: str = Field(default="your-secret-key-here", description="应用密钥")
# TODO: Fix environment variable parsing for ALLOWED_HOSTS and CORS_ORIGINS
ALLOWED_HOSTS: list[str] = Field(default=["*"], description="允许的主机列表")
CORS_ORIGINS: list[str] = Field(default=["*"], description="CORS允许的源列表")
CORS_METHODS: list[str] = Field(
default=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
description="CORS允许的方法",
)
CORS_HEADERS: list[str] = Field(default=["*"], description="CORS允许的头部")
# 日志配置
LOG_LEVEL: str = Field(default="INFO", description="日志级别")
LOG_FORMAT: str = Field(default="json", description="日志格式")
LOG_DIR: str = Field(default="./logs", description="日志目录")
LOG_FILE: str = Field(default="./logs/app.log", description="日志文件路径")
LOG_MAX_SIZE: int = Field(default=104857600, description="日志文件最大大小(字节)")
LOG_BACKUP_COUNT: int = Field(default=10, description="日志文件备份数量")
# ==== OpenTelemetry 统一配置 ====
# 服务基础信息
OTEL_ENABLED: bool = Field(default=True, description="是否启用OpenTelemetry")
OTEL_SERVICE_NAME: str = Field(
default="apple-exchange-backend", description="服务名称"
)
OTEL_SERVICE_VERSION: str = Field(default="2.0.0", description="服务版本")
OTEL_SERVICE_NAMESPACE: str = Field(
default="apple-exchange", description="服务命名空间"
)
# 统一OTLP导出器配置
OTEL_EXPORTER_OTLP_ENDPOINT: str = Field(
default="http://localhost:4317", description="OTLP导出器统一端点"
)
OTEL_EXPORTER_OTLP_PROTOCOL: str = Field(
default="grpc", description="OTLP协议 (grpc, http/protobuf)"
)
OTEL_EXPORTER_OTLP_HEADERS: dict[str, str] = Field(
default_factory=dict, description="OTLP导出器头部信息"
)
OTEL_EXPORTER_OTLP_TIMEOUT: int = Field(
default=30, description="OTLP导出器超时时间(秒)"
)
# 功能模块开关
OTEL_TRACES_ENABLED: bool = Field(default=True, description="是否启用Trace")
OTEL_METRICS_ENABLED: bool = Field(default=True, description="是否启用Metrics")
OTEL_LOGS_ENABLED: bool = Field(default=True, description="是否启用日志导出")
# Trace配置
OTEL_TRACES_SAMPLER: str = Field(
default="always_on", description="采样器 (always_on, always_off, ratio)"
)
OTEL_TRACES_SAMPLER_RATIO: float = Field(
default=1.0, description="ratio采样器的采样率"
)
# Metrics配置
OTEL_METRICS_EXPORT_INTERVAL: int = Field(
default=5000, description="Metrics导出间隔(毫秒)"
)
# gRPC导出器优化配置
OTEL_GRPC_RETRY_ENABLED: bool = Field(default=True, description="是否启用gRPC重试")
OTEL_GRPC_RETRY_MAX_ATTEMPTS: int = Field(default=3, description="最大重试次数")
OTEL_GRPC_RETRY_INITIAL_BACKOFF: float = Field(
default=1.0, description="初始退避时间(秒)"
)
OTEL_GRPC_RETRY_MAX_BACKOFF: float = Field(
default=60.0, description="最大退避时间(秒)"
)
OTEL_GRPC_RETRY_BACKOFF_MULTIPLIER: float = Field(
default=2.0, description="退避时间倍数"
)
# 批处理器统一配置
OTEL_BATCH_MAX_QUEUE_SIZE: int = Field(default=2048, description="批处理队列大小")
OTEL_BATCH_MAX_EXPORT_BATCH_SIZE: int = Field(
default=512, description="单次导出批量大小"
)
OTEL_BATCH_EXPORT_TIMEOUT: int = Field(
default=30000, description="批处理导出超时(毫秒)"
)
OTEL_BATCH_SCHEDULE_DELAY: int = Field(
default=5000, description="批处理调度延迟(毫秒)"
)
# 性能限制配置
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT: int = Field(
default=1024, description="属性值长度限制"
)
OTEL_ATTRIBUTE_COUNT_LIMIT: int = Field(default=128, description="属性数量限制")
OTEL_SPAN_EVENT_COUNT_LIMIT: int = Field(
default=128, description="Span事件数量限制"
)
OTEL_SPAN_LINK_COUNT_LIMIT: int = Field(default=128, description="Span链接数量限制")
# 文件存储配置
UPLOAD_DIR: str = Field(default="./data/uploads", description="上传文件目录")
SNAPSHOT_DIR: str = Field(default="./data/snapshot", description="截图保存目录")
HTML_DIR: str = Field(default="./data/html", description="HTML文件保存目录")
FILE_STORAGE_PATH: str = Field(default="./data", description="文件存储路径")
MAX_FILE_SIZE: int = Field(default=16777216, description="最大文件大小(字节)")
# 健康检查配置
HEALTH_CHECK_INTERVAL: int = Field(default=30, description="健康检查间隔(秒)")
@field_validator("ENVIRONMENT", mode="before")
@classmethod
def validate_environment(cls, v):
"""验证环境配置"""
if isinstance(v, str):
return Environment(v.lower())
return v
@field_validator("LOG_LEVEL")
@classmethod
def validate_log_level(cls, v):
"""验证日志级别"""
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
if v.upper() not in valid_levels:
raise ValueError(f"日志级别必须是以下之一: {valid_levels}")
return v.upper()
@field_validator("LOG_FORMAT")
@classmethod
def validate_log_format(cls, v):
"""验证日志格式"""
valid_formats = ["json", "text"]
if v.lower() not in valid_formats:
raise ValueError(f"日志格式必须是以下之一: {valid_formats}")
return v.lower()
@field_validator("MAX_THREADS")
@classmethod
def validate_max_threads(cls, v):
"""验证最大线程数"""
if v < 1:
raise ValueError("最大线程数必须大于0")
if v > 50:
raise ValueError("最大线程数不能超过50")
return v
@field_validator("PORT")
@classmethod
def validate_port(cls, v):
"""验证端口号"""
if not (1 <= v <= 65535):
raise ValueError("端口号必须在1-65535之间")
return v
@field_validator("OTEL_TRACES_SAMPLER_RATIO")
@classmethod
def validate_sampler_ratio(cls, v):
"""验证采样器参数"""
if not (0.0 <= v <= 1.0):
raise ValueError("采样率必须在0.0-1.0之间")
return v
@field_validator("OTEL_GRPC_RETRY_MAX_ATTEMPTS")
@classmethod
def validate_retry_attempts(cls, v):
"""验证重试次数"""
if v < 0 or v > 10:
raise ValueError("重试次数必须在0-10之间")
return v
@field_validator("OTEL_BATCH_MAX_QUEUE_SIZE")
@classmethod
def validate_queue_size(cls, v):
"""验证队列大小"""
if v < 1 or v > 10000:
raise ValueError("队列大小必须在1-10000之间")
return v
class Config:
"""Pydantic配置"""
env_file = ".env"
env_file_encoding = "utf-8"
case_sensitive = True
extra = "ignore"
# 全局配置实例
_settings: Settings | None = None
def get_settings() -> Settings:
"""获取配置实例(单例模式)"""
global _settings
if _settings is None:
_settings = Settings()
return _settings
def reload_settings() -> Settings:
"""重新加载配置"""
global _settings
_settings = Settings()
return _settings