BlueSkyXN commited on
Commit
ff725f8
·
unverified ·
1 Parent(s): cb69859

Program Concurrency Performance (#5)

Browse files

本次提交实施了稳定的三层防护方案,解决单进程模式导致的CPU争抢问题。

## 核心改进

### 第1层:启用多进程Worker模式
- 修改 entrypoint.sh:添加 --workers 参数
- 默认4个worker进程,充分利用多核CPU
- 预期CPU利用率从12.5%提升至60-70%

### 第2层:并发限制保护机制
- 修改 main.py:添加信号量(Semaphore)控制
- 每个worker最多3个并发ImageMagick进程
- 防止资源过载和系统不稳定

### 第3层:ImageMagick资源限制
- 修改 Dockerfile:配置资源限制环境变量
- MAGICK_MEMORY_LIMIT=512MiB (内存限制)
- MAGICK_TIME_LIMIT=300 (超时限制)
- MAGICK_THREAD_LIMIT=2 (线程限制)

Files changed (3) hide show
  1. Dockerfile +11 -0
  2. entrypoint.sh +22 -7
  3. main.py +17 -10
Dockerfile CHANGED
@@ -6,6 +6,17 @@ ENV PORT=8000
6
  ENV PYTHONUNBUFFERED=1
7
  ENV TEMP_DIR=/app/temp
8
 
 
 
 
 
 
 
 
 
 
 
 
9
  # 2. 安装 ImageMagick 和 AVIF/HEIC 依赖
10
  # libheif-examples 提供了 magick 所需的 heif-enc 编码器
11
  RUN apt-get update && apt-get install -y \
 
6
  ENV PYTHONUNBUFFERED=1
7
  ENV TEMP_DIR=/app/temp
8
 
9
+ # ImageMagick 资源限制(防止资源过载)
10
+ ENV MAGICK_MEMORY_LIMIT=512MiB
11
+ ENV MAGICK_MAP_LIMIT=1GiB
12
+ ENV MAGICK_DISK_LIMIT=4GiB
13
+ ENV MAGICK_TIME_LIMIT=300
14
+ ENV MAGICK_THREAD_LIMIT=2
15
+
16
+ # 并发控制配置
17
+ ENV WORKERS=4
18
+ ENV MAX_CONCURRENT_PER_WORKER=3
19
+
20
  # 2. 安装 ImageMagick 和 AVIF/HEIC 依赖
21
  # libheif-examples 提供了 magick 所需的 heif-enc 编码器
22
  RUN apt-get update && apt-get install -y \
entrypoint.sh CHANGED
@@ -1,18 +1,33 @@
1
  #!/bin/sh
2
 
3
  # 打印环境信息用于调试
4
- echo "Starting Magick API Service"
5
- echo "Environment: PORT=$PORT"
 
 
 
 
 
 
 
 
6
 
7
  # 验证Magick是否可用
8
- echo "Checking ImageMagick installation..."
9
  magick --version | head -n 1
10
- echo "Checking AVIF encoder (heif-enc) installation..."
11
  which heif-enc
12
 
13
  # 确保使用正确的端口变量
14
  PORT="${PORT:-8000}"
15
- echo "Using port: $PORT"
16
 
17
- # 执行 uvicorn 服务器 (同您的 OCR-HFS)
18
- exec uvicorn main:app --host 0.0.0.0 --port $PORT
 
 
 
 
 
 
 
 
 
1
  #!/bin/sh
2
 
3
  # 打印环境信息用于调试
4
+ echo "=========================================="
5
+ echo "Magick API Service Starting"
6
+ echo "=========================================="
7
+ echo "Configuration:"
8
+ echo " PORT: ${PORT:-8000}"
9
+ echo " WORKERS: ${WORKERS:-4}"
10
+ echo " MAX_CONCURRENT_PER_WORKER: ${MAX_CONCURRENT_PER_WORKER:-3}"
11
+ echo " MAGICK_MEMORY_LIMIT: ${MAGICK_MEMORY_LIMIT:-512MiB}"
12
+ echo " MAGICK_TIME_LIMIT: ${MAGICK_TIME_LIMIT:-300}"
13
+ echo "=========================================="
14
 
15
  # 验证Magick是否可用
16
+ echo "Checking dependencies..."
17
  magick --version | head -n 1
 
18
  which heif-enc
19
 
20
  # 确保使用正确的端口变量
21
  PORT="${PORT:-8000}"
22
+ WORKERS="${WORKERS:-4}"
23
 
24
+ echo "Starting $WORKERS workers on port $PORT..."
25
+ echo "=========================================="
26
+
27
+ # 执行 uvicorn 服务器 - 启用多进程模式
28
+ exec uvicorn main:app \
29
+ --host 0.0.0.0 \
30
+ --port $PORT \
31
+ --workers $WORKERS \
32
+ --log-level info \
33
+ --access-log
main.py CHANGED
@@ -50,6 +50,11 @@ MAX_FILE_SIZE_MB = 200 # 允许上传的最大文件大小 (MB)
50
  TIMEOUT_SECONDS = 300 # Magick 进程执行的超时时间 (秒)
51
  TEMP_DIR = os.getenv("TEMP_DIR", tempfile.gettempdir()) # 临时文件存储目录,优先使用环境变量,否则使用系统临时目录
52
 
 
 
 
 
 
53
  # --- 2. API 参数类型定义 ---
54
 
55
  # 定义 API 路径中允许的目标格式
@@ -339,16 +344,18 @@ async def _perform_conversion(
339
  command_str = ' '.join(cmd)
340
  logger.info(f"正在执行命令: {command_str}")
341
 
342
- # 7. 异步执行 Magick 命令 (继承自 imagemagickapi-hfs 实践)
343
- process = await asyncio.subprocess.create_subprocess_exec(
344
- *cmd,
345
- stdout=asyncio.subprocess.PIPE,
346
- stderr=asyncio.subprocess.PIPE
347
- )
348
- stdout, stderr = await asyncio.wait_for(
349
- process.communicate(),
350
- timeout=TIMEOUT_SECONDS
351
- )
 
 
352
 
353
  # 8. 检查命令执行结果
354
  if process.returncode != 0:
 
50
  TIMEOUT_SECONDS = 300 # Magick 进程执行的超时时间 (秒)
51
  TEMP_DIR = os.getenv("TEMP_DIR", tempfile.gettempdir()) # 临时文件存储目录,优先使用环境变量,否则使用系统临时目录
52
 
53
+ # 并发控制配置(防止资源过载)
54
+ MAX_CONCURRENT_CONVERSIONS = int(os.getenv("MAX_CONCURRENT_PER_WORKER", "3"))
55
+ conversion_semaphore = asyncio.Semaphore(MAX_CONCURRENT_CONVERSIONS)
56
+ logger.info(f"并发限制已启用: 每个worker最多 {MAX_CONCURRENT_CONVERSIONS} 个并发转换")
57
+
58
  # --- 2. API 参数类型定义 ---
59
 
60
  # 定义 API 路径中允许的目标格式
 
344
  command_str = ' '.join(cmd)
345
  logger.info(f"正在执行命令: {command_str}")
346
 
347
+ # 7. 异步执行 Magick 命令 (使用信号量限制并发)
348
+ async with conversion_semaphore:
349
+ logger.info(f"获取并发许可,开始ImageMagick处理")
350
+ process = await asyncio.subprocess.create_subprocess_exec(
351
+ *cmd,
352
+ stdout=asyncio.subprocess.PIPE,
353
+ stderr=asyncio.subprocess.PIPE
354
+ )
355
+ stdout, stderr = await asyncio.wait_for(
356
+ process.communicate(),
357
+ timeout=TIMEOUT_SECONDS
358
+ )
359
 
360
  # 8. 检查命令执行结果
361
  if process.returncode != 0: