前言
Java 做 ERP 太重?Spring Cloud 全家桶学不动?XEERP项目用纯 Python 技术栈证明:FastAPI 同样能撑起复杂业务的微服务架构。10 个独立服务、Consul 服务发现、统一网关鉴权限流、全异步 ORM——这套方案在真实生产环境稳定运行。
今天带你拆解这套系统的技术内核。
整体架构:网关 + 业务微服务 + 共享内核
三段式架构:
- 网关层:统一入口,处理认证、限流、路由转发
- 业务服务层:按领域拆分为 10 个独立服务
- 共享内核:公共组件库(数据库/Redis/工具类),避免重复造轮子
核心设计原则: 服务边界清晰、技术栈统一、解耦性强。
Gateway 网关:微服务的"门面担当"
网关是所有请求的第一站,承担三大职责:
2.1 JWT 统一鉴权
class AuthVerifyMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): path = request.url.path # 白名单直接放行(登录/验证码/刷新Token) for white_path in config.whitelist_paths: if path.startswith(white_path): return await call_next(request) # 验证 Token token = request.headers.get("Authorization", "") payload = JwtUtil.verify_token(token) if not payload: return ResponseUtil.unauthorized(msg="token无效或已过期") # 用户信息注入 request.state request.state.user = payload return await call_next(request)
设计要点:
- Token 验证在网关统一处理,业务服务无需重复实现
- 用户信息通过
request.state 向下透传
2.2 动态限流(Consul KV 热更新)
class RequestLimiterMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): # 从 URL 提取服务名 # /api/system-service/user/list → system-service service_name = parts[1] # 从 Consul KV 动态读取限流规则 consul_key = f"config/gateway/limit/{service_name}" limit_rule = ConsulUtil.get_config_kv(consul_key) # "60/minute" # 按 IP + 服务名限流 cache_key = f"{client_ip}:{service_name}" if len(REQUEST_RECORD[cache_key]) >= max_count: return ResponseUtil.too_many_requests()
亮点:
- 限流规则存储在 Consul KV,修改即时生效,无需重启网关
- 支持
second/minute/hour 三种时间窗口
2.3 路由转发(Consul 服务发现)
@router.api_route("/{service_name}/{path:path}", methods=["GET","POST","PUT","DELETE"])async def proxy_request(request, service_name, path): # 从 Consul 获取服务实例 service_map = ConsulUtil.get_all_services_with_instances() instances = service_map[service_name] # 构建目标 URL instance = instances[0] target_url = f"http://{instance['address']}:{instance['port']}/{path}" # 流式转发(支持大文件/SSE) resp = await client.send(req, stream=True) return StreamingResponse(resp.aiter_raw(), ...)
一条路由搞定所有转发 — 通过 FastAPI 的 path 参数捕获,动态路由到对应服务。Consul 负责服务注册与健康检查,网关只做转发。
服务发现:Consul 注册中心
每个微服务启动时自动向 Consul 注册,关停时注销:
@asynccontextmanagerasync def lifespan(app: FastAPI): # 启动:注册到 Consul ConsulUtil.register_service( name="system-service", address=None, # 自动获取本地IP port=8001, service_id="system-service-001" ) yield # 关停:从 Consul 注销 ConsulUtil.deregister_service("system-service", "system-service-001")
Consul 在系统中的角色:
全异步数据库层:SQLAlchemy 2.x + asyncmy
异步引擎 + 连接池
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmakerasync_engine = create_async_engine( url="mysql+asyncmy://user:pass@host:3306/xeerp?charset=utf8mb4", pool_size=50, # 连接池大小 max_overflow=10, # 允许溢出连接数 pool_recycle=3600, # 连接回收周期(防止 MySQL 8小时断开) pool_pre_ping=True, # 每次使用前健康检查)AsyncSessionLocal = async_sessionmaker( bind=async_engine, expire_on_commit=False, # 关键:提交后对象不失效)
为什么选 asyncmy 而非 aiomysql?
- asyncmy 是 asyncio 原生实现,性能更好
- 与 SQLAlchemy 2.x async 无缝集成
ORM 模型示例
class Base(AsyncAttrs, DeclarativeBase): """所有 Model 的基类""" passclass SysUser(Base): __tablename__ = 'sys_user' user_id = Column(BigInteger, primary_key=True) user_name = Column(String(64), nullable=False) dept_id = Column(BigInteger) status = Column(CHAR(1), default='0') create_time = Column(DateTime, default=datetime.now)
权限体系:RBAC + 数据权限
功能权限
采用 用户 → 角色 → 菜单/权限标识 的经典 RBAC 模型:
@CheckPerm("system:user:add") # 装饰器声明所需权限async def add_user(request, user): ...# 装饰器实现def CheckPerm(perm: str): def decorator(func): async def wrapper(*args, **kwargs): user = kwargs.get("user") # 超级管理员直接通过 if "*" in user.perms: return await func(*args, **kwargs) # 检查权限标识 if perm not in user.perms: raise ServiceException(f"无权限:{perm}", 403) return await func(*args, **kwargs) return wrapper return decorator
数据权限(部门级别)
class DataPermission: @staticmethod def get_filter(model, user): scope = user.data_scope # 1=全部 2=本部门及子级 3=本部门 4=本人 5=自定义 if scope == 1: return True if scope == 4: return model.create_user_id == user.user_id if scope == 3: return model.create_dept_id == user.dept_id if scope == 5: return model.create_dept_id.in_(user.custom_dept_ids)
五种数据范围,覆盖企业常见场景: 总经理看全部、部门经理看本部门、业务员只看自己的数据。
跨服务通信:BaseClient 封装
微服务之间通过 HTTP 调用通信,统一封装在 clients/ 目录:
class BaseClient: """所有服务客户端的基类""" def __init__(self, base_url: str, timeout: int = 30): self.base_url = base_url self.timeout = aiohttp.ClientTimeout(total=timeout) async def _request(self, method, path, params=None, data=None): session = await self.get_session() url = f"{self.base_url}/{path.lstrip('/')}" async with session.request(method, url, params=params, json=data) as resp: return resp.status, await resp.json() async def get(self, path, **kwargs): return await self._request("GET", path, **kwargs) async def post(self, path, **kwargs): return await self._request("POST", path, **kwargs)
业务服务继承 BaseClient,一行代码完成跨服务调用:
class StockClient(BaseClient): def __init__(self): super().__init__(base_url="http://127.0.0.1:8003") async def get_inventory(self, product_id: int): return await self.get(f"/api/stock/inventory/{product_id}")
设计思考:
统一响应 + 全局异常:企业级 API 规范
ResponseUtil 统一响应
class ResponseUtil: @classmethod def success(cls, msg="操作成功", data=None, rows=None, total=None): return JSONResponse(content={ "code": 200, "msg": msg, "success": True, "time": datetime.now().isoformat(), "data": data, "rows": rows, "total": total }) @classmethod def fail(cls, msg="操作失败", code=500): ... @classmethod def unauthorized(cls, msg="请先登录"): ... @classmethod def too_many_requests(cls, msg="请求频繁"): ...
15+ 种预定义响应方法,覆盖所有 HTTP 状态码场景。
四层异常处理器
# 业务异常 → 400app.add_exception_handler(ServiceException, biz_exception_handler)# HTTP 异常 → 对应状态码app.add_exception_handler(HTTPException, http_exception_handler)# 参数校验 → 400app.add_exception_handler(RequestValidationError, validation_exception_handler)# 兜底异常 → 500app.add_exception_handler(Exception, global_exception_handler)
任何异常都不会返回原始堆栈给前端。
定时任务:APScheduler + 数据库持久化
系统服务内置完整的定时任务管理能力:
from apscheduler.schedulers.asyncio import AsyncIOSchedulerfrom apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore# 任务存储到数据库(重启不丢失)job_stores = { 'default': SQLAlchemyJobStore(engine=sync_engine)}scheduler = AsyncIOScheduler( jobstores=job_stores, executors={'default': AsyncIOExecutor()},)
特色功能:
- 支持 6/7 位 Cron 表达式(兼容 Linux crontab 和带秒级的扩展格式)
# 自定义 Cron 支持 6/7 位class MyCronTrigger(CronTrigger): @classmethod def from_crontab(cls, expr, timezone=None): values = expr.split() # 5位=标准 / 6位=带秒 / 7位=带秒+年 if len(values) == 5: # 秒 分 时 日 月 周 ...
Redis 缓存策略
Redis 在系统中承担多重角色:
| | |
|---|
| session:{user_id} | |
| sys_dict:{dict_type} | |
| sys_config:{key} | |
| captcha:{uuid} | |
| | |
启动预热机制:
# 服务启动时,一次性加载高频数据到 Redisawait RedisUtil.init_sys_dict(redis) # 字典预热await RedisUtil.init_sys_config(redis) # 参数预热await RedisUtil.init_sys_user(redis) # 在线用户恢复
每个服务的统一分层
所有微服务遵循相同的目录结构,新人一看就懂:
service/├── api/v1/ # Controller 层:路由定义 + 参数校验│ ├── user_controller.py│ └── dept_controller.py├── service/ # Service 层:业务编排 + 事务│ ├── user_service.py│ └── dept_service.py├── dao/ # DAO 层:数据库操作封装│ ├── user_dao.py│ └── dept_dao.py├── models/ # Model 层:ORM 实体├── schemas/ # Schema 层:Pydantic 校验模型├── clients/ # Client 层:跨服务调用├── core/ # 本服务私有配置└── middlewares/ # 本服务中间件
分层铁律:
性能优化细节
orjson 替换标准 json
app = FastAPI(json_serializer=orjson.dumps)
orjson 比标准库快 3-10 倍,对于高频 JSON 序列化场景效果显著。
Linux 自动切换高性能模式
if os.name == "posix": LOOP = "uvloop" # 替换默认 asyncio 事件循环 HTTP = "httptools" # 替换默认 HTTP 解析器
uvloop + httptools 组合,让 FastAPI 的吞吐量接近 Go/Rust 级别。
连接池调优
# 50 个常驻连接 + 10 个溢出连接pool_size=50, max_overflow=10# 连接回收(防 MySQL wait_timeout 断开)pool_recycle=3600# 使用前探活(避免拿到死连接)pool_pre_ping=True
技术栈全景
| | |
|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| Gunicorn + Uvicorn Workers | |
10 个服务一览
技术对比:FastAPI vs Spring Cloud
很多人会问:企业级 ERP 为什么不用 Java?我们来做一个实打实的对比:
框架层对比
| | |
|---|
| ~1s 极快启动 | |
| ~50-80MB / 服务 | |
| | 线程池(Servlet)/ WebFlux(响应式) |
| 极高(50% 代码量) | |
| | Bean Validation + Swagger 配置 |
| 自动生成(零配置) | |
| | 需 Spring DevTools / JRebel |
| ~20MB(纯代码) | |
微服务基础设施对比
性能实测对比(同条件 CRUD 接口)
测试环境:4核 8G,MySQL 8.0,100 并发连接,单表 10w 行数据
开发体验对比
| | |
|---|
| 5 分钟 | |
| 复制模板 10 分钟 | |
| | |
| async链路清晰 | |
| 1-2 天 | |
| pip install | Maven/Gradle + JDK + 打包 + JVM 调优 |
适用场景对比
核心结论
不是“Python 能不能做微服务”的问题,而是“什么场景下用哪个更合适”的问题。
我们选择 FastAPI 的核心理由:
- 人效比:3 人 Python 团队 = 8 人 Java 团队的产出
- 资源占用:10 个服务总内存 < 1GB,Java 需 4-5GB
- AI 融合:同一语言对接 LLM/RAG/数据分析,零成本
- 部署简单:无 JVM、无 Maven、无 Fat JAR,一个 pip install
总结:Python 做微服务的几点思考
- FastAPI 完全能胜任企业级微服务 — 异步性能优秀,生态成熟
- Consul 比 Nacos 更轻量 — 单二进制部署,Go 实现,资源占用极低
- 网关是微服务的咽喉 — 鉴权、限流、日志在此一把搞定
- 共享内核(common)是关键 — 统一响应/分页/日志,避免各服务各搞一套
- 全异步到底 — 从 ORM 到 Redis 到 HTTP 调用,一条链路全异步不阻塞
- 分层约定大于自由 — Controller/Service/DAO 三层严格分离,新人一天上手
Python 微服务不是玩具,它可以是真正的生产力。
关于 XEERP-FASTAPI
企业级进销存微服务系统,面向中小制造与流通型企业。10 个微服务按业务域独立部署,覆盖权限、商品、库存、生产、采购、销售、报表、审批全业务线。
技术栈:Python 3.12 + FastAPI + SQLAlchemy 2.x + MySQL 8 + Redis 7 + Consul + Vue3。
开源地址:https://gitee.com/zhiops/xeerp-fastapi
如果需要进一步了解可以加:
