030-企业级最佳实践

030-企业级最佳实践

🎯 难度: 专家级 | ⏱️ 预计时间: 150分钟 | 📋 前置: 029-requests源码深度解析

学习目标

完成本章节后,你将能够:

  • 掌握企业级requests应用的最佳实践
  • 实现高可用、高性能的HTTP客户端
  • 建立完善的监控和日志体系
  • 处理复杂的企业级场景和需求
  • 制定代码规范和架构标准

企业级架构设计

分层架构模式

网络层
基础设施层
HTTP客户端层
服务层
业务层
代理服务
防火墙
负载均衡
连接池管理
缓存系统
监控系统
日志系统
统一HTTP客户端
请求路由器
负载均衡器
API服务
数据服务
认证服务
业务逻辑
数据处理
业务规则

企业级HTTP客户端设计

# 文件路径: enterprise/http_client.py
"""
企业级HTTP客户端实现
"""

import requests
import time
import logging
import threading
from typing import Dict, Any, Optional, Callable, List
from dataclasses import dataclass, field
from enum import Enum
from urllib.parse import urljoin
import json
from datetime import datetime, timedelta

class ServiceTier(Enum):
    """服务等级"""
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"

@dataclass
class ServiceConfig:
    """服务配置"""
    base_url: str
    timeout: int = 30
    max_retries: int = 3
    retry_backoff: float = 1.0
    tier: ServiceTier = ServiceTier.MEDIUM
    circuit_breaker_threshold: int = 5
    circuit_breaker_timeout: int = 60
    rate_limit: Optional[int] = None
    auth_config: Optional[Dict[str, Any]] = None
    custom_headers: Dict[str, str] = field(default_factory=dict)

@dataclass
class RequestMetrics:
    """请求指标"""
    start_time: datetime
    end_time: Optional[datetime] = None
    duration: Optional[float] = None
    status_code: Optional[int] = None
    error: Optional[str] = None
    retries: int = 0
    cache_hit: bool = False

class CircuitBreaker:
    """熔断器实现"""
    
    def __init__(self, threshold: int = 5, timeout: int = 60):
        self.threshold = threshold
        self.timeout = timeout
        self.failure_count = 0
        self.last_failure_time = None
        self.state = 'CLOSED'  # CLOSED, OPEN, HALF_OPEN
        self._lock = threading.Lock()
    
    def call(self, func: Callable, *args, **kwargs):
        """执行函数调用,带熔断保护"""
        with self._lock:
            if self.state == 'OPEN':
                if time.time() - self.last_failure_time > self.timeout:
                    self.state = 'HALF_OPEN'
                else:
                    raise Exception("Circuit breaker is OPEN")
        
        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            raise e
    
    def _on_success(self):
        """成功回调"""
        with self._lock:
            self.failure_count = 0
            self.state = 'CLOSED'
    
    def _on_failure(self):
        """失败回调"""
        with self._lock:
            self.failure_count += 1
            self.last_failure_time = time.time()
            
            if self.failure_count >= self.threshold:
                self.state = 'OPEN'

class RateLimiter:
    """速率限制器"""
    
    def __init__(self, max_calls: int, time_window: int = 60):
        self.max_calls = max_calls
        self.time_window = time_window
        self.calls = []
        self._lock = threading.Lock()
    
    def acquire(self) -> bool:
        """获取调用许可"""
        with self._lock:
            now = time.time()
            # 清理过期的调用记录
            self.calls = [call_time for call_time in self.calls 
                         if now - call_time < self.time_window]
            
            if len(self.calls) < self.max_calls:
                self.calls.append(now)
                return True
            return False
    
    def wait_time(self) -> float:
        """计算需要等待的时间"""
        if not self.calls:
            return 0
        
        oldest_call = min(self.calls)
        return max(0, self.time_window - (time.time() - oldest_call))

class RequestCache:
    """请求缓存"""
    
    def __init__(self, max_size: int = 1000, ttl: int = 300):
        self.max_size = max_size
        self.ttl = ttl
        self.cache = {
   
   }
        self._lock = threading.Lock()
    
    def get(self, key: str) -> Optional[Any]:
        """获取缓存"""
        with self._lock:
            if key in self.cache:
                value, timestamp = self.cache[key]
                if time.time() - timestamp < self.ttl:
                    return value
                else:
                    del self.cache[key]
            return None
    
    def set(self, key: str, value: Any):
        """设置缓存"""
        with self._lock:
            # 清理过期缓存
            self._cleanup()
            
            # 如果缓存已满,删除最旧的条目
            if len(self.cache) >= self.max_size:
                oldest_key = min(self.cache.keys(), 
                               key=lambda k: self.cache[k][1])
                del self.cache[oldest_key]
            
            self.cache[key] = (value, time.time())
    
    def _cleanup(self):
        """清理过期缓存"""
        now = time.time()
        expired_keys = [key for key, (_, timestamp) in self.cache.items() 
                       if now - timestamp >= self.ttl]
        for key in expired_keys:
            del self.cache[key]

class EnterpriseHTTPClient:
    """企业级HTTP客户端"""
    
    def __init__(self, config: ServiceConfig):
        self.config = config
        self.session = self._create_session()
        self.circuit_breaker = CircuitBreaker(
            threshold=config.circuit_breaker_threshold,
            timeout=config.circuit_breaker_timeout
        )
        self.rate_limiter = RateLimiter(config.rate_limit) if config.rate_limit else None
        self.cache = RequestCache()
        self.metrics = []
        self.logger = self._setup_logger()
        
    def _create_session(self) -> requests.Session:
        """创建配置好的会话"""
        session = requests.Session()
        
        # 设置适配器
        adapter = requests.adapters.HTTPAdapter(
            pool_connections=20,
            pool_maxsize=20,
            max_retries=0  # 我们自己处理重试
        )
        session.mount('http://', adapter)
        session.mount('https://', adapter)
        
        # 设置默认头部
        session.headers.update({
   
   
            'User-Agent': 'EnterpriseClient/1.0',
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            **self.config.custom_headers
        })
        
        # 设置认证
        if self.config.auth_config:
            self._setup_auth(session)
        
        return session
    
    def _setup_auth(self, session: requests.Session):
        """设置认证"""
        auth_type = self.config.auth_config.get('type')
        
        if auth_type == 'basic':
            from requests.auth import HTTPBasicAuth
            session.auth = HTTPBasicAuth(
                self.config.auth_config['username'],
                self.config.auth_config['password']
            )
        elif auth_type == 'bearer':
            session.headers['Authorization'] = f"Bearer {
     
     self.config.auth_config['token']}"
        elif auth_type == 'api_key':
            key_name = self.config.auth_config.get('key_name', 'X-API-Key')
            session.headers[key_name] = self.config.auth_config['api_key']
    
    def _setup_logger(self) -> logging.Logger:
        """设置日志记录器"""
        logger = logging.getLogger(f'enterprise_client_{
     
     id(self)}')
        logger.setLevel(logging.INFO)
        
        if not logger.handlers:
            handler = logging.StreamHandler()
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
            )
            handler.setFormatter(formatter)
            logger.addHandler(handler)
        
        return logger
    
    def request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
        """发送请求"""
        url = urljoin(self.config.base_url, endpoint)
        
        # 创建请求指标
        metrics = RequestMetrics(start_time=datetime.now())
        
        try:
            # 速率限制检查
            if self.rate_limiter and not self.rate_limiter.acquire():
                wait_time = self.rate_limiter.wait_time()
                self.logger.warning(f"Rate limit exceeded, waiting {
     
     wait_time:.2f}s")
                time.sleep(wait_time)
                if not self.rate_limiter.acquire():
                    raise Exception("Rate limit still exceeded after waiting")
            
            # 检查缓存(仅对GET请求)
            cache_key = None
            if method.upper() == 'GET':
                cache_key = self._generate_cache_key(url, kwargs)
                cached_response = self.cache.get(cache_key)
                if cached_response:
                    metrics.cache_hit = True
                    metrics.end_time = datetime.now()
                    metrics.duration = (metrics.end_time - metrics.start_time).total_seconds()
                    self.metrics.append(metrics)
                    self.logger.info(f"Cache hit for {
     
     method} {
     
     url}")
                    return cached_response
            
            # 使用熔断器执行请求
            response = self.circuit_breaker.call(
                self._execute_request_with_retry,
                method, url, metrics, **kwargs
            )
            
            # 缓存成功的GET响应
            if method.upper() == 'GET' and response.status_code == 200 and cache_key:
                self.cache.set(cache_key, response)
            
            return response
            
        except Exception as e:
            metrics.error = str(e)
            metrics.end_time = datetime.now()
            metrics.duration = (metrics.end_time - metrics.start_time).total_seconds()
            self.metrics.append(metrics)
            
            self.logger.error(f"Request failed: {
     
     method} {
     
     url} - {
     
     e}")
            raise
    
    def _execute_request_with_retry(self, method: str, url: str, 
                                   metrics: RequestMetrics, **kwargs) -> requests.Response:
        """执行带重试的请求"""
        last_exception = None
        
        for attempt in range(self.config.max_retries + 1):
            try:
                if attempt > 0:
                    wait_time = self.config.retry_backoff * (2 ** (attempt - 1))
                    self.logger.info(f"Retrying request (attempt {
     
     attempt + 1}) after 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvjesus

码力充电

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值