GraiaProject / Application

一个设计精巧, 协议实现完备的, 基于 mirai-api-http 的即时聊天软件自动化框架.
https://graia-document.vercel.app/
GNU Affero General Public License v3.0
438 stars 63 forks source link

存在断开连接情况(超过5分钟左右) #84

Closed macle-abc closed 3 years ago

macle-abc commented 3 years ago

遇到的问题: 当距离发送最后一条群消息后,大概超过5分钟后发送一条群消息,Python程序会出现没有响应的情况,但是miria-api-http还是能成功获取到消息的

复现步骤: 该 BUG 会在进行以下操作后出现:

  1. 任意发送一条群消息
  2. 等待5分钟以上(中间不发送任何消息)
  3. 再次发送群消息

发生错误的代码

https://graia-document.vercel.app/docs/guides/features/kanata/kanata-readme 上述测试代码

控制台日志输出截图:

控制台 群消息 python程序

运行环境:

额外信息:

确切地说,问题应该叫做python程序阻塞在接收服务器推送的地方,

https://github.com/GraiaProject/Application/blob/a03a34dced116370831b1a4596f2cc2918dbae07/src/graia/application/__init__.py#L1363

async def websocket_daemon(self): while True: self.logger.info("websocket daemon: websocket connection starting...") try: await self.ws_all_poster() except aiohttp.client_exceptions.ClientConnectorError: self.logger.info( "websocket daemon: it seems that remote down, waiting for 10 seconds..." ) await asyncio.sleep(10) self.logger.info("websocket daemon: detected closed, restarting...")

async def ws_all_poster(self): async with self.session.ws_connect( str( URL(self.url_gen("all")).with_query( {"sessionKey": self.connect_info.sessionKey} ) ) ) as connection: self.logger.info("websocket: connected") while True: try: ws_message = await connection.receive() # 经过debug发现问题应该发生在这里 我尝试过使用timeout来设置超时,但是这样当服务器没有收到qq消息时,会导致python程序认为服务器没有推送消息而触发TimeoutError从而使得机器人重复回复消息

GreyElaina commented 3 years ago

我不觉得这是 graia 的问题......hmmmmmmmmmmm

macle-abc commented 3 years ago

我不觉得这是 graia 的问题......hmmmmmmmmmmm ...我个人猜测是aiohttp心跳包没有成功所导致的,因为https://github.com/GraiaProject/Application/blob/a03a34dced116370831b1a4596f2cc2918dbae07/src/graia/application/init.py#L1363 实际上依赖了aiohttp,https://github.com/aio-libs/aiohttp/blob/master/aiohttp/client_ws.py#L239 不过仅仅是推测

目前想到的解决方法是用户自己设计一个超时时间,不依赖于aiohttp,定时重启进程。虽然我也不知道会发生什么...是否有更好的解决方案呢

macle-abc commented 3 years ago

我不觉得这是 graia 的问题......hmmmmmmmmmmm emmmmmmmm,确实,ws连接断开原来有人提出了这个问题,不过建议,可以采纳node.js的做法改为Python版本

Drincann commented 3 years ago

我发现大部分依赖 mirai-api-http 的 WebSocket 接口的项目都存在这个问题。 大概因为,多数用作测试的机器人账号总能在五分钟内收到一条消息,这导致很难有人发现五分钟后连接会丢失。 即使发生,表面上看来也是令人头疼的毫无规律的 BUG。 目前的一个有效实践是,每分钟向服务端发送一个 ping 包。

ws.on('open', () => {
    // 每 60s 发个心跳
    const interval = setInterval(() => {
        ws.ping((err) => {
            if (err) {
                throw error;
            }
        });
    }, 60000);

    ws.on('close', (code, reason) => {
        // 关闭心跳
        clearInterval(interval);
        close(code, reason);
    });
});
GreyElaina commented 3 years ago

我发现大部分依赖 mirai-api-http 的 WebSocket 接口的项目都存在这个问题。 大概因为,多数用作测试的机器人账号总能在五分钟内收到一条消息,这导致很难有人发现五分钟后连接会丢失。 即使发生,表面上看来也是令人头疼的毫无规律的 BUG。 目前的一个有效实践是,每分钟向服务端发送一个 ping 包。

ws.on('open', () => {
    // 每 60s 发个心跳
    const interval = setInterval(() => {
        ws.ping((err) => {
            if (err) {
                throw error;
            }
        });
    }, 60000);

    ws.on('close', (code, reason) => {
        // 关闭心跳
        clearInterval(interval);
        close(code, reason);
    });
});

graia 的 0.16.1 将尝试解决这个问题

GreyElaina commented 3 years ago

在本地的测试中已经通过每 30s 一次的 ping 解决问题.