computer engineering
Python - 비동기 프로그래밍 실전 적용
제이훈 : 세상 모든 지식의 탐구자
2025. 5. 20. 16:01
Python 비동기 라이브러리
- asyncio: 표준 비동기 이벤트 루프. async/await 기반
- aiohttp: 비동기 HTTP 요청 처리. requests 비동기 버전
- aiofiles: 비동기 파일 I/O. open() 대신 aiofiles.open()
- aiomultiprocess: asyncio + multiprocessing. CPU 바운드용
- trio / curio: asyncio 대체. 현대적 구조(nursery)
- httpx.AsyncClient: aiohttp 대체 가능. requests 스타일 + async
크롤링 Task 구조 요약
- 콘텐츠 단위로 유저 데이터 수집
- 작품별 회차 수 다름 (예: 1~100화 vs 1~57화)
- 회차별 데이터 수량 다름 (예: 1화=1000개, 2화=500개)
- HTTP 요청으로 response 수신
- 데이터 존재 여부 판단 → 파싱
- 작품 단위로 DataFrame 변환
- 전체 콘텐츠에 대해 반복
비동기 처리 관련 오해
- ❌ await는 기다리는 것 → ✅ 제어권 양보
- ❌ async def만 써도 성능 좋아짐 → ✅ 병렬 예약 없으면 의미 없음
- ❌ await 많으면 느려짐 → ✅ await 많아야 효과 큼 (I/O에 한함)
- ❌ 이벤트 루프는 멀티스레드 → ✅ 단일 스레드 기반
비동기 처리 주의사항
- 이벤트 루프는 큐/콜백 기반. 우선순위 없음
- gather vs create_task → 전체 대기 vs 개별 관리
- CPU 작업은 asyncio X → ProcessPoolExecutor 사용
- 예외는 try/except 또는 return_exceptions=True로 처리
async def 함수의 실행 구조
- await 전까지는 실행. await에서 중단
- await는 제어권을 이벤트 루프에 넘김
- 이후 완료되면 resume
Python 비동기 병렬 실행 방법
1. asyncio.gather()
results = await asyncio.gather(task1(), task2(), task3())
- 모든 작업을 동시에 실행
- 모두 끝날 때까지 대기
2. asyncio.create_task()
task1 = asyncio.create_task(my_coro())
await task1
- 비동기 작업을 백그라운드로 실행
- Task 객체로 상태 추적 가능
3. asyncio.as_completed()
for coro in asyncio.as_completed(tasks):
result = await coro
- 끝나는 순서대로 처리
- 빠른 응답 우선 처리 가능
비교 요약
- gather: 결과 순서 중요할 때
- create_task: 추적/취소 필요할 때
- as_completed: 빠른 결과 우선 처리
코루틴 중첩 패턴
async def inner():
await asyncio.sleep(1)
return 42
async def outer():
result = await inner()
- 코루틴 안에서 다른 코루틴 호출 가능
- 반드시 await 또는 create_task로 실행
비동기 크롤링 실전 이슈 및 대응
1) Too many open files
- Linux 기본: 1024
- 확장 방법:
ulimit -n 4096
2) 과도한 병렬 요청 제어
- 타이틀 단위:
asyncio.Semaphore(10)
- 페이지 단위:
asyncio.Semaphore(5)
- TCP 연결 제한:
aiohttp.TCPConnector( limit=50, limit_per_host=10, force_close=True)
3) 세션 닫힘 오류
- ClientSession은 한 번 생성 → 모든 요청에 공유
4) 재시도 로직
for attempt in range(3):
try:
...
except:
await asyncio.sleep(1 + attempt)
5) Server disconnected 대응
- 과도한 연결 → 서버 차단
- 요청 지연 → timeout
- 커넥션 재사용 문제 → reset 발생
- 세션이 커넥션 과다 유지 → crash 발생
- 서버 rate limit → 요청 간 간격 조절 필요