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
相关产品推荐
相关产品推荐

