GraiaProject / Application

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

ws开启时shutdown报错 #53

Closed NekodRider closed 4 years ago

NekodRider commented 4 years ago

描述你遇到的问题: 开启 websocket 时,使用 CTRL+C 退出需要按两次:第一次输出 application shutdowned.;第二次输出报错 Task exception was never retrieved。 PS: 我在 win10 下运行是正常的,不知道为什么 ubuntu 服务器上就会这样,可能跟 asyncio 在不同系统上面的实现有关。网上搜了下,相关的有个这个 https://github.com/python/asyncio/issues/341

复现步骤: 入门文档 中的代码再给 app.launch_blocking() 加上 try/except KeyboardInterrupt

root@HostKvm-9cf9b6:~# python3.7 bot.py
[2020-11-09 00:10:42,700][INFO]: initializing app...
[2020-11-09 00:10:42,724][INFO]: detecting remote's version...
[2020-11-09 00:10:42,736][INFO]: detected remote's version: 1.8.4
[2020-11-09 00:10:42,739][INFO]: using pure websocket to receive event
[2020-11-09 00:10:42,739][INFO]: event receive method checked.
[2020-11-09 00:10:42,739][INFO]: this application's initialization has been completed.
[2020-11-09 00:10:42,740][INFO]: --- setting start ---
[2020-11-09 00:10:42,740][INFO]: broadcast using: <graia.broadcast.Broadcast object at 0x7f9ee7f9a4d0>
[2020-11-09 00:10:42,740][INFO]: enable log of chat: yes
[2020-11-09 00:10:42,740][INFO]: debug: no
[2020-11-09 00:10:42,740][INFO]: version(remote): 1.8.4
[2020-11-09 00:10:42,740][INFO]: --- setting end ---
[2020-11-09 00:10:42,741][INFO]: application has been initialized, used 0.0404s
^C[2020-11-09 00:10:47,871][INFO]: application shutdowned.
^C[2020-11-09 00:10:51,304][ERROR]: Task exception was never retrieved
future: <Task finished coro=<GraiaMiraiApplication.ws_all_poster() done, defined at /usr/local/lib/python3.7/dist-packages/graia/application/__init__.py:957> exception=KeyboardInterrupt()>
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/graia/application/__init__.py", line 1080, in launch_blocking
    loop.run_until_complete(self.getFetching()())
  File "/usr/lib/python3.7/asyncio/base_events.py", line 566, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 534, in run_forever
    self._run_once()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 1735, in _run_once
    event_list = self._selector.select(timeout)
  File "/usr/lib/python3.7/selectors.py", line 468, in select
    fd_event_list = self._selector.poll(timeout, max_ev)
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "bot.py", line 29, in <module>
    app.launch_blocking()
  File "/usr/local/lib/python3.7/dist-packages/graia/application/__init__.py", line 1083, in launch_blocking
    loop.run_until_complete(self.shutdown())
  File "/usr/lib/python3.7/asyncio/base_events.py", line 566, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 534, in run_forever
    self._run_once()
  File "/usr/lib/python3.7/asyncio/base_events.py", line 1771, in _run_once
    handle._run()
  File "/usr/lib/python3.7/asyncio/events.py", line 88, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/local/lib/python3.7/dist-packages/graia/application/__init__.py", line 964, in ws_all_poster
    received_data = await connection.receive_json()
  File "/usr/local/lib/python3.7/dist-packages/aiohttp/client_ws.py", line 291, in receive_json
    data = await self.receive_str(timeout=timeout)
  File "/usr/local/lib/python3.7/dist-packages/aiohttp/client_ws.py", line 274, in receive_str
    msg = await self.receive(timeout)
KeyboardInterrupt

建议: 我自己简单改了下 graia/application/__init__.py 里的 shutdown

    async def shutdown(self):
        if self.broadcast is not None:
            loop = self.broadcast.loop
            try:
                await self.broadcast.layered_scheduler(
                    listener_generator=self.broadcast.default_listener_generator(ApplicationShutdowned),
                    event=ApplicationShutdowned(self)
                )
            except:
                self.logger.error("it seems our shutdown operator has been failed...check the remote alive.")
                traceback.print_exc()
        else:
            loop = loop or asyncio.get_event_loop()
        for t in asyncio.all_tasks(loop):
            if t == asyncio.current_task(loop):
                continue
            t.cancel()
            try:
                await t
            except asyncio.CancelledError:
                pass
        await self.signout()
        await self.session.close()
        self.logger.info("application shutdowned.")
GreyElaina commented 4 years ago

正常情况, 顺便, 建议提起 Pull Request 帮助改进

NekodRider commented 4 years ago

不知道这样处理合不合理来着就没发pr,明天补一个