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

Django服务每日执行函数:实现令牌自动更新的最优方案

Python/Django 环境下实现每日令牌更新的高效安全方案

在Python/Django环境里,没法像JS那样用setInterval在Web进程里维持长期定时任务——因为Django的请求是短生命周期的,进程可能随时重启或回收。推荐用定时任务框架来实现每日更新令牌的需求,下面是几种成熟的实现方式:

方案一:Celery + Celery Beat(生产级首选)

Celery是Python生态最成熟的异步任务框架,Celery Beat专门负责定时任务调度,适合高并发、多实例的生产环境。

1. 安装依赖

pip install celery redis

(Redis作为消息队列,也可替换为RabbitMQ)

2. 配置Celery

在项目根目录创建celery.py:

import os
from celery import Celery
from celery.schedules import crontab

os.environ.setdefault('DJANGO_SETTINGS_MODULE', '你的项目名.settings')

app = Celery('你的项目名')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

# 配置每日定时任务(凌晨2点执行)
app.conf.beat_schedule = {
    'daily-refresh-expiring-tokens': {
        'task': '你的app名.tasks.refresh_expiring_tokens',
        'schedule': crontab(hour=2, minute=0),
    },
}

3. 定义刷新任务

在app目录下创建tasks.py:

from celery import shared_task
from django.utils import timezone
from .models import SomeModel
import requests

@shared_task
def refresh_expiring_tokens():
    # 筛选今日过期的令牌实例
    today = timezone.now().date()
    expiring_instances = SomeModel.objects.filter(
        refresh_token_expiry_date__date=today
    )
    
    for instance in expiring_instances:
        try:
            # 调用第三方API刷新令牌
            response = requests.post(
                '第三方API刷新地址',
                data={'refresh_token': instance.refresh_token}
            )
            response.raise_for_status()
            token_data = response.json()
            
            # 仅更新需要修改的字段,提升性能
            instance.access_token = token_data['access_token']
            instance.refresh_token = token_data['refresh_token']
            instance.refresh_token_expiry_date = timezone.now() + timezone.timedelta(weeks=2)
            instance.save(update_fields=['access_token', 'refresh_token', 'refresh_token_expiry_date'])
        except Exception as e:
            # 记录错误,避免单个实例失败中断整个任务
            print(f"实例{instance.id}令牌刷新失败: {str(e)}")

4. 启动服务

  • 启动Redis服务
  • 启动Celery Worker:celery -A 你的项目名 worker --loglevel=info
  • 启动Celery Beat:celery -A 你的项目名 beat --loglevel=info

方案二:Django Q(轻量替代方案)

如果不想引入Celery这类重型框架,Django Q是更轻量的选择,直接集成到Django生态中,无需额外的消息队列服务(可使用Django数据库作为任务存储)。

1. 安装配置

pip install django-q

在settings.py的INSTALLED_APPS中添加django_q,并配置任务集群:

Q_CLUSTER = {
    'name': 'DjangoORM',
    'workers': 4,
    'timeout': 90,
    'retry': 120,
    'queue_limit': 50,
    'orm': 'default'  # 使用Django数据库存储任务
}

2. 定义并注册定时任务

在app的tasks.py中:

from django_q.tasks import schedule
from django.utils import timezone
from .models import SomeModel
import requests

def refresh_expiring_tokens():
    today = timezone.now().date()
    expiring_instances = SomeModel.objects.filter(
        refresh_token_expiry_date__date=today
    )
    
    for instance in expiring_instances:
        try:
            response = requests.post(
                '第三方API刷新地址',
                data={'refresh_token': instance.refresh_token}
            )
            response.raise_for_status()
            token_data = response.json()
            
            instance.access_token = token_data['access_token']
            instance.refresh_token = token_data['refresh_token']
            instance.refresh_token_expiry_date = timezone.now() + timezone.timedelta(weeks=2)
            instance.save(update_fields=['access_token', 'refresh_token', 'refresh_token_expiry_date'])
        except Exception as e:
            print(f"实例{instance.id}令牌刷新失败: {str(e)}")

# 注册每日凌晨2点执行的任务
schedule(
    '你的app名.tasks.refresh_expiring_tokens',
    schedule_type='D',
    hour=2,
    minute=0
)

3. 启动任务集群

python manage.py qcluster

方案三:系统Cron + Django管理命令(无第三方依赖)

如果不想引入任何异步框架,可直接用系统Cron定时执行Django管理命令,适合小型项目或资源受限的环境。

1. 创建管理命令

在app目录下创建management/commands/refresh_tokens.py(需先创建management和commands目录,每个目录都要有空的__init__.py):

from django.core.management.base import BaseCommand
from django.utils import timezone
from ...models import SomeModel
import requests

class Command(BaseCommand):
    help = '每日刷新即将过期的令牌'

    def handle(self, *args, **options):
        today = timezone.now().date()
        expiring_instances = SomeModel.objects.filter(
            refresh_token_expiry_date__date=today
        )
        
        for instance in expiring_instances:
            try:
                response = requests.post(
                    '第三方API刷新地址',
                    data={'refresh_token': instance.refresh_token}
                )
                response.raise_for_status()
                token_data = response.json()
                
                instance.access_token = token_data['access_token']
                instance.refresh_token = token_data['refresh_token']
                instance.refresh_token_expiry_date = timezone.now() + timezone.timedelta(weeks=2)
                instance.save(update_fields=['access_token', 'refresh_token', 'refresh_token_expiry_date'])
                
                self.stdout.write(self.style.SUCCESS(f"实例{instance.id}令牌刷新成功"))
            except Exception as e:
                self.stderr.write(self.style.ERROR(f"实例{instance.id}令牌刷新失败: {str(e)}"))

2. 配置Cron

运行crontab -e添加定时任务:

0 2 * * * /虚拟环境路径/bin/python /项目路径/manage.py refresh_tokens

(每天凌晨2点执行)

关键注意事项

  • 批量优化:若实例数量较多,可分批次处理或使用批量更新操作,避免占用过多数据库连接。
  • 锁机制:多实例部署时,需添加分布式锁(如Redis锁)或数据库锁,防止任务重复执行。
  • 时区一致性:确保Django时区配置与第三方API时区一致,避免日期判断错误。
  • 日志记录:建议使用Django的logging模块代替print,方便后续排查问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 04:24:25