23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
Rate Limit이란 API를 사용할 때 일정 시간 동안에 호출할 수 있는 횟수를 제한하는 것을 말합니다.
외부 API를 호출할때 Rate Limit을 감안하지 않고 대량의 Request를 보낼 경우 서버에 부하가 걸리거나,
API 제공자에게 무리한 요청을 보내서 200 OK 가 아닌 에러 Status code를 받을 가능성도 있습니다.
예외처리로 해결하는 방법도 있겠지만 실패한 요청에 대해서 반복하여 발송하거나 혹시나 DDoS로 오해받아서 계정이나 api key가 block 되는 불상사도 발생할 수 있겠죠.
파이썬에서 API Call을 하는 가장 간단한 방법은 예제와 튜토리얼에서 많이 등장하는 동기식 requests 라이브러리를 사용하는 방법입니다.
동기식 API Call의 경우 간단하고 코드를 알아보기 쉽다는 장점이 있지만, API Call을 기다리는 동안 다른 작업을 수행하지 못하고 대기해야 하기 때문에 비효율적입니다.
최근에는 비동기 프로그래밍이 강조되면서 비동기로 API Call을 하는 aiohttp, httpx 등이 있습니다.
비동기로 API Call을 하는 경우, API Call을 기다리는 동안 다른 작업을 수행할 수 있기 때문에 효율적입니다.
하지만, 비동기로 API Call을 하는 경우 Rate Limit을 적용하기가 어렵습니다.
Rate Limit을 적용하기 위한 방법은 여러가지가 있겠지만, 가장 간단한 방법은 Rate Limit을 적용할 API Call을 호출하기 전에 Rate Limit을 확인하고,
Rate Limit을 초과하지 않는다면 API Call을 수행하고, Rate Limit을 초과한다면 API Call을 대기하는 것입니다.
이 글에서는 httpx를 이용한 간단한 http client를 만들고 여기에 rate limit을 적용해보는 방법을 알아보겠습니다.
먼저 필요한 패키지를 설치합니다
pip install httpx aiolimiter aiometer간단한 클라이언트 함수를 작성해보겠습니다.
cf) httpx는 max_connections 옵션을 통해 동시에 열 수 있는 connection의 수를 제한할 수 있습니다.
하지만 각 connection 으로 호출하는 API의 응답시간이 각기 다르므로 정교한 Rate Control이 어렵습니다.
import httpx
async def fetch(url, limiter):
async with limiter:
async with httpx.AsyncClient() as client:
response = await client.get(url)
return responseaiolimiter는 asyncio를 이용한 Rate Limit을 적용하기 위한 라이브러리입니다.
aiolimiter를 이용하면 간단하게 Rate Limit을 적용할 수 있습니다. 전체 코드는 다음과 같습니다.
import asyncio
import time
import httpx
from aiolimiter import AsyncLimiter
NUM_OF_TOTAL_URLS = 10
RATE_LIMIT = 10
async def fetch(url, limiter):
async with limiter:
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response
async def main(urls, rate_limit=10):
# Create an AsyncLimiter instance
limiter = AsyncLimiter(rate_limit, time_period=1)
# Create a list of tasks
tasks = [fetch(url, limiter) for url in urls]
# Run the tasks concurrently
results = await asyncio.gather(*tasks)
# Process the results
for result in results:
print(result.status_code)
# List of URLs to fetch
urls = ["https://httpbin.org/get"] * NUM_OF_TOTAL_URLS
# Run the main function
start_time = time.time()
asyncio.run(main(urls, RATE_LIMIT))
end_time = time.time()
print(f"Total time: {end_time - start_time} seconds")
# Run the main function
asyncio.run(main(urls))전체 URL을 10개, limit을 10개로 적용했을 때 소요 시간은 다음과 같습니다.(실행 환경에 따라 차이는 있습니다)
Total time: 1.4457590579986572 seconds여기서 limit만 2로 변경하면 어떻게 될까요?
Total time: 4.87471604347229 seconds전체적인 호출 개수는 10개로 동일하지만 limit마다 끊어서 호출하기 때문에 소요시간이 더 오래 걸리게 되는 것을 확인할 수 있었습니다.
aiometer는 aiolimiter와 같이 asyncio를 이용한 Rate Limit을 적용하기 위한 라이브러리입니다. 아직 베타 버전이라 향후 여러 변경사항이 있을 수 있습니다.
이 글에서 사용한 버전은 0.5.x 입니다
max_at_once: 주어진 시간에 동시에 실행되는 작업의 최대 수를 제한하는 데 사용됩니다.
(100개의 작업이 있고 max_at_once를 10으로 설정하면, aiometer는 동시에 10개 이상 실행되지 않도록 보장합니다.)
max_per_second: 초당 생성되는 작업 수를 제한합니다.
전체 코드를 바로 살펴봅시다.
import asyncio
import functools
import time
import aiometer
import httpx
NUM_OF_TOTAL_URLS = 10
MAX_PER_SECOND = 2
MAX_AT_ONCE = 5
async def fetch(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response
async def main(urls):
results = await aiometer.run_all(
[functools.partial(fetch, url) for url in urls],
max_per_second=MAX_PER_SECOND, # Limit to 2 requests per second
max_at_once=MAX_AT_ONCE, # Limit to 5 concurrent tasks
)
for result in results:
print(result.status_code)
# List of URLs to fetch
urls = ["https://httpbin.org/get"] * NUM_OF_TOTAL_URLS
# Run the main function
start_time = time.time()
asyncio.run(main(urls))
end_time = time.time()
print(f"Total time: {end_time - start_time} seconds")이 문서는 파이썬에서 비동기 API 호출에 대한 Rate Limit을 적용하는 방법을 설명합니다.
httpx와 aiolimiter, aiometer 라이브러리를 사용하여 동시성과 초당 요청 수를 제어하는 다양한 방법을 제시합니다.
이를 통해 과도한 I/O를 방지하고 서버 용량을 고려한 효율적인 API 호출을 가능하게 합니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.