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

如何确保Django Channels启动时代码仅执行一次(兼容多Worker)

我之前在做Django Channels项目时也踩过这个坑!AppConfig.ready在多worker环境下(不管是runserver自动重载还是daphne多进程)确实会被每个进程执行一遍,导致初始化代码重复运行——比如我当时注册的定时任务多跑了好几个实例,排查了半天才找到原因。下面分享几个亲测有效的方案,既能兼容开发服务器,又能适配daphne多worker场景:

方案1:本地文件锁(适合单服务器/开发环境)

利用文件排他锁的特性,确保只有第一个启动的进程能执行初始化代码,适合开发环境或者单机器部署的生产场景。

from django.apps import AppConfig
import fcntl
import os
import sys

class MyAppConfig(AppConfig):
    name = 'myapp'

    def ready(self):
        # 跳过runserver自动重载的子进程(开发环境专属)
        if 'runserver' in sys.argv and 'reload' in sys.argv:
            return

        # 定义锁文件路径,建议放在项目根目录或系统临时目录
        lock_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'app_init.lock')
        lock_file = open(lock_file_path, 'w')
        
        try:
            # 尝试获取非阻塞排他锁
            fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
            # --------------------------
            # 这里放你需要只执行一次的代码
            print("初始化操作执行成功,仅运行一次!")
            # --------------------------
        except BlockingIOError:
            # 其他进程获取锁失败,直接跳过
            pass
        finally:
            # 无需手动关闭文件,进程退出时会自动释放锁
            pass

方案2:Redis分布式锁(适合多服务器/容器化部署)

如果是分布式环境(多机器、Docker集群),文件锁就失效了,这时候用Redis的原子操作实现分布式锁,能跨机器确保只有一个进程执行初始化。

首先安装依赖:pip install redis

from django.apps import AppConfig
import redis
import sys
from django.conf import settings

class MyAppConfig(AppConfig):
    name = 'myapp'

    def ready(self):
        # 跳过runserver重载进程
        if 'runserver' in sys.argv and 'reload' in sys.argv:
            return

        # 连接Redis(配置从settings里取)
        r = redis.Redis(
            host=settings.REDIS_HOST,
            port=settings.REDIS_PORT,
            db=settings.REDIS_DB,
            password=settings.REDIS_PASSWORD
        )
        lock_key = "myapp_global_init_lock"
        
        # 设置锁:nx=True表示仅当key不存在时才设置成功,ex=30是锁的过期时间(防止进程意外退出锁未释放)
        lock_acquired = r.set(lock_key, "locked", nx=True, ex=30)
        
        if lock_acquired:
            try:
                # --------------------------
                # 这里放分布式环境下的初始化代码
                print("分布式环境初始化完成,仅运行一次!")
                # --------------------------
            finally:
                # 执行完后手动释放锁
                r.delete(lock_key)
        else:
            # 其他进程获取锁失败,跳过执行
            pass

方案3:Channels Worker ID标识(仅Channels环境可用)

Channels启动worker时会给每个进程设置CHANNEL_WORKER_ID环境变量,第一个worker的ID是0,我们可以只让ID为0的进程执行初始化。

from django.apps import AppConfig
import os
import sys

class MyAppConfig(AppConfig):
    name = 'myapp'

    def ready(self):
        # 跳过runserver重载进程
        if 'runserver' in sys.argv and 'reload' in sys.argv:
            return

        # 仅让第一个Channels Worker执行初始化
        worker_id = os.environ.get('CHANNEL_WORKER_ID', '0')
        if worker_id == '0':
            # --------------------------
            # 这里放初始化代码
            print("仅第一个Channels Worker执行初始化!")
            # --------------------------

额外建议

不管用哪种方案,都尽量把初始化代码做成幂等的——也就是重复执行也不会产生副作用(比如重复创建数据表、重复注册定时任务),这样即使偶尔出现多次执行的情况,也不会导致系统故障。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:41:42