【问题标题】:Can't stop aiohttp websocket server无法停止 aiohttp websocket 服务器
【发布时间】:2019-03-19 08:15:43
【问题描述】:

我无法从应用程序中取消我的 aiohttp websocket 服务器。当我收到“取消”字符串时,我想停止服务器并关闭 从客户端。是的,我明白了,我完成了我的协同例程 (websocket_handler),但是来自 aiohttp 库的三个协同例程仍在继续工作。

当然,我可以在我的协程结束时调用asyncio.get_event_loop().stop(),但是有没有优雅的方法来停止aiohttp服务器?

从我的代码可以看出我尝试使用Application().on_shutdown.append(),但失败了。

什么是正确的方法?

#!/usr/bin/env python # -- 编码:utf-8 -- 导入操作系统 导入异步 进口信号 导入弱引用

import aiohttp.web
from   aiohttp import ClientConnectionError, WSCloseCode

# This restores the default Ctrl+C signal handler, which just kills the process
#https://stackoverflow.com/questions/27480967/why-does-the-asyncios-event-loop-suppress-the-keyboardinterrupt-on-windows
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)

HOST = os.getenv('HOST', 'localhost')
PORT = int(os.getenv('PORT', 8881))

async def testhandle(request):
    #Сопрограмма одрабатывающая http-запрос по адресу "http://127.0.0.1:8881/test"
    print("server: into testhandle()")
    return aiohttp.web.Response(text='Test handle')

async def websocket_handler(request):
    #Сопрограмма одрабатывающая ws-запрос по адресу "http://127.0.0.1:8881"   
    print('Websocket connection starting')
    ws = aiohttp.web.WebSocketResponse()
    await ws.prepare(request)
    request.app['websockets'].add(ws)
    print('Websocket connection ready')
    try:
        async for msg in ws:
            if msg.type == aiohttp.WSMsgType.TEXT:
                if msg.data == 'close':
                    print(msg.data) 
                    break    
                else:
                    print(msg.data)
                    await ws.send_str("You said: {}".format(msg.data))
            elif msg.type == aiohttp.WSMsgType.ERROR:
                print('ws connection closed with exception %s' %
                    ws.exception())             
    except (asyncio.CancelledError, ClientConnectionError):   
        pass    # Тут оказываемся когда, клиент отвалился. 
                # В будущем можно тут освобождать ресурсы. 
    finally:
        print('Websocket connection closed')
        request.app['websockets'].discard(ws)
        #pending = asyncio.Task.all_tasks()
        #asyncio.get_event_loop().stop()
    return ws

async def on_shutdown(app):
    for ws in set(app['websockets']):
        await ws.close(code=WSCloseCode.GOING_AWAY, message='Server shutdown')   

def main():
    loop = asyncio.get_event_loop()
    app  = aiohttp.web.Application()
    app['websockets'] = weakref.WeakSet()
    app.on_shutdown.append(on_shutdown)  
    app.add_routes([aiohttp.web.get('/', websocket_handler)])        #, aiohttp.web.get('/test', testhandle)   

    try:
        aiohttp.web.run_app(app, host=HOST, port=PORT, handle_signals=True)
        print("after run_app")
    except Exception as exc:
        print ("in exception")
    finally:
        loop.close()

if __name__ == '__main__':
    main()

【问题讨论】:

    标签: python-3.x aiohttp


    【解决方案1】:

    https://docs.aiohttp.org/en/v3.0.1/web_reference.html#aiohttp.web.Application.shutdown

    app.shutdown()
    app.cleanup()
    

    关机后你也应该做cleanup()

    【讨论】:

    • 链接不再正确,app.shutdown() 和 app.cleanup() 除了调用相关回调之外似乎没有做任何事情。
    【解决方案2】:

    我相信正确的答案很简单:

    raise aiohttp.web.GracefulExit()
    

    在捕获异常后,它调用附加到on_shutdownon_cleanup 信号的所有处理程序并终止。

    One can see in the source code aiohttp.web.run_app() 等待两个异常:GracefulExitKeyboardInterrupt。虽然后者相当无趣,但跟踪GracefulExit 可以引导您到this place in web_runner.py,它将SIGINTSIGTERM 信号处理程序注册到raise GracefulExit() 的函数中。

    确实,我还设法通过提高自身的 signal.SIGINTsignal.SIGTERM 来优雅地关闭它,例如

    import signal
    signal.raise_signal(signal.SIGINT)
    

    经过测试,它可以在 Fedora Linux 34、Python 3.9.7、aiohttp 3.7.4 上运行。

    【讨论】:

    • 第二个选项 (signal.raise_signal(signal.SIGINT)) 要好得多,因为它不会将异常消息打印到控制台。很好的答案!
    猜你喜欢
    • 2016-11-18
    • 2018-05-21
    • 2013-02-11
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多