在微服务架构横行的今天,API 网关(API Gateway) 绝对是站在最前线的"看门大爷"。不管是统一身份认证、流量控制、路由转发,还是日志监控,都离不开它。
很多人觉得网关这种底层基础设施,必须得用 Go(比如 Kong)或者 Java(比如 Spring Cloud Gateway)。但今天,我们要给 Python 正名:利用 Python 的异步生态(FastAPI + asyncio + httpx),我们完全可以搞出一个既轻量又高性能的 API 网关!
本文将带你从零手写一个 Python API 网关全栈项目,包含核心网关引擎与前端管理后台。
为什么是 Python?先看架构设计
在动笔写代码之前,咱们先聊聊架构。一个合格的 API 网关,最核心的任务就是接收客户端请求 -> 解析并鉴权 -> 转发给后端微服务 -> 返回响应。
在这个过程中,网关主要在做 I/O 密集型 的工作。Python 的 FastAPI 配合 uvicorn,底层基于 uvloop,处理高并发 I/O 的能力完全够用。
项目技术栈
-
网关核心(Backend/Engine): FastAPI + HTTPX(异步 HTTP 客户端)+ Redis(用于限流和缓存)
-
管理后台(Frontend): Vue 3 + Vite + Element Plus(用于动态配置路由、查看日志)
-
持久化存储: PostgreSQL / MySQL(存储路由规则、用户信息)
核心实现:三步构建网关引擎
别把网关想得太神秘,它的本质就是一个高级的反向代理服务器。我们来看看最核心的三个核心模块怎么写。
1. 动态路由与反向代理
网关需要根据请求的 URL,把请求转发到不同的后端服务。在 FastAPI 中,我们可以使用一个通配符路由来捕获所有请求:
from fastapi import FastAPI, Request, Response
import httpx
app = FastAPI()
# 初始化异步 HTTP 客户端,维持长连接复用
client = httpx.AsyncClient()
# 模拟一个内存中的路由表(实际项目中应从 Redis 或 数据库中读取)
ROUTE_TABLE = {
"/user": "http://127.0.0.1:8001",
"/order": "http://127.0.0.1:8002"
}
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def route_gateway(path: str, request: Request):
# 1. 寻找匹配的后端服务
target_service = None
for prefix, target in ROUTE_TABLE.items():
if path.startswith(prefix.lstrip("/")):
target_service = target
break
if not target_service:
return Response(content="Service Not Found", status_code=404)
# 2. 拼接目标 URL
target_url = f"{target_service}/{path}"
# 3. 转发请求
# 转发时需要带上原请求的 method, headers, query_params 和 body
req_headers = dict(request.headers)
# 移除可能引发冲突的 host 头部
req_headers.pop("host", None)
try:
target_response = await client.request(
method=request.method,
url=target_url,
headers=req_headers,
params=request.query_params,
content=await request.body(),
timeout=10.0
)
# 4. 返回后端服务的响应
return Response(
content=target_response.content,
status_code=target_response.status_code,
headers=dict(target_response.headers)
)
except httpx.RequestError as exc:
return Response(content=f"Gateway Error: {str(exc)}", status_code=502)
2. 拦截器:统一身份认证(Auth Middleware)
网关的第二个大招是统一鉴权,免去每个微服务重复写鉴权逻辑的痛苦。我们可以利用 FastAPI 的中间件或者依赖注入来实现。
from fastapi import HttpExceptions, status
async def verify_jwt_token(request: Request):
# 从 header 中获取 Authorization
token = request.headers.get("Authorization")
if not token or not token.startswith("Bearer "):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing or invalid token"
)
# 这里可以解密 JWT 或者去 Redis 校验 token 有效性
# user_info = decode_jwt(token)
# request.state.user = user_info
3. 坚固的盾牌:基于 Redis 的漏桶/令牌桶限流
为了防止后端微服务被突发流量冲垮,网关必须具备限流(Rate Limiting)能力。利用 Redis 的原子操作,我们可以轻松实现一个固定窗口或令牌桶限流器。
import time
from aioredis import Redis
async def is_rate_limited(redis: Redis, client_ip: str, limit: int = 100, window: int = 60) -> bool:
"""
简单窗口限流:每个IP在 window(秒) 内最多访问 limit(次)
"""
current_time = int(time.time())
key = f"rate:{client_ip}:{current_time // window}"
# 增加计数
current_requests = await redis.incr(key)
if current_requests == 1:
# 设置过期时间,防止内存泄露
await redis.expire(key, window)
return current_requests > limit
全栈的另一半:可视化控制台
光有后台引擎还不够,优秀的开源项目都有个漂亮的 Dashboard。
在我们的前端 Vue 3 项目中,主要构建以下几个模块:
-
路由管理面板 :支持动态配置
前端路径 -> 后端服务URL的映射,一键禁用/启用路由。 -
插件开关:可视化勾选是否开启某个路由的"限流"、"鉴权"、"日志追踪"功能。
-
监控大屏:通过对接后端上报的 Prometheus 数据或者直接读取 Redis 中的统计数据,展示网关当前的 QPS、响应延迟和错误率。
避坑提示(CORS问题) :因为网关是所有流量的入口,前端管理后台在调用网关自身的管理 API 时,一定要记得在 FastAPI 中配置好
CORSMiddleware,否则天天和浏览器跨域报错"面面相觑"。
性能调优与生产落地建议
如果你打算把这个 Python 网关用到生产环境,以下几点是拉开小白与大牛差距的关键:
-
连接池复用: 核心引擎中的
httpx.AsyncClient()千万不要每个请求都实例化一遍!必须作为全局单例复用,否则高并发下光是建立 TCP 连接就会把文件句柄耗尽。 -
配置热更新: 路由表不要频繁读取数据库。正确的做法是:前端修改配置存入数据库,同时向 Redis 发布一个
route_update频道(Pub/Sub);网关引擎订阅该频道,一旦收到通知,直接更新内存中的路由表,实现无感零停机热更新。 -
多进程部署: 单个 Uvicorn 进程只能利用单核。生产环境请用
Gunicorn -k uvicorn.workers.UvicornWorker -w 4(根据 CPU 核心数调整),把多核性能榨干。
总结
手写一个 API 网关不仅能让你对 HTTP 协议、异步编程、网络 I/O 有更深的理解,更是简历上极其吸睛的全栈加分项。
用 Python 写网关,或许在绝对性能上无法和 Go/Rust 刚正面,但在开发效率、动态扩展能力以及生态对接上,Python 的体验绝对是超一流的。
项目代码: