You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Eventlet与Docker部署Flask-SocketIO应用时遭遇语法错误

部署Flask-SocketIO应用到Render时的Eventlet/Gunicorn兼容错误

我有一个演示客户端摄像头视频流的Flask应用,本地运行正常,但在Render上通过Docker结合Gunicorn部署时,出现以下错误:

flask-client-camera  | 
flask-client-camera  | Error: class uri 'eventlet' invalid or not found: 
flask-client-camera  | 
flask-client-camera  | [Traceback (most recent call last):
flask-client-camera  |   File "/usr/local/lib/python3.8/site-packages/gunicorn/util.py", line 124, in load_class
flask-client-camera  |     return pkg_resources.load_entry_point("gunicorn",
flask-client-camera  |   File "/usr/local/lib/python3.8/site-packages/pkg_resources/__init__.py", line 534, in load_entry_point
flask-client-camera  |     return get_distribution(dist).load_entry_point(group, name)
flask-client-camera  |   File "/usr/local/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2930, in load_entry_point
flask-client-camera  |     return ep.load()
flask-client-camera  |   File "/usr/local/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2517, in load
flask-client-camera  |     return self.resolve()
flask-client-camera  |   File "/usr/local/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2523, in resolve
flask-client-camera  |     module = __import__(self.module_name, fromlist=['__name__'], level=0)
flask-client-camera  |   File "/usr/local/lib/python3.8/site-packages/gunicorn/workers/geventlet.py", line 18
flask-client-camera  |     from gunicorn.workers.async import AsyncWorker
flask-client-camera  |                           ^
flask-client-camera  | SyntaxError: invalid syntax
flask-client-camera  | ]

相关配置文件

Dockerfile

FROM python:3.8.13-slim-bullseye

WORKDIR /app

RUN apt-get -y update && apt-get install -y \
  wget \
  ffmpeg \
  libsm6 \
  libxext6

RUN pip install --upgrade setuptools 

COPY requirements.txt .

RUN pip install -r requirements.txt

ADD . . 

CMD gunicorn --worker-class eventlet -w 1 app:app

Flask应用代码(app.py)

import base64
import os

import cv2
import numpy as np
from flask import Flask, render_template, send_from_directory
from flask_socketio import SocketIO, emit

app = Flask(__name__, static_folder="./templates/static")
app.config["SECRET_KEY"] = "secret!"
socketio = SocketIO(app)


@app.route("/favicon.ico")
def favicon():
    """
    The favicon function serves the favicon.ico file from the static directory.
    
    :return: A favicon
    """
    return send_from_directory(
        os.path.join(app.root_path, "static"),
        "favicon.ico",
        mimetype="image/vnd.microsoft.icon",
    )


def base64_to_image(base64_string):
    """
    The base64_to_image function accepts a base64 encoded string and returns an image.
    The function extracts the base64 binary data from the input string, decodes it, converts 
    the bytes to numpy array, and then decodes the numpy array as an image using OpenCV.
    
    :param base64_string: Pass the base64 encoded image string to the function
    :return: An image
    """
    base64_data = base64_string.split(",")[1]
    image_bytes = base64.b64decode(base64_data)
    image_array = np.frombuffer(image_bytes, dtype=np.uint8)
    image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
    return image


@socketio.on("connect")
def test_connect():
    """
    The test_connect function is used to test the connection between the client and server.
    It sends a message to the client letting it know that it has successfully connected.
    
    :return: A 'connected' string
    """
    print("Connected")
    emit("my response", {"data": "Connected"})


@socketio.on("image")
def receive_image(image):
    """
    The receive_image function takes in an image from the webcam, converts it to grayscale, and then emits
    the processed image back to the client.


    :param image: Pass the image data to the receive_image function
    :return: The image that was received from the client
    """
    # Decode the base64-encoded image data
    image = base64_to_image(image)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    frame_resized = cv2.resize(gray, (640, 360))
    encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]
    result, frame_encoded = cv2.imencode(".jpg", frame_resized, encode_param)
    processed_img_data = base64.b64encode(frame_encoded).decode()
    b64_src = "data:image/jpg;base64,"
    processed_img_data = b64_src + processed_img_data
    emit("processed_image", processed_img_data)


@app.route("/")
def index():
    """
    The index function returns the index.html template, which is a landing page for users.
    
    :return: The index
    """
    return render_template("index.html")


if __name__ == "__main__":
    socketio.run(app, port=os.getenv("PORT", default=5000), debug=os.getenv("DEBUG", default=True), host='0.0.0.0')

项目目录结构

├── app.py
├── Dockerfile
├── LICENSE.md
├── README.md
├── render.yaml
├── requirements.txt
└── templates
    ├── index.html
    └── static
        ├── favicon.ico
        └── script.js

requirements.txt

Flask-SocketIO==4.3.1
python-engineio==3.13.2
python-socketio==4.6.0
Flask==2.0.3
Werkzeug==2.0.3
opencv_python==4.7.0.68
numpy==1.24.2
gunicorn==18.0
eventlet==0.33.3

问题排查与修复

核心原因

gunicorn 18.0是发布于2014年的老旧版本,它的geventlet.py文件使用了Python2风格的相对导入语法,在Python3.8环境下会触发SyntaxError,同时旧版gunicorn对eventlet的兼容性也较差。

修复步骤

  1. 更新gunicorn版本:将requirements.txt中的gunicorn==18.0替换为gunicorn==20.1.0(该版本是兼容Python3.8+且支持eventlet worker的稳定版本)
  2. 可选优化:将eventlet升级到0.34.0,进一步提升兼容性
  3. 保持Dockerfile的CMD命令不变,依然使用gunicorn --worker-class eventlet -w 1 app:app
  4. 重新构建Docker镜像并部署到Render

修改后的requirements.txt核心依赖片段:

gunicorn==20.1.0
eventlet==0.34.0

内容的提问来源于stack exchange,提问作者Ifeanyi Nneji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 06:45:02