모든 것을 지배하는 하나의 라이브러리: AnyIO를 사용한 비동기 Python
Olivia Novak
Dev Intern · Leapcell

소개
비동기 프로그래밍은 현대 Python 개발에서, 특히 웹 서버, 데이터베이스 상호 작용, 네트워크 통신과 같은 I/O 바운드 작업을 처리할 때 없어서는 안 될 패러다임이 되었습니다. Python은 주로 asyncio
라이브러리를 통해 비동기 프로그래밍에 대한 강력한 네이티브 지원을 제공합니다. 그러나 에코시스템에서는 구조화된 동시성에 대해 종종 선호되는 접근 방식을 제공하는 Trio와 같은 대안적이고 성능이 뛰어난 비동기 프레임워크도 등장했습니다. 이 훌륭한 비동기 프레임워크의 확산은 일반적인 문제를 제기합니다. 개발자는 특정 프레임워크에 묶이지 않는 비동기 코드를 작성하여 더 큰 유연성과 다양한 프로젝트에 더 쉬운 통합을 허용할 수 있을까요? 이것이 바로 anyio
가 우아하게 해결하는 문제로, 비동기 Python 코드가 asyncio
와 Trio 모두 위에서 원활하게 실행될 수 있도록 하는 통합 추상화 계층을 제공합니다.
비동기 환경과 AnyIO의 솔루션
anyio
에 대해 자세히 알아보기 전에 비동기 Python을 작동시키는 핵심 개념을 빠르게 정의하고 anyio
가 해결하는 미묘한 차이를 이해해 봅시다.
핵심 용어:
- 비동기 프로그래밍: 일반적으로 I/O 작업 중에 제어권을 양보함으로써 프로그램이 차단 없이 여러 작업을 동시에 실행할 수 있도록 하는 프로그래밍 패러다임입니다.
- 이벤트 루프: 동시 작업의 예약 및 관리를 담당하는 비동기 런타임의 중앙 구성 요소입니다.
- 코루틴:
async def
로 정의되며 실행을 일시 중지하고 나중에 다시 시작할 수 있는 함수입니다. asyncio
: Python의 내장 비동기 I/O 프레임워크로, 이벤트 루프, 작업, 스트림 및 기타 기본 요소를 제공합니다. 미래 기반 동시성을 사용합니다.- Trio: 일반적인 동시성 버그를 방지하고 동시 코드에 대한 추론을 더 쉽게 만드는 구조화된 동시성 모델로 알려진 대안 비동기 프레임워크입니다. 작업 관리를 위해 너서리를 사용합니다.
- 너서리 (Trio): Trio에서 새로운 하위 작업을 생성할 수 있게 하고 구조화된 동시성을 강제하는 너서리 자체가 종료되기 전에 모든 하위 작업이 완료되도록 보장하는 구성 요소입니다.
근본적인 문제는 asyncio
와 Trio가 모두 훌륭한 비동기 기본 요소를 제공하지만 API가 다르다는 것입니다. asyncio
의 asyncio.sleep()
에 대해 작성된 코드는 Trio의 trio.sleep()
과 직접 작동하지 않으며 asyncio.create_task()
는 Trio의 너서리 기반 작업 생성에 직접적인 동등물이 없습니다. 이 프레임워크 잠금은 라이브러리 작성자와 애플리케이션 개발자 모두에게 장애물을 만듭니다.
anyio
는 호환성 계층으로 작동합니다. 현재 실행 중인 이벤트 루프에 따라 asyncio
또는 Trio 위에서 구현된 일련의 고급 비동기 기본 요소를 제공합니다. 이는 코드를 anyio
API를 사용하여 한 번 작성하면 자동으로 기본 백엔드에 적응함을 의미합니다.
AnyIO 작동 방식:
anyio
는 다음을 통해 이를 달성합니다:
- 백엔드 감지: 런타임에
anyio
는asyncio
이벤트 루프 또는 Trio 이벤트 루프가 실행 중인지 감지합니다. - 범용 API 제공: 잠자기, 동시 작업 실행, 잠금 및 작업 간 통신(큐, 이벤트)과 같은 일반적인 비동기 작업에 대한 통합 API를 노출합니다.
- 호출 변환:
anyio
기본 요소(예:await anyio.sleep(1)
)를 호출하면anyio
는 이를 적절한 백엔드별 호출(예:await asyncio.sleep(1)
또는await trio.sleep(1)
)로 변환합니다.
실제 적용:
1초 동안 잠자기와 여러 작업을 동시에 실행하는 간단한 예를 생각해 봅시다.
AnyIO 없이 (프레임워크별):
# asyncio 별 import asyncio async def task_asyncio(name): print(f"Asyncio task {name}: Starting") await asyncio.sleep(0.1) print(f"Asyncio task {name}: Finishing") async def main_asyncio(): await asyncio.gather(task_asyncio("A"), task_asyncio("B")) # trio 별 import trio async def task_trio(name): print(f"Trio task {name}: Starting") await trio.sleep(0.1) print(f"Trio task {name}: Finishing") async def main_trio(): async with trio.open_nursery() as nursery: nursery.start_soon(task_trio, "A") nursery.start_soon(task_trio, "B")
sleep
의 차이점과 작업 시작 방식( asyncio.gather
대 trio.open_nursery
)에 주목하세요.
AnyIO 사용 (프레임워크 독립형):
import anyio async def workers_agnostic(name): print(f"AnyIO task {name}: Starting") await anyio.sleep(0.1) print(f"AnyIO task {name}: Finishing") async def main_anyio(): async with anyio.create_task_group() as tg: tg.start_soon(workers_agnostic, "X") tg.start_soon(workers_agnostic, "Y") # asyncio로 실행하려면: # anyio.run(main_anyio, backend="asyncio") # trio로 실행하려면: # anyio.run(main_anyio, backend="trio")
여기서 anyio.sleep()
및 anyio.create_task_group()
는 백엔드 세부 정보를 추상화합니다. anyio
의 create_task_group
은 Trio의 너서리에 직접적인 대응물 역할을 하며 asyncio
에 대한 호환 가능한 API를 제공하여 작업 관리 기능을 모방합니다.
고급 기능 및 시나리오:
anyio
는 간단한 작업 생성 및 잠자기 외에도 훨씬 더 많은 기능을 제공합니다:
- 네트워킹:
anyio.connect_tcp()
,anyio.create_tcp_listener()
,anyio.connect_unix()
등 백엔드 전반에서 작동하는 네트워크 연결을 설정합니다. - 동기화 기본 요소:
anyio.Lock
,anyio.Semaphore
,anyio.Event
,anyio.Queue
– 모두 기본 이벤트 루프에 관계없이 안정적으로 작동하도록 추상화되었습니다. - 파일 I/O:
anyio.open_file()
을 사용한 비동기 파일 작업입니다. - 외부 프로세스 실행:
await anyio.run_process()
를 사용하여 외부 프로세스를 비동기적으로 실행하고 상호 작용합니다. - 취소 처리:
anyio.CancelScope
는 작업 취소를 관리하는 통합된 방법을 제공하며,asyncio
의 취소를 Trio의 보다 강력한 구조화된 접근 방식과 일치시킵니다. 이는anyio
가 가능한 한 취소가 두 백엔드 모두에서 일관되게 거동하도록 적극적으로 보장한다는 것을 의미하며, 이는 신뢰성에 상당한 이점을 제공합니다.
AnyIO 사용 시기:
- 라이브러리 개발자:
asyncio
또는 Trio를 사용하는 애플리케이션에서 사용할 수 있는 비동기 라이브러리를 구축하는 경우anyio
는 필수입니다. 최대한의 도달 범위를 확보하고 유지 관리 오버헤드를 줄입니다. - 유연성을 추구하는 애플리케이션 개발자: 애플리케이션에서 성능, 기능 세트 또는 에코 시스템 이유로
asyncio
와 Trio 간에 전환해야 할 수 있는 경우anyio
는 해당 비상 탈출구를 제공합니다. - 코드 단순화: 하나의 백엔드만 대상으로 하더라도
anyio
의 종종 더 깔끔하고 일관된 API는 비동기 코드를 더 쉽게 작성하고 이해할 수 있게 하며, 특히 구조화된 동시성을 위한create_task_group
은 더욱 그렇습니다.
결론
anyio
는 Python 비동기 에코시스템에서 중요한 라이브러리로, asyncio
와 Trio 간의 API 차이를 성공적으로 연결합니다. 통합된 고수준 인터페이스를 제공함으로써 개발자는 진정으로 이식 가능한 비동기 코드를 작성할 수 있으며, 이를 통해 유연성을 향상시키고 개발을 단순화하며 보다 응집력 있고 강력한 비동기 환경을 조성합니다. anyio
를 사용하면 비동기 로직을 한 번 작성하고 프로젝트의 요구 사항에 가장 적합한 백엔드에서 자신 있게 실행할 수 있으며, 코드 재사용성과 미래 보증에서 상당한 이점을 얻을 수 있습니다.