本文目录导读:

设计Python数据API的批量操作时,核心目标是减少网络请求次数、提高吞吐量、保证数据一致性,下面从设计模式、技术实现和最佳实践三个维度来讲解。
核心设计模式
批量接口 vs 单条接口
# ❌ 不推荐:逐个调用单条接口
for item in items:
requests.post("/api/resource", json=item)
# ✅ 推荐:一次性批量提交
requests.post("/api/resource/batch", json={"items": items})
请求合并(Request Batching)
将多个独立请求合并为一个批量请求:
class BatchAPI:
def __init__(self, batch_size=100, max_wait=0.5):
self.batch_size = batch_size
self.max_wait = max_wait
self.queue = []
self.lock = threading.Lock()
def add(self, item):
with self.lock:
self.queue.append(item)
if len(self.queue) >= self.batch_size:
self.flush()
def flush(self):
items = self.queue[:]
self.queue = []
# 发送批量请求
self._send_batch(items)
技术实现方案
方案1:简单批量提交(同步)
import requests
from typing import List, Dict
class SimpleBatchClient:
def __init__(self, base_url: str, batch_size: int = 100):
self.base_url = base_url
self.batch_size = batch_size
def bulk_create(self, items: List[Dict]) -> List[Dict]:
"""批量创建资源"""
results = []
for i in range(0, len(items), self.batch_size):
batch = items[i:i + self.batch_size]
response = requests.post(
f"{self.base_url}/api/resources/batch",
json={"resources": batch}
)
results.extend(response.json()["data"])
return results
def bulk_update(self, items: List[Dict]) -> List[Dict]:
"""批量更新资源"""
results = []
for i in range(0, len(items), self.batch_size):
batch = items[i:i + self.batch_size]
response = requests.put(
f"{self.base_url}/api/resources/batch",
json={"resources": batch}
)
results.extend(response.json()["data"])
return results
方案2:异步并发批量(推荐)
import asyncio
import aiohttp
from typing import List, Dict
class AsyncBatchClient:
def __init__(self, base_url: str, batch_size: int = 100, concurrency: int = 5):
self.base_url = base_url
self.batch_size = batch_size
self.semaphore = asyncio.Semaphore(concurrency)
async def _send_batch(self, session: aiohttp.ClientSession,
items: List[Dict]) -> List[Dict]:
async with self.semaphore:
async with session.post(
f"{self.base_url}/api/resources/batch",
json={"resources": items}
) as resp:
return await resp.json()
async def bulk_create(self, items: List[Dict]) -> List[Dict]:
"""异步批量创建"""
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(0, len(items), self.batch_size):
batch = items[i:i + self.batch_size]
tasks.append(self._send_batch(session, batch))
results = await asyncio.gather(*tasks)
return [item for batch in results for item in batch["data"]]
方案3:智能批处理(含重试和回退)
import time
import logging
from typing import List, Dict, Callable
from tenacity import retry, stop_after_attempt, wait_exponential
class SmartBatchProcessor:
def __init__(self, batch_size: int = 100, max_retries: int = 3):
self.batch_size = batch_size
self.max_retries = max_retries
self.logger = logging.getLogger(__name__)
@retry(stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10))
def _send_request(self, items: List[Dict]) -> Dict:
"""发送批量请求(带重试机制)"""
# 实际请求逻辑
response = requests.post(
"https://api.example.com/batch",
json={"items": items},
timeout=30
)
response.raise_for_status()
return response.json()
def _split_by_strategy(self, items: List[Dict],
strategy: str = "size") -> List[List[Dict]]:
"""根据策略拆分批次"""
if strategy == "size":
return [items[i:i + self.batch_size]
for i in range(0, len(items), self.batch_size)]
elif strategy == "adaptive":
# 自适应拆分逻辑
return self._adaptive_split(items)
def process(self, items: List[Dict],
callback: Callable = None) -> List[Dict]:
"""批量处理主逻辑"""
results = []
failed_items = []
batches = self._split_by_strategy(items)
for batch in batches:
try:
response = self._send_request(batch)
if callback:
callback(batch, response)
results.extend(response["data"])
# 成功批量后动态调整批次大小
self._adjust_batch_size(len(batch), success=True)
except Exception as e:
self.logger.error(f"Batch failed: {e}")
# 对失败的批次进行降级处理
if len(batch) > 1:
# 拆分为更小的批次重试
smaller_batches = self._split_by_strategy(
batch, "adaptive"
)
results.extend(self.process(smaller_batches))
else:
failed_items.append(batch[0])
return results
def _adjust_batch_size(self, batch_size: int, success: bool):
"""动态调整批次大小"""
if success:
self.batch_size = min(self.batch_size * 1.1, 500)
else:
self.batch_size = max(self.batch_size * 0.8, 10)
API服务端设计
批量端点设计
# FastAPI 批量端点示例
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
app = FastAPI()
class ItemBatchRequest(BaseModel):
items: List[dict]
options: Optional[dict] = None
class BatchResponse(BaseModel):
success_count: int
failed_count: int
results: List[dict]
errors: List[dict] = []
@app.post("/api/resources/batch")
async def batch_create(request: ItemBatchRequest):
"""批量创建资源"""
results = []
errors = []
for item in request.items:
try:
# 业务逻辑处理
result = await create_resource(item)
results.append(result)
except Exception as e:
errors.append({
"item": item,
"error": str(e)
})
return BatchResponse(
success_count=len(results),
failed_count=len(errors),
results=results,
errors=errors
)
事务性批处理
from contextlib import asynccontextmanager
class TransactionalBatchProcessor:
def __init__(self, db_session):
self.db = db_session
@asynccontextmanager
async def transaction(self):
"""事务上下文管理器"""
async with self.db.begin() as transaction:
try:
yield transaction
await transaction.commit()
except Exception:
await transaction.rollback()
raise
async def process_batch(self, items: List[Dict]):
"""事务性批量处理"""
async with self.transaction() as tx:
# 所有操作在同一个事务中
results = []
for item in items:
result = await self._process_item(tx, item)
results.append(result)
# 验证所有操作成功
if not self._validate_results(results):
raise ValueError("Batch validation failed")
return results
性能优化策略
连接池复用
import aiohttp
class ConnectionPoolClient:
def __init__(self):
# 创建连接池
connector = aiohttp.TCPConnector(
limit=100, # 最大连接数
limit_per_host=20, # 每台主机的最大连接
ttl_dns_cache=300, # DNS缓存时间
)
self.session = aiohttp.ClientSession(connector=connector)
请求压缩
import gzip
import json
def compress_request(data: dict) -> bytes:
"""压缩请求数据"""
json_str = json.dumps(data)
return gzip.compress(json_str.encode())
# 请求时加入压缩
headers = {"Content-Encoding": "gzip"}
response = requests.post(url, data=compress_request(data), headers=headers)
响应流式处理
async def stream_batch_response(session, url, items):
"""流式处理批量响应"""
async with session.post(url, json={"items": items}) as resp:
async for chunk in resp.content.iter_chunks():
process_chunk(chunk)
监控与容错
指标收集
from prometheus_client import Counter, Histogram
class BatchMetrics:
def __init__(self):
self.batch_counter = Counter(
'batch_requests_total',
'Total batch requests',
['status']
)
self.batch_duration = Histogram(
'batch_processing_seconds',
'Batch processing duration',
buckets=[0.1, 0.5, 1, 5, 10]
)
def record(self, success: bool, duration: float):
status = "success" if success else "failed"
self.batch_counter.labels(status=status).inc()
self.batch_duration.observe(duration)
熔断机制
from pybreaker import CircuitBreaker
class CircuitBreakerClient:
def __init__(self):
self.breaker = CircuitBreaker(
fail_max=5, # 连续失败次数
reset_timeout=60, # 重置超时
)
def batch_request(self, items):
@self.breaker
def do_request():
return requests.post("/batch", json={"items": items})
try:
return do_request()
except CircuitBreakerError:
# 熔断时降级处理
return self._fallback_sequential(items)
最佳实践总结
| 场景 | 推荐方案 | 注意事项 |
|---|---|---|
| 实时数据处理 | 异步并发+小批次 | 控制并发数,避免过载 |
| 离线批量同步 | 同步分批+大批次 | 设置超时和重试 |
| 混合场景 | 自适应批处理 | 动态调整批次大小 |
| 事务要求高 | 事务性批处理 | 考虑锁和死锁问题 |
关键设计原则:
- 粒度平衡:批次不宜过大(超时风险)或过小(请求过多)
- 错误隔离:单条失败不影响其他条
- 幂等设计:重复请求不会导致数据异常
- 限流保护:服务端和客户端都要做流量控制
- 监控预警:记录失败率、耗时等关键指标
这样的设计能确保API批量操作既高效又可靠,适合生产环境使用。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播