Django集成OpenCV出现504超时及Apache卡顿问题排查求助
Django集成OpenCV后请求超时(504 Gateway Timeout)与Apache卡顿问题排查与优化
兄弟,我太懂你这种踩坑的感受了——把CPU密集的CV/OCR逻辑直接塞在同步Django请求里,Apache的工作进程分分钟被堵死,超时、卡顿都是必然结果!咱们一步步拆解问题,给你落地的优化方案:
问题根源分析
你的代码本身逻辑没问题,但犯了Web开发里的经典错误:
- 同步请求阻塞:Django默认是同步处理请求,
get_string里的图像解码、旋转、OCR全是CPU密集型操作,每个请求会占着一个Apache进程不放,并发几个请求服务器直接卡死 - 不必要的磁盘IO:每次处理都写临时文件再读取,磁盘读写的开销拖慢了整体速度
- Tesseract调用开销:每次调用
image_to_string都会重新初始化Tesseract,启动成本很高
优化方案(按优先级排序)
1. 把CV/OCR移到异步任务队列(最关键)
用Celery+Redis把重计算的活扔到后台,Web服务器只处理轻量的请求转发,彻底解决阻塞问题。
步骤1:安装依赖
pip install celery redis
步骤2:配置Celery(在Django的settings.py里)
CELERY_BROKER_URL = 'redis://localhost:6379/0' CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' CELERY_ACCEPT_CONTENT = ['json'] CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json'
步骤3:创建异步任务(新建tasks.py)
from celery import shared_task from PIL import Image import numpy as np import cv2 import pytesseract @shared_task def get_string_task(image_bytes, image_name): # 图像解码 image = np.asarray(bytearray(image_bytes), dtype="uint8") image = cv2.imdecode(image, cv2.IMREAD_COLOR) # 缩小图像尺寸(减少计算量) max_dim = 1000 h, w = image.shape[:2] if max(h, w) > max_dim: scale = max_dim / max(h, w) image = cv2.resize(image, (int(w*scale), int(h*scale)), interpolation=cv2.INTER_AREA) # 灰度化与反色 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) gray = cv2.bitwise_not(gray) # 阈值处理 thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 计算旋转角度 coords = np.column_stack(np.where(thresh > 0)) angle = cv2.minAreaRect(coords)[-1] if angle < -45: angle = -(90 + angle) else: angle = -angle # 旋转校正(用更快的INTER_LINEAR插值) (h, w) = image.shape[:2] center = (w // 2, h // 2) M = cv2.getRotationMatrix2D(center, angle, 1.0) rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE) # 再次灰度阈值处理 gray_rotated = cv2.cvtColor(rotated, cv2.COLOR_BGR2GRAY) gray_rotated = cv2.threshold(gray_rotated, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 直接用内存对象给Tesseract,去掉临时文件 pil_image = Image.fromarray(gray_rotated) text = pytesseract.image_to_string(pil_image, lang='spa') return text
步骤4:修改视图逻辑
from django.http import JsonResponse from celery.result import AsyncResult from .tasks import get_string_task def process_image(request): if request.method == 'POST' and request.FILES.get('imagen'): imagen = request.FILES['imagen'] image_bytes = imagen.read() # 提交异步任务 task = get_string_task.delay(image_bytes, imagen.name) # 返回任务ID,前端可轮询结果 return JsonResponse({'task_id': task.id}) return JsonResponse({'error': 'Invalid request'}, status=400) # 新增获取任务结果的视图 def get_task_result(request, task_id): result = AsyncResult(task_id) if result.ready(): return JsonResponse({'text': result.get()}) else: return JsonResponse({'status': '处理中...'}, status=202)
2. 去掉临时文件IO(已包含在上面的任务里)
原来的代码里写临时文件再读取完全没必要——把OpenCV处理后的numpy数组直接转成PIL Image传给Tesseract,省掉磁盘读写的耗时。
3. 临时优化Apache配置(治标)
如果暂时不想上异步,可以先调整Apache的MPM模块配置,提升并发能力(以mpm_prefork为例):
<IfModule mpm_prefork_module> StartServers 8 MinSpareServers 5 MaxSpareServers 15 MaxRequestWorkers 200 MaxConnectionsPerChild 1000 </IfModule>
但注意:这只是缓解,本质问题还是同步阻塞,高并发下还是会超时。
4. 微调CV操作性能
- 用
cv2.INTER_LINEAR替代cv2.INTER_CUBIC做旋转插值,速度更快,OCR精度足够 - 预加载Tesseract语言包:确保
lang='spa'的语言包已经安装,避免每次调用都下载
最后总结
核心思路就是把CPU密集型操作从Web请求的同步链路中剥离,让Apache只处理轻量的请求转发,重活交给Celery worker去做。配合内存IO优化和CV操作微调,就能彻底解决超时和卡顿问题。
内容的提问来源于stack exchange,提问作者Miguel Vargas
相关产品推荐
相关产品推荐

