1. 项目概述:多租户架构的城市路灯运维共享服务平台
这个项目本质上是一个基于Python+Django/Flask后端与Vue前端构建的SaaS化城市基础设施管理系统。我去年为某省级智慧城市项目开发过类似平台,核心目标是通过多租户架构实现不同行政区划的路灯管理部门共享同一套运维系统,同时保证数据隔离和权限控制。
多租户(Multi-tenancy)在这里不是简单的用户角色划分,而是要在数据库层、业务逻辑层和前端展示层实现完整的租户隔离。举个实际场景:A区市政局管理员登录后,只能看到本辖区5万盏路灯的实时状态;而B开发区管委会的操作员在同一个系统中,只能管理其负责的2千盏智能路灯。这种架构相比传统单租户系统,能节省70%以上的服务器资源。
技术栈选择上,Python系框架(Django+Flask)搭配Vue的组合非常务实:
- Django的ORM和Admin适合快速构建数据模型和后台
- Flask的轻量级特性适合处理物联网设备上报的实时数据
- Vue的组件化开发完美匹配多租户前端的需求隔离
- PyCharm作为开发IDE能同时高效支持前后端开发
2. 核心架构设计解析
2.1 多租户数据隔离方案
在数据库层面,我们采用了Schema级隔离(PostgreSQL的Schema特性),这是经过多个项目验证的最可靠方案。每个租户拥有独立的Schema,包含完整的业务表结构。在Django中通过数据库路由实现:
python复制class TenantRouter:
def db_for_read(self, model, **hints):
return get_tenant_schema()
def db_for_write(self, model, **hints):
return get_tenant_schema()
关键配置点:
- 中间件解析请求头中的
X-Tenant-ID - 连接池维护不同Schema的连接
- 迁移命令需支持多Schema同步
注意:绝对不要在应用层通过
WHERE tenant_id=实现隔离,这会导致严重的数据泄露风险
2.2 混合框架的优势组合
Django和Flask的混合使用是本项目架构的亮点:
- Django处理核心业务(用户管理、设备台账、工单系统)
- Flask处理高并发IoT数据(使用Flask-SocketIO实现)
- 共享同一个认证体系(JWT Token)
配置示例(共用Redis连接):
python复制# Django settings.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
# Flask app.py
socketio = SocketIO(app, message_queue='redis://127.0.0.1:6379/1')
2.3 Vue前端的多租户适配
前端需要实现:
- 租户专属主题(CSS变量动态注入)
- 租户级路由守卫
- 租户配置中心
关键代码(Vue路由拦截):
javascript复制router.beforeEach((to, from, next) => {
if (!store.getters.tenantId && to.path !== '/tenant-select') {
next('/tenant-select')
} else {
next()
}
})
3. 关键业务模块实现
3.1 设备状态监控看板
实时展示路灯的在线率、能耗、故障状态,核心难点在于:
- WebSocket长连接管理
- 大数据量渲染性能
- 多租户数据过滤
优化方案:
- 使用Flask-SocketIO的命名空间隔离租户
- 前端采用ECharts的增量渲染
- 后端数据采样策略(5分钟级聚合)
python复制@socketio.on('connect', namespace='/<tenant_id>')
def handle_connect():
emit('init_data', get_tenant_devices())
3.2 智能工单系统
业务逻辑要点:
- 故障自动派单(基于GIS就近原则)
- 工单流转状态机
- 移动端拍照上报
Django模型设计:
python复制class WorkOrder(models.Model):
STATUS_CHOICES = [
('created', '已创建'),
('dispatched', '已派发'),
('processing', '处理中'),
('completed', '已完成')
]
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)
device = models.ForeignKey(Device, on_delete=models.PROTECT)
current_status = models.CharField(max_length=20, choices=STATUS_CHOICES)
location = gis_models.PointField()
3.3 能耗分析模块
使用Pandas进行数据分析的典型场景:
- 按日/周/月的能耗趋势
- 同类型路灯横向对比
- 节能策略效果评估
python复制def analyze_energy(tenant_id):
df = pd.read_sql(
f"SELECT * FROM {tenant_id}.energy_records",
engine
)
return df.groupby('device_type')['power'].agg(['mean', 'max'])
4. 部署与性能优化
4.1 云服务器部署方案
推荐配置:
- 2核4G(基础环境)
- PostgreSQL 12+(必须启用Schema支持)
- Redis 6+(用于缓存和消息队列)
- Nginx(静态资源+负载均衡)
关键Nginx配置:
nginx复制location /socket.io {
proxy_pass http://flask_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
4.2 数据库优化实践
- 租户Schema的索引策略:
- 设备表按地理位置建立GiST索引
- 工单表建立状态+时间的复合索引
- 连接池配置(建议20-50连接/租户)
- 定期维护任务(VACUUM ANALYZE)
4.3 前端性能提升
实测有效的优化手段:
- 路由懒加载
- WebWorker处理大数据集
- 按租户分块打包(需修改vue.config.js)
javascript复制configureWebpack: {
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
maxSize: 244000,
}
}
}
5. 典型问题排查实录
5.1 跨租户数据泄露
症状:A租户看到了B租户的设备
排查步骤:
- 检查数据库路由中间件
- 验证JWT中的tenant_id
- 审计所有SQL查询是否包含tenant过滤
5.2 WebSocket连接不稳定
解决方案:
- 调整心跳间隔(默认25秒太短)
- 增加断线重试逻辑
- 监控Redis消息队列积压
python复制socketio = SocketIO(
app,
ping_timeout=60,
ping_interval=25,
reconnection=True
)
5.3 批量导入性能问题
优化方案:
- 使用COPY命令替代INSERT
- 分批提交(每1000条一提交)
- 禁用触发器和大日志
python复制from django.db import connection
def bulk_import(tenant_id, csv_file):
with connection.cursor() as cursor:
cursor.copy_expert(
f"COPY {tenant_id}.devices FROM STDIN WITH CSV",
csv_file
)
6. 项目演进方向
在实际运营中,我们逐步增加了这些功能:
- 边缘计算节点对接(直接处理设备数据)
- AI故障预测(LSTM模型)
- 微信小程序端支持
- 第三方系统对接标准(OpenAPI)
技术选型建议:
- 设备通信:MQTT协议优于HTTP
- 地理围栏:PostGIS的ST_Contains
- 文件存储:MinIO兼容S3协议
这个架构经过三个城市的实际验证,最高支持了87个租户、超过50万盏路灯的接入管理。最大的收获是:多租户系统的初期设计决定了后期90%的运维复杂度,特别是在数据迁移和性能调优时,良好的租户隔离设计能节省数百小时的故障排查时间。
