File size: 3,261 Bytes
48ae4e0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
"""
Async HTTP Client with Retry Logic
"""
import aiohttp
import asyncio
from typing import Dict, Optional, Any
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
class APIClient:
def __init__(self, timeout: int = 10, max_retries: int = 3):
self.timeout = aiohttp.ClientTimeout(total=timeout)
self.max_retries = max_retries
self.session: Optional[aiohttp.ClientSession] = None
async def __aenter__(self):
self.session = aiohttp.ClientSession(timeout=self.timeout)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.session:
await self.session.close()
async def get(
self,
url: str,
headers: Optional[Dict] = None,
params: Optional[Dict] = None,
retry_count: int = 0
) -> Dict[str, Any]:
"""Make GET request with retry logic"""
start_time = datetime.utcnow()
try:
async with self.session.get(url, headers=headers, params=params) as response:
elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)
# Try to parse JSON response
try:
data = await response.json()
except:
data = await response.text()
return {
"success": response.status == 200,
"status_code": response.status,
"data": data,
"response_time_ms": elapsed_ms,
"error": None if response.status == 200 else {
"type": "http_error",
"message": f"HTTP {response.status}"
}
}
except asyncio.TimeoutError:
elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)
if retry_count < self.max_retries:
logger.warning(f"Timeout for {url}, retrying ({retry_count + 1}/{self.max_retries})")
await asyncio.sleep(2 ** retry_count) # Exponential backoff
return await self.get(url, headers, params, retry_count + 1)
return {
"success": False,
"status_code": 0,
"data": None,
"response_time_ms": elapsed_ms,
"error": {"type": "timeout", "message": "Request timeout"}
}
except aiohttp.ClientError as e:
elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)
return {
"success": False,
"status_code": 0,
"data": None,
"response_time_ms": elapsed_ms,
"error": {"type": "client_error", "message": str(e)}
}
except Exception as e:
elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000)
logger.error(f"Unexpected error for {url}: {e}")
return {
"success": False,
"status_code": 0,
"data": None,
"response_time_ms": elapsed_ms,
"error": {"type": "unknown", "message": str(e)}
}
|