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

Telethon结合Quart使用全局变量导致WebSocket阻塞问题求助

问题解决方法

核心故障原因

  • 你在异步协程中使用了同步阻塞的 time.sleep(1),会直接卡住整个asyncio事件循环,导致Telethon的消息监听、Quart的请求处理等所有异步任务都无法执行。你加了if x != 1判断后,因为事件循环被堵死,NewMessage事件永远得不到触发,x永远是初始值1,进入死循环状态,程序完全失去响应。
  • 同步文件写入操作也会阻塞事件循环,加剧卡顿问题。
  • 全局变量轮询方案效率低下,且存在多协程读写的线程安全隐患。

具体修改方案

1. 替换同步sleep为异步sleep

所有协程内的等待操作都要使用 asyncio.sleep 并加await关键字,绝对不能使用time.sleep。

2. 用asyncio.Event做消息通知替代轮询

不用每秒轮询全局变量x的值,通过Event实现收到消息后主动通知WebSocket协程,性能更高也不会浪费资源。

3. 替换同步文件写入为异步写入

使用aiofiles库实现异步文件操作,避免阻塞事件循环。

4. 修正命名冲突

你的WebSocket路由函数名random和导入的随机数库重名,会引发隐性bug,建议修改为其他名称。

修改后的代码参考

from telethon import TelegramClient, connection, events, utils
import hypercorn.asyncio
from quart import Quart, websocket
import asyncio
import aiofiles
import random

from variables import api_id, api_hash

SESSION = 'quart'
client = TelegramClient(SESSION, api_id, api_hash)
client.parse_mode = 'html'  

teste_html = """
<!doctype html>
<html>
  <head>
    <title>Quart + Telethon</title>
    <link rel="stylesheet" type="text/css" href="/static/style.css">
  </head>
  <body>
    <h1>Página de teste!</h1>
    <p>No momento, estamos testando o websocket</p>
    <ul></ul>
    <script type="text/javascript">
    let socket = new WebSocket('ws://localhost:8000/ws');

    socket.onmessage = function(event) {
        var messages_dom = document.getElementsByTagName('ul')[0];
        var message_dom = document.createElement('li');
        var cotent_dom = document.createTextNode('Received: ' + event.data);
        message_dom.appendChild(cotent_dom);
        messages_dom.appendChild(message_dom);
    };
    </script>
    
  </body>
</html>
"""

app = Quart(__name__)
lock = asyncio.Lock()
# 新增消息通知Event
new_msg_event = asyncio.Event()
x = None

@app.before_serving
async def startup():
    await client.connect()

@app.after_serving
async def cleanup():
    await client.disconnect()

@client.on(events.NewMessage)
async def receber(event):
    global x
    try:
        sender = await event.get_sender()
        name = utils.get_display_name(sender)
        message = name + "::::::" + event.text + "\n"
        async with lock:
            x = message
            # 异步写文件
            async with aiofiles.open('msg.txt', 'a', encoding='utf-8') as f:
                await f.write(message)
        # 通知所有等待的WebSocket协程有新消息
        new_msg_event.set()
    except KeyboardInterrupt:
        print("Adeus!")
    except Exception as e:
        print("\n======ERRO======\n")
        print(e)

@app.websocket('/ws')
async def ws_endpoint():
    global x
    try:
        while True:
            # 等待新消息通知,不会阻塞事件循环
            await new_msg_event.wait()
            print(x)
            data = str(random.randint(0,5))
            await websocket.send(f"Quart enviou 1: {data}")
            # 异步等待1秒
            await asyncio.sleep(1)
            # 重置Event,等待下一条消息
            new_msg_event.clear()
    except Exception as e:
        print(e)
        print("erro 2")

@app.route('/')
async def hello_world():
    await client.send_message('me', 'Hello World')
    return teste_html

async def main():
    await hypercorn.asyncio.serve(app, hypercorn.Config())

if __name__ == '__main__':
    try:
        client.loop.run_until_complete(main())
    except KeyboardInterrupt:
        print("Adeus!")
    except Exception as e:
        print(e)

更优的实现方案

如果你的需求是收到Telegram消息就直接推送给前端,不需要每秒发随机数,可以直接维护一个活跃WebSocket连接集合,收到新消息时遍历所有连接主动推送对应内容,完全不需要轮询:

  • 定义一个全局集合 active_websockets = set()
  • WebSocket连接建立时把当前websocket对象加入集合,断开时移除
  • 收到Telegram新消息时,遍历集合调用每个连接的send()方法推送消息内容即可。

内容的提问来源于stack exchange,提问作者Joao Pedro Lourenco Affonso

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 08:36:03