Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backend] async server run 방식 수정 #45

Open
heojae opened this issue Mar 1, 2021 · 0 comments
Open

[Backend] async server run 방식 수정 #45

heojae opened this issue Mar 1, 2021 · 0 comments
Labels
backend bug Something isn't working

Comments

@heojae
Copy link
Owner

heojae commented Mar 1, 2021

대주제 : async Server 를 보다 효율적으로 디자인하고 싶다.

소주제 : async Server 의 singal handler 를 추가하여, 보다 안전하게 서버를 종료하고 싶다.

수정하게 된 계기

처음 async server 에 대해서, 참조를 했던 코드는 GRPC python example 에서 참조하여, 구현하여 아래와 같이 구현하였으나,

try:
    await server.wait_for_termination()
except KeyboardInterrupt:
    redis.close()
    await redis.wait_closed()  # Coroutine waiting until underlying connections are closed.
    await database.disconnect()

    await server.stop(0)

try exceptionasyncio 의 EventLoop 단에서 잡혀서, 저 아래까지 내려오지 않는 것을 확인할 수 있었고,

서버를 종료할 떄, DB 에 대한 컨넥션이 제대로 종료가 안되는 점을 발견하면서, 이 부분에 대해서 수정할 필요가 있다는 것을 느꼈습니다.

스크린샷 2021-03-01 오후 11 30 25

또한, 서버(grpc server)는 종료가 되었으나, python 프로세스는 종료되지 않은 것을 확인하여, 이를 수정할 필요성을 느꼈습니다.


해결 방법 (signal handler 추가)

FastAPIdjango, flask 등 대다수의 라이브러리들은 before shut down 과 같은 타이밍에 처리하는 함수들이 있기에,

asyncio 에서도 이는 존재할것이라 생각하였습니다.

그래서 찾은 것이 loop.add_signal_handler 이였습니다.

async def shut_down_server(signal, loop):
    global redis, server

    redis.close()
    await redis.wait_closed()  # Coroutine waiting until underlying connections are closed.
    await database.disconnect()
    await server.stop(0)

    loop.stop()
    
# ------------------------------------
# 이전 방식
# asyncio.run(serve())

# 현재방식
loop = asyncio.get_event_loop()

signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT)
for s in signals:
    loop.add_signal_handler(s, lambda s=s: asyncio.create_task(shut_down_server(s, loop)))

try:
    loop.run_until_complete(serve())
finally:
    loop.close()

asyncio.run() 의 경우, 함수 내부에서, loop = events.new_event_loop() 를 통해서, 새로운 EvenetLoop 를 생성하기에 loop 에 대한 signal_handler 를 지정할 수 없기에, 쓸 수 없었습니다.

접기/펼치기 버튼
def run(main, *, debug=False):
    """Execute the coroutine and return the result.

    This function runs the passed coroutine, taking care of
    managing the asyncio event loop and finalizing asynchronous
    generators.

    This function cannot be called when another asyncio event loop is
    running in the same thread.

    If debug is True, the event loop will be run in debug mode.

    This function always creates a new event loop and closes it at the end.
    It should be used as a main entry point for asyncio programs, and should
    ideally only be called once.

    Example:

        async def main():
            await asyncio.sleep(1)
            print('hello')

        asyncio.run(main())
    """
    if events._get_running_loop() is not None:
        raise RuntimeError(
            "asyncio.run() cannot be called from a running event loop")

    if not coroutines.iscoroutine(main):
        raise ValueError("a coroutine was expected, got {!r}".format(main))

    loop = events.new_event_loop()
    try:
        events.set_event_loop(loop)
        loop.set_debug(debug)
        return loop.run_until_complete(main)
    finally:
        try:
            _cancel_all_tasks(loop)
            loop.run_until_complete(loop.shutdown_asyncgens())
        finally:
            events.set_event_loop(None)
            loop.close()

위와 같은 형태로 수정을 하였으며, 아래처럼 무언가가 올바르게 DB 와의 연결이 종료되고, python 프로세스 또한 안전하게 종료된것을 확인할 수 있었습니다.

스크린샷 2021-03-02 오전 12 06 56

그리고, 위에서, 아래와 같은 경고문이 떠서 확인을 해보았는데,

app.py:123: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
  loop.run_until_complete(serve())

스크린샷 2021-03-01 오후 11 10 46

위의 사진에 있는, asyncio 함수들중 loop 를 추가 인자로 받는 것이 앞으로 사라진다는 의미였고, 저 warning 에 대해서는 걱정하지 않으셔도 됩니다.

@heojae heojae added backend bug Something isn't working labels Mar 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant