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

在Django应用中运行python-telegram-bot避免阻塞服务器的方案咨询

在Django中集成python-telegram-bot避免阻塞服务器的解决方案

问题描述

尝试在基于Django框架的Web应用中运行python-telegram-bot,按照Telegram库示例调用application.run_polling()方法时,该方法会阻塞Web服务器,想替换为更优方案却无从下手。

根据官方文档提示:

在将python-telegram-bot与其他基于asyncio的框架结合时,使用此方法并非最佳选择,因为它会阻塞事件循环直到收到停止信号。你可以手动调用以下方法来启动和关闭应用及更新器,维持事件循环运行并监听停止信号由你自行处理。

文档还列出了相关方法:

另请参阅:initialize(), start(), stop(), shutdown() telegram.ext.Updater.start_polling(), telegram.ext.Updater.stop(), run_webhook()

尝试理解这些方法后,仍无法实现python-telegram-bot在后台运行且不影响主服务器,需要此类场景的实现方案或详细步骤。

附当前使用的代码:

import asyncio

from django.apps import AppConfig
from telegram import Update
from telegram.ext import ContextTypes, Application, CommandHandler

from TaixTracking.configApp import ConfigApp


async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_text("Help!")


async def start_telegram():
    application = Application.builder().token(ConfigApp().get_value('telegram', 'token', '')).build()
    application.add_handler(CommandHandler("help", help_command))

    await application.initialize()
    await application.start()
    await application.updater.start_polling()
    print('Telegram load')


class CommunicationConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'communication'

    def ready(self):
        if not ConfigApp().is_telegram_launch():
            ConfigApp().set_telegram_launch(True)
            asyncio.run(start_telegram())

解决方案

方案1:使用线程单独运行Telegram Bot事件循环

Django的ready()方法为同步执行逻辑,直接调用asyncio.run()会阻塞主线程。可将Bot的异步逻辑放到单独线程中运行,避免影响Web服务器。

修改后代码:

import asyncio
import threading

from django.apps import AppConfig
from telegram import Update
from telegram.ext import ContextTypes, Application, CommandHandler

from TaixTracking.configApp import ConfigApp


async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_text("Help!")


async def start_telegram():
    application = Application.builder().token(ConfigApp().get_value('telegram', 'token', '')).build()
    application.add_handler(CommandHandler("help", help_command))

    await application.initialize()
    await application.start()
    # 启用Polling并允许丢弃未处理的更新
    await application.updater.start_polling(drop_pending_updates=True)
    # 保持事件循环运行直到Bot收到停止信号
    await application.idle()


def run_telegram_thread():
    # 为线程创建独立的asyncio事件循环
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(start_telegram())


class CommunicationConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'communication'

    def ready(self):
        if not ConfigApp().is_telegram_launch():
            ConfigApp().set_telegram_launch(True)
            # 启动守护线程运行Bot,Django退出时线程自动结束
            telegram_thread = threading.Thread(target=run_telegram_thread, daemon=True)
            telegram_thread.start()

关键改动:

  • 为Bot创建独立线程,避免阻塞Django主线程
  • 使用application.idle()维持事件循环持续运行
  • 设置线程为守护线程,保证Django退出时线程自动终止

方案2:改用WebHook替代Polling(生产环境推荐)

Polling需要持续请求Telegram服务器,WebHook则由Telegram主动推送更新到Django接口,更高效且不会占用线程持续运行。

步骤1:配置Telegram Bot的WebHook地址

新建Django管理命令完成WebHook配置:

# 在communication/management/commands下新建set_webhook.py
from django.core.management.base import BaseCommand
from telegram import Bot
from TaixTracking.configApp import ConfigApp

class Command(BaseCommand):
    help = '设置Telegram Bot的WebHook地址'

    def handle(self, *args, **options):
        bot_token = ConfigApp().get_value('telegram', 'token', '')
        # 替换为你的Django应用公网地址
        webhook_url = "https://your-domain.com/telegram/webhook/"
        bot = Bot(token=bot_token)
        bot.set_webhook(url=webhook_url)
        self.stdout.write(self.style.SUCCESS(f"WebHook已设置为:{webhook_url}"))

运行命令完成配置:python manage.py set_webhook

步骤2:创建接收WebHook的Django视图

# communication/views.py
from django.http import HttpResponse, HttpResponseBadRequest
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
from asgiref.sync import sync_to_async
from TaixTracking.configApp import ConfigApp

# 全局单例Bot应用实例,避免重复初始化
bot_application = Application.builder().token(ConfigApp().get_value('telegram', 'token', '')).build()

async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_text("Help!")

# 注册命令处理器
bot_application.add_handler(CommandHandler("help", help_command))

async def process_telegram_update(update_data):
    update = Update.de_json(update_data, bot_application.bot)
    await bot_application.process_update(update)

def telegram_webhook(request):
    if request.method == "POST":
        update_data = request.body.decode('utf-8')
        # 异步处理Telegram更新,不阻塞Django请求
        sync_to_async(process_telegram_update)(update_data)
        return HttpResponse("OK")
    else:
        return HttpResponseBadRequest()

步骤3:配置路由

# communication/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('telegram/webhook/', views.telegram_webhook, name='telegram_webhook'),
]

方案3:ASGI模式下整合事件循环(适合异步Django)

若使用Django 3.0+版本并搭配ASGI服务器(如Uvicorn),可直接将Bot任务加入Django的事件循环,无需额外线程:

import asyncio
from django.apps import AppConfig
from telegram import Update
from telegram.ext import ContextTypes, Application, CommandHandler
from TaixTracking.configApp import ConfigApp

async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_text("Help!")

async def start_telegram():
    application = Application.builder().token(ConfigApp().get_value('telegram', 'token', '')).build()
    application.add_handler(CommandHandler("help", help_command))
    await application.initialize()
    await application.start()
    await application.updater.start_polling(drop_pending_updates=True)
    await application.idle()

class CommunicationConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'communication'

    def ready(self):
        if not ConfigApp().is_telegram_launch():
            ConfigApp().set_telegram_launch(True)
            # 获取ASGI服务器的事件循环并添加Bot任务
            loop = asyncio.get_event_loop()
            loop.create_task(start_telegram())

注意:此方案仅适用于ASGI运行模式,WSGI模式下无法获取asyncio事件循环。

关键注意事项

  • 生产环境优先选择WebHook方案,Polling仅适合开发调试场景
  • 确保Bot的Application实例为全局单例,避免重复初始化
  • 使用线程方案时,需通过配置或缓存标记Bot是否已启动,防止Django重启时重复创建线程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:52:40