주뇽's 저장소

Uvicorn에서 Gunicorn으로 전환 시 발생한 Kafka 문제 해결: 비동기, 동기, 블로킹, 논블로킹 개념과 적용 방법 본문

웹개발

Uvicorn에서 Gunicorn으로 전환 시 발생한 Kafka 문제 해결: 비동기, 동기, 블로킹, 논블로킹 개념과 적용 방법

뎁쭌 2024. 9. 13. 14:53
728x90
반응형

Uvicorn에서 Gunicorn으로 전환 시 발생한 Kafka 문제 해결: 비동기, 동기, 블로킹, 논블로킹 개념과 적용 방법

FastAPI 프로젝트에서 Kafka를 사용해 비동기 메시지 처리를 구현하다 보면, Uvicorn 단독 실행에서는 문제없이 동작하던 코드가 Gunicorn과 함께 실행할 때 문제를 일으킬 수 있다. 이 글에서는 Uvicorn과 Gunicorn의 차이, 비동기/동기, 블로킹/논블로킹 개념을 바탕으로 Kafka와 관련된 문제를 해결하는 방법에 대해 설명한다.

문제점: Gunicorn 환경에서 비동기 처리가 제대로 되지 않음 😓

FastAPI와 Kafka를 연동해 Uvicorn에서 workers=4로 설정하여 실행했을 때는 비동기 처리가 잘 이루어졌다. 그러나 Gunicorn과 Uvicorn을 함께 사용하면서 asyncio.to_thread를 통해 비동기 처리를 하던 코드가 무한 루프에 빠지거나, 요청이 끝나지 않는 문제가 발생했다. 반면, aiokafka와 같은 완전한 비동기 라이브러리를 사용하자 문제가 해결되었다.

해결책: 비동기 라이브러리 사용 🛠️

Gunicorn에서의 문제는 동기 작업을 비동기로 바꾸기 위한 asyncio.to_thread와, 이벤트 루프와 스레드 관리 방식의 차이에서 비롯되었다. 비동기적으로 동작하는 aiokafka를 사용하면, 이런 복잡한 스레드 문제 없이 원활한 처리가 가능하다. 이를 해결하기 위해 비동기 프로그래밍과 비동기 라이브러리의 차이점을 이해하는 것이 중요하다.


비동기와 동기, 블로킹과 논블로킹 개념 이해하기 📚

1. 동기(Synchronous)와 비동기(Asynchronous)

  • 동기: 작업을 순차적으로 처리하며, 한 작업이 끝나야 다음 작업을 시작할 수 있다.
    예시: 학생들이 줄을 서서 하나씩 급식을 받는 상황.
  • 비동기: 여러 작업을 동시에 처리할 수 있으며, 앞의 작업이 끝나기를 기다리지 않고 다음 작업을 진행할 수 있다.
    예시: 뷔페 식당에서 여러 학생이 동시에 음식을 받는 상황.

2. 블로킹(Blocking)과 논블로킹(Non-blocking)

  • 블로킹: 작업이 완료될 때까지 프로그램이 멈추고 기다린다. 다른 작업을 할 수 없다.
    예시: 전화 상담에서 한 고객이 상담을 마칠 때까지 다른 고객은 기다려야 한다.
  • 논블로킹: 작업이 끝나기를 기다리지 않고 바로 다음 작업을 진행할 수 있다.
    예시: 채팅 상담에서 여러 고객과 동시에 상담이 가능하다.

Uvicorn과 Gunicorn의 차이 🖥️

Uvicorn 단독 실행:

Uvicorn에서 workers=4로 설정하면, 각 워커가 독립적으로 비동기 작업을 처리한다. 각 워커는 자신의 이벤트 루프를 관리하며, 동기 작업을 asyncio.to_thread로 처리할 때 스레드 간 통신에 문제가 발생하지 않는다. 즉, 작은 규모에서는 비동기 처리 방식이 제대로 동작한다.

Gunicorn + Uvicorn 실행:

Gunicorn은 애플리케이션을 실행할 때 여러 프로세스를 관리하는 역할을 한다. 그러나 이 과정에서 각 프로세스가 생성하는 이벤트 루프와 스레드 관리 방식이 복잡해진다. Gunicorn은 Uvicorn과는 다른 방식으로 프로세스 간 통신과 스레드 관리가 이루어지며, 이로 인해 asyncio.to_thread가 제대로 작동하지 않는 문제가 발생할 수 있다. Gunicorn 환경에서는 스레드 간의 통신이 혼란스러워질 수 있고, 그 결과로 요청이 끝나지 않거나 무한 루프에 빠지는 현상이 발생한다.

Kafka에서 발생한 문제 해결하기 🛠️

Gunicorn에서 asyncio.to_thread를 사용해 동기 작업을 처리하려 하면, 스레드와 프로세스 간의 복잡한 상호작용 때문에 제대로 동작하지 않을 수 있다. 하지만 aiokafka와 같은 비동기 라이브러리는 처음부터 이벤트 루프 내에서 비동기로 처리되기 때문에, 스레드 관리 문제 없이 Gunicorn 환경에서도 원활하게 동작한다.


Uvicorn과 Gunicorn의 차이점 더 깊게 이해하기

Uvicorn에서 멀티 워커 사용

  • Uvicorn은 자체적으로 여러 개의 워커 프로세스를 생성할 수 있다.
  • 각 워커 프로세스는 독립적으로 동작하며, 자신의 이벤트 루프스레드 풀을 가진다.
  • asyncio.to_thread를 사용할 때, 각 워커 내에서 스레드가 생성되고 관리되므로 문제가 없다.

Gunicorn + Uvicorn 사용 시 차이

  • Gunicorn은 애플리케이션을 실행하기 위해 워커 프로세스를 생성하고, 그 안에서 Uvicorn이 동작한다.
  • 그러나 Gunicorn은 Uvicorn과는 다른 방식으로 워커와 이벤트 루프를 관리한다.
  • 이로 인해 이벤트 루프와 스레드 풀의 초기화 순서관리 방식이 달라질 수 있다.
  • 이러한 차이로 인해 asyncio.to_thread가 예상대로 작동하지 않을 수 있고, 스레드와 이벤트 루프 간의 통신에 문제가 생길 수 있다.

구체적 예시를 통한 설명

Uvicorn에서 workers=4로 실행한 경우

  • 각각의 교실(Uvicorn 워커 프로세스)에 선생님이 있고, 학생들은 자기 교실의 선생님에게 질문한다.
  • 선생님은 어려운 질문이 오면 같은 교실 내에서 보조 교사(스레드)에게 도움을 요청한다.
  • 보조 교사는 그 교실의 선생님과 원활하게 소통하므로 문제가 없다.

Gunicorn + Uvicorn을 사용한 경우

  • Gunicorn은 여러 교실을 관리하는 관리자이고, 각 교실에서 Uvicorn이 선생님 역할을 한다.
  • 그러나 이번에는 선생님이 보조 교사에게 도움을 요청할 때, 보조 교사가 어느 교실의 선생님을 도와야 할지 혼란스러워진다.
  • 왜냐하면 Gunicorn의 관리 방식 때문에 보조 교사(스레드)선생님(이벤트 루프) 간의 연결이 올바르게 설정되지 않을 수 있기 때문이다.
  • 그 결과, 질문에 대한 답변이 전달되지 않거나 시간이 오래 걸리게 된다.

aiokafka가 왜 문제없이 동작할까?

  • aiokafka는 보조 교사(스레드)를 필요로 하지 않고, 선생님(이벤트 루프)이 직접 모든 질문을 처리할 수 있게 해준다.
  • 따라서 보조 교사와의 소통 문제나 혼란이 없어서, Gunicorn + Uvicorn 환경에서도 원활하게 동작한다.

핵심 포인트

  • 스레드를 사용하는 asyncio.to_thread 방식은 Gunicorn과 같이 여러 프로세스를 관리하는 환경에서 스레드와 프로세스 간의 복잡한 상호 작용 때문에 문제가 발생할 수 있다.
  • 비동기 라이브러리인 aiokafka를 사용하는 방식은 스레드를 사용하지 않고, 이벤트 루프 내에서 직접 작업하므로 이러한 문제가 발생하지 않는다.

구현 단계: 비동기 Kafka 설정 및 실행 🔧

  1. aiokafka 설치하기:
  2. pip install aiokafka
  3. Kafka 소비자(Consumer) 설정:
    비동기 Kafka 클라이언트를 설정하고, 메시지를 처리하는 함수를 비동기로 작성한다.
  4. from aiokafka import AIOKafkaConsumer import asyncio async def consume(): consumer = AIOKafkaConsumer( 'my_topic', bootstrap_servers='localhost:9092', group_id="my_group" ) await consumer.start() try: async for message in consumer: print(f"Consumed message: {message.value}") finally: await consumer.stop() asyncio.run(consume())
  5. Gunicorn을 사용해 FastAPI 실행:
    Gunicorn에서 Uvicorn 워커를 사용해 애플리케이션을 실행한다.
  6. gun

icorn -w 4 -k uvicorn.workers.UvicornWorker myapp:app

```


결론: 비동기 라이브러리로 안정적인 처리 구현 🏆

비동기 프로그래밍은 여러 작업을 동시에 처리할 수 있어 고성능 웹 애플리케이션 개발에 매우 유용하다. 특히, Kafka와 같은 대규모 메시지 시스템을 사용할 때는 비동기 라이브러리(aiokafka)를 사용하는 것이 Gunicorn과 같은 복잡한 환경에서도 안정적인 처리를 보장하는 데 효과적이다.
이를 통해 프로그램이 무한 루프에 빠지거나 요청이 끝나지 않는 문제를 해결할 수 있으며, 비동기 처리로 성능을 극대화할 수 있다.