Python HTTP 클라이언트 라이브러리를 이용한 동기 및 비동기 요청 처리

개요

Python에서 HTTP 요청을 처리하는 방법은 다양합니다. 표준 라이브러리부터 서드파티 라이브러리까지 여러 옵션이 있으며, 각각의 장단점이 존재합니다. 본 문서에서는 대표적인 HTTP 클라이언트 라이브러리들을 살펴보고, 동기 및 비동기 방식의 요청 처리를 비교합니다.

주요 HTTP 클라이언트 라이브러리

  • http.client: Python 표준 라이브러리
  • urllib: Python 표준 라이브러리 (urllib.request)
  • requests: 가장 널리 사용되는 동기 요청 라이브러리
  • aiohttp: 비동기 HTTP 클라이언트
  • httpx: 동기/비동기 모두 지원하는 moderna 라이브러리

테스트 환경 구성

필수 조건

Python 3.7 이상

필요 패키지

aiohttp==3.8.5
aiosignal==1.3.1
anyio==3.7.1
async-timeout==4.0.3
asynctest==0.13.0
attrs==23.1.0
certifi==2023.7.22
charset-normalizer==3.2.0
exceptiongroup==1.1.3
frozenlist==1.3.3
h11==0.14.0
httpcore==0.17.3
httpx==0.24.1
idna==3.4
importlib-metadata==6.7.0
multidict==6.0.4
requests==2.31.0
sniffio==1.3.0
typing_extensions==4.7.1
urllib3==2.0.4
yarl==1.9.2
zipp==3.15.0

테스트용 서버 구현

HTTP 클라이언트 테스트를 위해 Flask 기반의 간단한 테스트 서버를 구현합니다.

# -*- coding:utf-8 -*-
'''
pip install flask
'''
from flask import Flask, jsonify, request

app = Flask(__name__)


@app.route("/", methods=["POST", "GET"])
def handle_request():
    return jsonify({
        "code": 0,
        "msg": "ok",
        "data": {
            "args": request.args,
            "form": request.form,
            "json": request.get_json(),
        }
    })


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=6666, debug=False)

동기 HTTP 클라이언트 구현

http.client를 이용한 요청

Python 표준 라이브러리인 http.client 모듈을 사용한 기본적인 HTTP 요청 처리입니다.

import time
from functools import wraps


def measure_time(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        results = func(*args, **kwargs)
        elapsed = round((time.time() - start), 4) * 1000
        success_count = len([r for r in results if r == 200])
        print(f"Func:{func.__name__};Total:{len(results)};Success:{success_count};Time:{elapsed}ms")
    return wrapper


@measure_time
def test_http_client(request_count=1):
    """
    표준 http.client 모듈을 사용한 HTTP 요청
    """
    import http.client
    import json
    
    response_codes = []
    connection = http.client.HTTPConnection(host="localhost", port=6666)
    
    for i in range(request_count):
        # 쿼리 파라미터 전송
        connection.request(
            method="GET",
            url="/?id=test" + str(i),
            body=None,
            headers={}
        )
        
        response = connection.getresponse()
        response_codes.append(response.status)
    
    connection.close()
    return response_codes

urllib를 이용한 요청

@measure_time
def test_urllib_request(request_count=1):
    """
    urllib.request 모듈을 사용한 HTTP 요청
    """
    import urllib.request
    import json
    
    response_codes = []
    
    for i in range(request_count):
        # 쿼리 파라미터가 포함된 URL 요청
        request_obj = urllib.request.Request(
            url="http://localhost:6666?id=test" + str(i),
            data=None,
            headers={},
            method="GET"
        )
        
        with urllib.request.urlopen(request_obj) as response:
            response_codes.append(response.status)
    
    return response_codes

requests 라이브러리를 이용한 요청

requests는 가장 직관적이고 사용하기 쉬운 API를 제공합니다.

# 설치 방법
pip install requests -i http://mirrors.cloud.tencent.com/pypi/simple --trust
@measure_time
def test_requests_library(request_count=1):
    """
    requests 라이브러리를 사용한 동기 HTTP 요청
    """
    import requests
    
    response_codes = []
    session = requests.session()
    
    for i in range(request_count):
        # GET 요청 with 쿼리 파라미터
        response = session.get(
            url="http://localhost:6666",
            params={"id": "test" + str(i)}
        )
        
        response_codes.append(response.status_code)
    
    return response_codes

httpx 동기 모드

@measure_time
def test_httpx_sync(request_count=1):
    """
    httpx 라이브러리의 동기 모드 사용
    """
    import httpx
    
    response_codes = []
    
    with httpx.Client(timeout=3.0) as client:
        for i in range(request_count):
            # 쿼리 파라미터 포함 GET 요청
            response = client.get(
                url="http://localhost:6666",
                params={"id": "test" + str(i)}
            )
            
            response_codes.append(response.status_code)
    
    return response_codes

비동기 HTTP 클라이언트 구현

aiohttp를 이용한 비동기 요청

pip install aiohttp
async def fetch_with_aiohttp(request_count=1):
    """
    aiohttp를 사용한 비동기 HTTP 요청
    """
    import aiohttp
    import time
    
    response_codes = []
    start_time = time.time()
    
    async with aiohttp.ClientSession() as session:
        for i in range(request_count):
            async with session.get(
                url="http://localhost:6666",
                params={"id": "test" + str(i)}
            ) as response:
                await response.json()
                response_codes.append(response.status)
    
    elapsed = round((time.time() - start_time), 4) * 1000
    success = len([r for r in response_codes if r == 200])
    print(f"Func:fetch_with_aiohttp;Total:{len(response_codes)};Success:{success};Time:{elapsed}ms")


import asyncio
asyncio.run(fetch_with_aiohttp(1))

httpx 비동기 모드

async def single_request_task(client, index):
    """
    개별 비동기 요청 태스크
    """
    response = await client.get(
        url="http://localhost:6666",
        params={"id": "test" + str(index)}
    )
    return response.status_code


async def fetch_with_httpx_async(request_count=1):
    """
    httpx의 AsyncClient를 사용한 비동기 요청
    """
    import httpx
    import asyncio
    import time
    
    start_time = time.time()
    
    async with httpx.AsyncClient(timeout=30.0) as client:
        tasks = [single_request_task(client, i) for i in range(request_count)]
        response_codes = await asyncio.gather(*tasks)
    
    elapsed = round((time.time() - start_time), 4) * 1000
    success = len([r for r in response_codes if r == 200])
    print(f"Func:fetch_with_httpx_async;Total:{len(response_codes)};Success:{success};Time:{elapsed}ms")


import asyncio
asyncio.run(fetch_with_httpx_async(10))

라이브러리 비교 요약

라이브러리동기비동기특징
http.client표준 라이브러리, 낮은 수준의 제어
urllib표준 라이브러리, 다양한 프로토콜 지원
requests직관적인 API, 높은 생산성
aiohttp비동기 전용, 높은 성능
httpx현대적인 API, Bothway 지원

요청 성능은 테스트 환경과 네트워크 조건에 따라 달라질 수 있으며, 대량의并发 요청 처리 시 비동기 라이브러리(aiohttp, httpx-async)가明显한 성능 이점을 제공합니다.

태그: python HTTP requests aiohttp httpx

6월 22일 00:35에 게시됨