python Web开发从入门到精通(二十五)FastAPI项目实战:微服务架构API网关(上)

2 阅读1分钟

摘要钩子:微服务架构中,几十个服务散落各处,接口调用混乱不堪?一个简单的用户请求要在十几个服务间跳转?是时候用一个统一的API网关来终结这种混乱了!本文带你从零构建一个基于FastAPI的企业级API网关,实现服务发现、动态路由、负载均衡、熔断限流等核心功能。通过3000+行实战代码,你将掌握微服务架构的核心枢纽技术,打造高可用、易维护的分布式系统!

开篇:微服务架构的"交通警察"为什么必不可少?

最近和一家电商公司的架构师朋友聊天,他们刚经历了微服务拆分后的"阵痛期":原本单体应用拆成了32个微服务,结果发现新的问题更多了——

  • 客户端调用混乱:前端需要对接十几个不同服务的接口地址
  • 认证授权分散:每个服务都要重复实现用户验证逻辑
  • 监控困难:问题发生时,难以追踪完整的请求链路
  • 性能瓶颈:没有统一的限流熔断,一个服务挂掉会拖垮整个系统

这就像一个大城市没有交通信号灯,每个路口都堵成一团。而API网关就是这个城市的"交通警察",它站在所有微服务的前端,统一处理所有入口流量,让系统变得井然有序。

先看一个真实案例:某在线教育平台,微服务化后接口响应时间从平均50ms暴涨到200ms。引入API网关后,通过智能路由和缓存优化,响应时间降低到70ms,同时系统可用率从99.5%提升到99.95%。这就是API网关的价值!

如果你正在规划或已经实施了微服务架构,却苦于如何管理这"散落一地"的服务,那么这篇文章就是为你准备的。我们将手把手构建一个功能完整的API网关,让你不仅理解理论,更能亲手实践。

第一部分:项目规划与架构设计

1.1 需求分析:企业级API网关需要哪些功能?

在开始编码之前,我们先明确API网关的核心职责:

  1. 路由转发:将客户端请求正确转发到后端服务
  2. 负载均衡:在多实例间分配请求,提高系统吞吐量
  3. 服务发现:动态感知后端服务的上线和下线
  4. 认证授权:统一验证用户身份,避免每个服务重复实现
  5. 限流熔断:防止雪崩效应,保证系统稳定性
  6. 日志监控:记录完整请求链路,便于问题排查
  7. 缓存优化:对热点数据进行缓存,减少后端压力

1.2 技术选型:为什么选择FastAPI?

2025年的微服务架构中,API网关有多种技术选择,我们选择FastAPI的原因:

技术方案

优势

劣势

适用场景

FastAPI

异步高性能,自动文档,类型安全

生态相对年轻

高并发API网关

Spring Cloud Gateway

Java生态完整,功能丰富

内存消耗大,启动慢

大型企业Java技术栈

Nginx + Lua

性能极致,稳定可靠

开发门槛高,调试困难

超大规模流量入口

Kong

功能全面,插件丰富

资源占用多,配置复杂

需要丰富插件支持

FastAPI的优势在于:

  • 异步非阻塞:单机即可支持数万并发连接
  • 自动文档:OpenAPI自动生成,调试方便
  • 类型安全:Pydantic保证数据格式正确性
  • 易于扩展:中间件机制灵活,可快速添加新功能

1.3 系统架构设计

我们的API网关将采用分层架构:

┌─────────────────────────────────────────┐
│           客户端请求                     │
└─────────────────┬───────────────────────┘
                  │
          ┌───────▼────────┐
          │   API网关入口   │ ←─ 认证、限流、日志
          │   (FastAPI)    │
          └───────┬────────┘
                  │
          ┌───────▼────────┐
          │   路由引擎      │ ←─ 服务发现、负载均衡
          │                │
          └───────┬────────┘
                  │
          ┌───────▼────────┐
          │   代理转发      │ ←─ 请求改写、响应处理
          │                │
          └───────┬────────┘
                  │
          ┌───────▼────────┐
          │  后端微服务     │
          │ (User/Order/...)│
          └─────────────────┘

1.4 项目结构规划

创建清晰的项目结构是保持代码可维护性的关键:

api-gateway/
├── app/
│   ├── __init__.py
│   ├── main.py              # 应用入口
│   ├── config.py            # 配置管理
│   ├── database.py          # 数据库连接(用于存储路由规则)
│   ├── models.py            # 数据模型
│   ├── schemas.py           # Pydantic模型
│   ├── dependencies.py      # 依赖注入
│   ├── middleware.py        # 中间件(认证、限流、日志)
│   ├── routers/
│   │   ├── __init__.py
│   │   ├── gateway.py       # 网关路由入口
│   │   └── admin.py         # 管理接口
│   ├── services/
│   │   ├── __init__.py
│   │   ├── router.py        # 路由引擎
│   │   ├── discovery.py     # 服务发现
│   │   ├── load_balancer.py # 负载均衡
│   │   ├── auth.py          # 认证服务
│   │   └── circuit_breaker.py # 熔断器
│   ├── utils/
│   │   ├── __init__.py
│   │   ├── logger.py        # 日志工具
│   │   └── cache.py         # 缓存工具
│   └── clients/
│       ├── __init__.py
│       └── http_client.py   # HTTP客户端
├── tests/                   # 测试代码
├── docker-compose.yml       # Docker编排
├── Dockerfile              # Docker镜像
├── requirements.txt        # Python依赖
└── README.md               # 项目说明

现在开始搭建基础环境。

第二部分:环境搭建与基础框架

2.1 一键安装与验证

我们提供三种安装方式,满足不同开发者的需求:

方式一:使用uv(2025年最快的包管理工具)

# 安装uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# 创建项目目录
mkdir api-gateway && cd api-gateway

# 创建虚拟环境并安装依赖
uv venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
uv pip install -r requirements.txt

方式二:传统pip方式

python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txt

方式三:Docker快速启动

# 克隆项目
git clone <your-repo>
cd api-gateway

# 启动所有服务
docker-compose up -d

安装完成后,验证FastAPI版本:

python -c "import fastapi; print(f'FastAPI版本: {fastapi.__version__}')"

2.2 配置管理:环境变量与配置文件分离

在企业级项目中,配置管理至关重要。我们采用三层配置策略:

  1. 环境变量:最敏感的信息(数据库密码、API密钥)
  2. 配置文件:环境相关的配置(数据库地址、Redis地址)
  3. 代码默认值:开发环境的默认配置

创建 app/config.py

# app/config.py
from pydantic import Field
from pydantic_settings import BaseSettings
from typing import Optional, List
import os

class Settings(BaseSettings):
    """应用配置"""
    
    # 应用配置
    app_name: str = "API Gateway"
    app_version: str = "1.0.0"
    debug: bool = False
    
    # 服务配置
    host: str = "0.0.0.0"
    port: int = 8000
    
    # 数据库配置
    database_url: str = Field(
        default="postgresql://postgres:password@localhost:5432/api_gateway",
        env="DATABASE_URL"
    )
    
    # Redis配置
    redis_url: str = Field(
        default="redis://localhost:6379/0",
        env="REDIS_URL"
    )
    
    # 认证配置
    secret_key: str = Field(
        default="your-secret-key-change-in-production",
        env="SECRET_KEY"
    )
    algorithm: str = "HS256"
    access_token_expire_minutes: int = 30
    
    # 网关配置
    service_discovery_type: str = "consul"  # consul, zookeeper, static
    consul_host: str = "localhost"
    consul_port: int = 8500
    
    # 限流配置
    rate_limit_enabled: bool = True
    rate_limit_requests: int = 100  # 每分钟请求数
    rate_limit_window: int = 60  # 时间窗口(秒)
    
    # 熔断器配置
    circuit_breaker_enabled: bool = True
    circuit_breaker_failure_threshold: int = 5
    circuit_breaker_recovery_timeout: int = 30
    
    class Config:
        env_file = ".env"
        env_file_encoding = "utf-8"

settings = Settings()

def get_settings() -> Settings:
    """获取配置实例"""
    return settings

创建 .env 文件(不提交到版本控制):

env

# 开发环境配置
DEBUG=true
DATABASE_URL=postgresql://postgres:password@localhost:5432/api_gateway_dev
REDIS_URL=redis://localhost:6379/0
SECRET_KEY=dev-secret-key-change-in-production

2.3 数据库模型设计

API网关需要存储路由规则、服务信息、限流策略等。我们使用SQLAlchemy ORM定义数据模型:

创建 app/models.py

# app/models.py
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, ForeignKey
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship
from app.database import Base

class Service(Base):
    """微服务信息"""
    __tablename__ = "services"
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(100), unique=True, index=True, nullable=False)
    description = Column(Text, nullable=True)
    base_url = Column(String(500), nullable=False)  # 服务基础URL
    health_check_url = Column(String(500), nullable=True)
    status = Column(String(20), default="healthy")  # healthy, unhealthy, degraded
    tags = Column(Text, nullable=True)  # JSON格式的标签
    
    # 负载均衡信息
    load_balancer_type = Column(String(50), default="round_robin")
    instances = relationship("ServiceInstance", back_populates="service", cascade="all, delete-orphan")
    routes = relationship("Route", back_populates="service", cascade="all, delete-orphan")
    
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())

class ServiceInstance(Base):
    """服务实例信息"""
    __tablename__ = "service_instances"
    
    id = Column(Integer, primary_key=True, index=True)
    service_id = Column(Integer, ForeignKey("services.id", ondelete="CASCADE"))
    instance_id = Column(String(100), unique=True, index=True)
    host = Column(String(200), nullable=False)
    port = Column(Integer, nullable=False)
    weight = Column(Integer, default=1)  # 权重
    metadata = Column(Text, nullable=True)  # JSON格式的元数据
    status = Column(String(20), default="up")  # up, down, draining
    
    service = relationship("Service", back_populates="instances")
    
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    last_heartbeat = Column(DateTime(timezone=True))

class Route(Base):
    """路由规则"""
    __tablename__ = "routes"
    
    id = Column(Integer, primary_key=True, index=True)
    service_id = Column(Integer, ForeignKey("services.id", ondelete="CASCADE"))
    path = Column(String(500), nullable=False, index=True)
    methods = Column(String(200), default="GET,POST,PUT,DELETE")  # 逗号分隔
    strip_prefix = Column(Boolean, default=True)  # 是否剥离路径前缀
    timeout = Column(Integer, default=30)  # 超时时间(秒)
    retry_count = Column(Integer, default=3)  # 重试次数
    rate_limit_id = Column(Integer, ForeignKey("rate_limits.id"), nullable=True)
    
    # 路由匹配优先级
    priority = Column(Integer, default=0)
    enabled = Column(Boolean, default=True)
    
    service = relationship("Service", back_populates="routes")
    rate_limit = relationship("RateLimit", back_populates="routes")
    
    created_at = Column(DateTime(timezone=True), server_default=func.now())

class RateLimit(Base):
    """限流策略"""
    __tablename__ = "rate_limits"
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(100), unique=True, index=True)
    limit = Column(Integer, nullable=False)  # 请求数量
    window = Column(Integer, nullable=False)  # 时间窗口(秒)
    algorithm = Column(String(50), default="fixed_window")  # fixed_window, sliding_window
    
    routes = relationship("Route", back_populates="rate_limit")
    
    created_at = Column(DateTime(timezone=True), server_default=func.now())

class CircuitBreaker(Base):
    """熔断器状态"""
    __tablename__ = "circuit_breakers"
    
    id = Column(Integer, primary_key=True, index=True)
    service_id = Column(Integer, ForeignKey("services.id", ondelete="CASCADE"))
    failure_count = Column(Integer, default=0)
    success_count = Column(Integer, default=0)
    state = Column(String(20), default="closed")  # closed, open, half_open
    last_failure_time = Column(DateTime(timezone=True), nullable=True)
    last_state_change = Column(DateTime(timezone=True))
    
    service = relationship("Service")

2.4 数据库连接与迁移

创建 app/database.py

# app/database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from app.config import settings

# 创建数据库引擎
engine = create_engine(
    settings.database_url,
    pool_pre_ping=True,  # 连接池健康检查
    pool_recycle=3600,   # 连接回收时间(秒)
    pool_size=20,        # 连接池大小
    max_overflow=40      # 最大溢出连接数
)

# 创建会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 声明基类
Base = declarative_base()

def get_db():
    """获取数据库会话"""
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

创建数据库迁移脚本 alembic/env.pyalembic/versions/,这里我们使用Alembic进行数据库迁移。

2.5 应用入口与FastAPI实例

创建 app/main.py

# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from app.config import settings
from app.database import engine, Base
from app.routers import gateway, admin
from app.middleware import (
    AuthenticationMiddleware,
    RateLimitMiddleware,
    LoggingMiddleware,
    CircuitBreakerMiddleware
)

@asynccontextmanager
async def lifespan(app: FastAPI):
    """应用生命周期管理"""
    # 启动时:创建数据库表
    print("🚀 API网关启动中...")
    Base.metadata.create_all(bind=engine)
    
    # 初始化服务发现
    from app.services.discovery import ServiceDiscovery
    service_discovery = ServiceDiscovery()
    await service_discovery.initialize()
    
    # 启动健康检查
    from app.services.health_check import HealthChecker
    health_checker = HealthChecker()
    health_checker.start()
    
    print(f"✅ API网关已启动,访问地址: http://{settings.host}:{settings.port}")
    print(f"📚 API文档: http://{settings.host}:{settings.port}/docs")
    print(f"📊 管理后台: http://{settings.host}:{settings.port}/admin")
    
    yield
    
    # 关闭时:清理资源
    print("🛑 API网关关闭中...")
    health_checker.stop()
    await service_discovery.close()

# 创建FastAPI应用
app = FastAPI(
    title=settings.app_name,
    version=settings.app_version,
    description="企业级微服务API网关",
    docs_url="/docs",
    redoc_url="/redoc",
    openapi_url="/openapi.json",
    lifespan=lifespan
)

# 添加CORS中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生产环境应限制来源
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 添加自定义中间件
app.add_middleware(AuthenticationMiddleware)
app.add_middleware(RateLimitMiddleware)
app.add_middleware(LoggingMiddleware)
app.add_middleware(CircuitBreakerMiddleware)

# 注册路由
app.include_router(gateway.router, prefix="", tags=["网关"])
app.include_router(admin.router, prefix="/admin", tags=["管理"])

@app.get("/health")
async def health_check():
    """健康检查接口"""
    return {
        "status": "healthy",
        "service": settings.app_name,
        "version": settings.app_version,
        "timestamp": datetime.datetime.now().isoformat()
    }

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(
        "app.main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.debug
    )

至此,我们的基础框架已经搭建完成。下一部分我们将实现核心的路由转发功能。

第三部分:核心路由转发功能

3.1 路由匹配引擎

路由匹配是API网关的核心功能,需要高效地将请求路径映射到对应的后端服务。我们实现一个支持通配符和正则表达式的路由匹配器。

创建 app/services/router.py

# app/services/router.py
import re
from typing import Dict, List, Optional, Tuple
from app.models import Route, Service
from app.database import SessionLocal

class RouteMatcher:
    """路由匹配引擎"""
    
    def __init__(self):
        self.routes: Dict[str, Route] = {}
        self.path_patterns: List[Tuple[str, Route]] = []
        self._load_routes()
    
    def _load_routes(self):
        """从数据库加载路由规则"""
        db = SessionLocal()
        try:
            routes = db.query(Route).filter(Route.enabled == True).all()
            for route in routes:
                self.routes[route.path] = route
                
                # 将路径转换为正则表达式
                pattern = self._path_to_regex(route.path)
                self.path_patterns.append((pattern, route))
                
            # 按优先级排序
            self.path_patterns.sort(key=lambda x: x[1].priority, reverse=True)
            
            print(f"✅ 加载了 {len(routes)} 条路由规则")
        finally:
            db.close()
    
    def _path_to_regex(self, path: str) -> str:
        """将路径模式转换为正则表达式"""
        # 替换路径参数
        pattern = re.sub(r'{([^}]+)}', r'(?P<\1>[^/]+)', path)
        # 处理通配符
        pattern = pattern.replace('*', '.*')
        # 确保精确匹配
        pattern = f'^{pattern}$'
        return pattern
    
    def match(self, request_path: str, method: str) -> Optional[Tuple[Route, Dict]]:
        """匹配路由"""
        # 先尝试精确匹配
        if request_path in self.routes:
            route = self.routes[request_path]
            if self._check_method(route, method):
                return route, {}
        
        # 尝试模式匹配
        for pattern, route in self.path_patterns:
            match = re.match(pattern, request_path)
            if match and self._check_method(route, method):
                params = match.groupdict()
                return route, params
        
        return None
    
    def _check_method(self, route: Route, method: str) -> bool:
        """检查请求方法是否允许"""
        allowed_methods = [m.strip().upper() for m in route.methods.split(',')]
        return method.upper() in allowed_methods or '*' in allowed_methods
    
    def refresh(self):
        """刷新路由缓存"""
        self.routes.clear()
        self.path_patterns.clear()
        self._load_routes()

# 全局路由匹配器实例
route_matcher = RouteMatcher()

3.2 服务发现与负载均衡

现代微服务架构中,服务实例是动态变化的。我们需要实现服务发现和负载均衡功能。

创建 app/services/discovery.py

# app/services/discovery.py
import asyncio
import random
from typing import Dict, List, Optional
from app.models import Service, ServiceInstance
from app.database import SessionLocal
import httpx
import json

class LoadBalancer:
    """负载均衡器"""
    
    def __init__(self):
        self.strategies = {
            'round_robin': self._round_robin,
            'random': self._random,
            'weighted': self._weighted,
            'least_connections': self._least_connections
        }
    
    def select_instance(
        self, 
        instances: List[ServiceInstance], 
        strategy: str = 'round_robin',
        **kwargs
    ) -> Optional[ServiceInstance]:
        """选择服务实例"""
        if not instances:
            return None
        
        # 过滤掉不健康的实例
        healthy_instances = [inst for inst in instances if inst.status == 'up']
        if not healthy_instances:
            return None
        
        # 选择负载均衡策略
        selector = self.strategies.get(strategy, self._round_robin)
        return selector(healthy_instances, **kwargs)
    
    def _round_robin(self, instances: List[ServiceInstance], **kwargs) -> ServiceInstance:
        """轮询策略"""
        if not hasattr(self, '_rr_counters'):
            self._rr_counters = {}
        
        instance_ids = [inst.id for inst in instances]
        key = tuple(instance_ids)
        
        if key not in self._rr_counters:
            self._rr_counters[key] = 0
        
        idx = self._rr_counters[key] % len(instances)
        self._rr_counters[key] += 1
        
        return instances[idx]
    
    def _random(self, instances: List[ServiceInstance], **kwargs) -> ServiceInstance:
        """随机策略"""
        return random.choice(instances)
    
    def _weighted(self, instances: List[ServiceInstance], **kwargs) -> ServiceInstance:
        """加权随机策略"""
        weights = [inst.weight for inst in instances]
        total = sum(weights)
        rand = random.uniform(0, total)
        
        cumulative = 0
        for inst, weight in zip(instances, weights):
            cumulative += weight
            if rand <= cumulative:
                return inst
        
        return instances[-1]
    
    def _least_connections(self, instances: List[ServiceInstance], **kwargs) -> ServiceInstance:
        """最少连接策略(简化版)"""
        # 实际项目中会从监控系统获取连接数
        return instances[0]

class ServiceDiscovery:
    """服务发现"""
    
    def __init__(self):
        self.services: Dict[str, Service] = {}
        self.instances: Dict[str, List[ServiceInstance]] = {}
        self.load_balancer = LoadBalancer()
        self._initialized = False
    
    async def initialize(self):
        """初始化服务发现"""
        if self._initialized:
            return
        
        # 从数据库加载服务信息
        self._load_services()
        
        # 根据配置选择服务发现方式
        from app.config import settings
        if settings.service_discovery_type == 'consul':
            await self._init_consul()
        elif settings.service_discovery_type == 'zookeeper':
            await self._init_zookeeper()
        else:
            # 静态配置
            self._init_static()
        
        self._initialized = True
        print(f"✅ 服务发现已初始化,发现 {len(self.services)} 个服务")
    
    def _load_services(self):
        """从数据库加载服务"""
        db = SessionLocal()
        try:
            services = db.query(Service).all()
            for service in services:
                self.services[service.name] = service
                
                # 加载服务实例
                instances = db.query(ServiceInstance).filter(
                    ServiceInstance.service_id == service.id,
                    ServiceInstance.status == 'up'
                ).all()
                self.instances[service.name] = instances
        finally:
            db.close()
    
    async def _init_consul(self):
        """初始化Consul客户端"""
        from app.config import settings
        import consul
        
        # 创建Consul客户端
        c = consul.Consul(
            host=settings.consul_host,
            port=settings.consul_port
        )
        
        # 获取所有服务
        services = c.agent.services()
        for service_id, service_info in services.items():
            service_name = service_info.get('Service')
            if service_name not in self.services:
                # 动态添加服务
                self._add_dynamic_service(service_name, service_info)
    
    def _init_static(self):
        """静态服务发现"""
        # 使用数据库中的配置
        pass
    
    def _add_dynamic_service(self, name: str, info: Dict):
        """添加动态发现的服务"""
        # 简化实现
        pass
    
    def get_service(self, name: str) -> Optional[Service]:
        """获取服务信息"""
        return self.services.get(name)
    
    def get_service_instance(
        self, 
        service_name: str, 
        strategy: str = 'round_robin'
    ) -> Optional[ServiceInstance]:
        """获取服务实例"""
        instances = self.instances.get(service_name, [])
        return self.load_balancer.select_instance(instances, strategy)
    
    async def close(self):
        """关闭服务发现"""
        self._initialized = False

# 全局服务发现实例
service_discovery = ServiceDiscovery()

3.3 HTTP客户端与代理转发

创建高效的HTTP客户端来处理请求转发。

创建 app/clients/http_client.py

# app/clients/http_client.py
import httpx
from typing import Dict, Any, Optional
import json
import time
from app.config import settings

class GatewayHTTPClient:
    """网关HTTP客户端"""
    
    def __init__(self):
        # 创建连接池
        limits = httpx.Limits(
            max_connections=1000,
            max_keepalive_connections=100
        )
        self.client = httpx.AsyncClient(
            limits=limits,
            timeout=httpx.Timeout(30.0),
            follow_redirects=False
        )
        self.circuit_breakers: Dict[str, Dict] = {}
    
    async def forward_request(
        self,
        method: str,
        target_url: str,
        headers: Dict[str, str],
        body: Optional[bytes] = None,
        params: Optional[Dict] = None,
        timeout: int = 30
    ) -> httpx.Response:
        """转发请求"""
        # 检查熔断器状态
        if self._is_circuit_open(target_url):
            raise CircuitBreakerOpen(f"Circuit breaker open for {target_url}")
        
        try:
            # 发送请求
            start_time = time.time()
            response = await self.client.request(
                method=method,
                url=target_url,
                headers=headers,
                content=body,
                params=params,
                timeout=timeout
            )
            elapsed = time.time() - start_time
            
            # 更新熔断器状态
            self._update_circuit_status(target_url, success=True)
            
            # 记录日志
            self._log_request(method, target_url, response.status_code, elapsed)
            
            return response
            
        except Exception as e:
            # 更新熔断器状态
            self._update_circuit_status(target_url, success=False)
            
            # 重新抛出异常
            raise
    
    def _is_circuit_open(self, target_url: str) -> bool:
        """检查熔断器是否开启"""
        # 简化实现
        return False
    
    def _update_circuit_status(self, target_url: str, success: bool):
        """更新熔断器状态"""
        # 简化实现
        pass
    
    def _log_request(
        self, 
        method: str, 
        url: str, 
        status_code: int, 
        elapsed: float
    ):
        """记录请求日志"""
        # 简化实现
        if settings.debug:
            print(f"🌐 {method} {url} -> {status_code} ({elapsed:.3f}s)")
    
    async def close(self):
        """关闭客户端"""
        await self.client.aclose()

class CircuitBreakerOpen(Exception):
    """熔断器开启异常"""
    pass

# 全局HTTP客户端实例
http_client = GatewayHTTPClient()

3.4 网关路由实现

现在将这些组件组合起来,实现完整的网关路由。

创建 app/routers/gateway.py

# app/routers/gateway.py
from fastapi import APIRouter, Request, Response, HTTPException
from fastapi.responses import JSONResponse
from app.services.router import route_matcher
from app.services.discovery import service_discovery
from app.clients.http_client import http_client
import json

router = APIRouter()

@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
async def gateway_proxy(request: Request, path: str):
    """网关代理入口"""
    
    # 构建完整请求路径
    full_path = f"/{path}" if path else "/"
    
    # 匹配路由
    match_result = route_matcher.match(full_path, request.method)
    if not match_result:
        raise HTTPException(status_code=404, detail="Route not found")
    
    route, path_params = match_result
    
    # 获取服务信息
    service = service_discovery.get_service(route.service.name)
    if not service:
        raise HTTPException(status_code=503, detail="Service unavailable")
    
    # 选择服务实例
    instance = service_discovery.get_service_instance(
        service.name, 
        strategy=service.load_balancer_type
    )
    if not instance:
        raise HTTPException(status_code=503, detail="No healthy instances")
    
    # 构建目标URL
    target_path = full_path
    if route.strip_prefix:
        # 移除路由前缀
        target_path = full_path[len(route.path):] or "/"
    
    target_url = f"http://{instance.host}:{instance.port}{target_path}"
    
    # 准备请求头
    headers = dict(request.headers)
    # 移除Host头,避免干扰
    headers.pop("host", None)
    
    # 添加网关标识
    headers["X-API-Gateway"] = "fastapi-gateway"
    headers["X-Forwarded-For"] = request.client.host if request.client else "unknown"
    
    # 获取请求体
    body = await request.body()
    
    try:
        # 转发请求
        response = await http_client.forward_request(
            method=request.method,
            target_url=target_url,
            headers=headers,
            body=body if body else None,
            params=dict(request.query_params),
            timeout=route.timeout
        )
        
        # 构建响应
        content = response.content
        if response.headers.get("content-length"):
            content_length = int(response.headers["content-length"])
        else:
            content_length = len(content) if content else 0
        
        # 创建FastAPI响应
        fastapi_response = Response(
            content=content,
            status_code=response.status_code,
            headers=dict(response.headers)
        )
        
        return fastapi_response
        
    except Exception as e:
        # 错误处理
        error_detail = f"Gateway error: {str(e)}"
        print(f"❌ 网关转发失败: {error_detail}")
        
        return JSONResponse(
            status_code=502,
            content={
                "error": "Bad Gateway",
                "message": error_detail,
                "service": service.name,
                "path": full_path
            }
        )

@router.get("/gateway/routes")
async def list_routes():
    """列出所有路由"""
    routes = []
    for path, route in route_matcher.routes.items():
        routes.append({
            "path": path,
            "service": route.service.name,
            "methods": route.methods,
            "enabled": route.enabled,
            "priority": route.priority
        })
    
    return {"routes": routes}

@router.post("/gateway/refresh")
async def refresh_routes():
    """刷新路由缓存"""
    route_matcher.refresh()
    return {"message": "Routes refreshed successfully"}

至此,我们的核心路由转发功能已经完成。下一部分我们将实现认证授权、限流熔断等高级功能。