fix: 钉钉连接超时后自动重连失败 (#7924)

* fix: improve DingTalk adapter error handling in run() method

* fix: add retry logic for DingTalk SDK task unexpected exit

* fix: use task.add_done_callback to wake thread on task completion, handle UnboundLocalError

* refactor: extract retry logic into handle_retry helper function

---------

Co-authored-by: Blueteemo <Blueteemo@users.noreply.github.com>
This commit is contained in:
千岚之夏
2026-05-03 15:09:49 +08:00
committed by GitHub
parent 9e09299dcb
commit 859ab28d43

View File

@@ -1,6 +1,7 @@
import asyncio
import json
import threading
import time
import uuid
from pathlib import Path
from typing import Literal, NoReturn, cast
@@ -747,18 +748,59 @@ class DingtalkPlatformAdapter(Platform):
async def run(self) -> None:
# await self.client_.start()
# 钉钉的 SDK 并没有实现真正的异步start() 里面有堵塞方法。
# SDK 内部已有 while True 重连循环,但需要监控 task 状态,
# 如果 task 意外退出则重新启动。
MAX_RETRIES = 5
RETRY_INTERVAL = 10
def start_client(loop: asyncio.AbstractEventLoop) -> None:
try:
self._shutdown_event = threading.Event()
task = loop.create_task(self.client_.start())
self._shutdown_event.wait()
if task.done():
task.result()
except Exception as e:
if "Graceful shutdown" in str(e):
logger.info("钉钉适配器已被关闭")
retry_count = 0
def handle_retry(error_msg: str) -> bool:
"""处理重试逻辑,返回 True 表示需要继续重试False 表示放弃。"""
nonlocal retry_count
logger.error(error_msg)
retry_count += 1
if retry_count < MAX_RETRIES:
logger.info(f"钉钉适配器尝试重连 ({retry_count}/{MAX_RETRIES})...")
time.sleep(RETRY_INTERVAL)
return True
logger.error("钉钉适配器重连失败,已达最大重试次数")
return False
while retry_count < MAX_RETRIES:
task = None
try:
self._shutdown_event = threading.Event()
task = loop.create_task(self.client_.start())
# 当 task 完成时唤醒线程(无论是正常退出还是异常退出)
task.add_done_callback(lambda _: self._shutdown_event.set())
self._shutdown_event.wait()
if task.done():
try:
exc = task.exception()
except asyncio.CancelledError:
logger.info("钉钉适配器 task 已取消")
return
if exc:
if "Graceful shutdown" in str(exc):
logger.info("钉钉适配器已被关闭")
return
if handle_retry(f"钉钉 SDK task 异常退出: {exc}"):
continue
return
# task 仍在运行shutdown_event 被设置(正常关闭)
return
logger.error(f"钉钉机器人启动失败: {e}")
except Exception as e:
if "Graceful shutdown" in str(e):
logger.info("钉钉适配器已被关闭")
return
if not handle_retry(f"钉钉机器人启动失败: {e}"):
return
finally:
# 仅在重试/失败路径取消 task正常关闭不取消
if task is not None and not task.done() and retry_count > 0:
task.cancel()
loop = asyncio.get_running_loop()
await loop.run_in_executor(None, start_client, loop)