## 问题描述 SDK 的异步调用路径中直接调用了基于同步 `requests` 且未设置 `timeout` 的方法。当到 api.dingtalk.com 的网络是"挂起"状态(TCP 半开,不报错也不返回)时,`requests` 会阻塞到 OS 层 TCP 超时(分钟级),导致整个 asyncio 事件循环冻结——进程内所有协程停止调度,且同步代码没有 await 点,`task.cancel()` 无法生效,进程表现为假死,只能强杀。 ## 具体位置 **1. `DingTalkStreamClient.start()` → `open_connection()`**(`dingtalk_stream/stream.py`) ```python # stream.py:65 — async def start() 的事件循环里直接同步调用 connection = self.open_connection() # stream.py:172 — open_connection() 内部,同步 requests 且无 timeout response = requests.post(DingTalkStreamClient.OPEN_CONNECTION_API, headers=request_headers, data=request_body) ``` **2. 各 `async_*` 卡片方法 → `get_access_token()`**(`card_replier.py` → `stream.py`) ```python # card_replier.py — async_create_and_send_card / async_streaming / # async_put_card_data 等异步方法开头均为: access_token = self.dingtalk_client.get_access_token() # stream.py:217 — get_access_token() 内部,同样是无 timeout 的 requests.post response = requests.post(url, headers=request_headers, data=json.dumps(values)) ``` 值得注意的是,卡片接口都有 aiohttp 实现的异步双版本(`streaming`/`async_streaming` 等),唯独 `open_connection` 和 `get_access_token` 没有异步版本,形成了"异步链路中的同步阻塞点"。 ## 影响 正常情况下无感(`get_access_token` 有约 2 小时缓存,`open_connection` 仅在(重)连时调用)。但触发点恰好都是服务最脆弱的时刻:启动建连、断线重连、token 过期刷新。一旦此时网络异常挂起,整个进程(包括与钉钉无关的其它协程)冻结数分钟,重连机制本身也失效。 ## 复现 ```python import asyncio from dingtalk_stream import Credential, DingTalkStreamClient async def main(): client = DingTalkStreamClient(Credential("fake", "fake")) task = asyncio.create_task(client.start()) await asyncio.sleep(1) task.cancel() # 网络不通时 cancel 无法打断阻塞中的 requests.post,这里超时 await asyncio.wait_for(task, timeout=5) asyncio.run(main()) ``` 在无法访问 api.dingtalk.com 的环境(或将该域名指向黑洞地址)下运行即可复现。 ## 建议 1. 为 `open_connection` / `get_access_token` 增加异步版本(SDK 已依赖 aiohttp),异步调用点切换过去;或最低成本地在调用点用 `await asyncio.to_thread(...)` 包装; 2. 为所有 `requests` / `aiohttp` 调用设置显式 `timeout`。 ## 环境 - dingtalk-stream 0.24.3 - Python 3.11,Windows 11(与平台无关,Linux 同样存在)
Activity
TBice123123 commentedon Sep 4, 2026
看到 PR #79(已合并,0.24.4b1)已通过 run_in_executor + 显式 timeout 覆盖了这个问题,等 0.24.4 正式版发布。先关闭,正式版若仍有问题再 reopen。