webman虽无内置多租户功能,但凭借中间件生命周期、协程上下文和di容器可扩展性,可构建生产级saas架构;租户识别须在最早中间件完成,推荐基于http_host域名匹配并调用tenantcontext::setcontext()与destroy(),数据库需按租户隔离连接,权限校验须平台层、租户层、数据层三重过滤。

Webman 本身不提供开箱即用的多租户能力,但它的中间件生命周期、协程上下文支持和 DI 容器可扩展性,足以支撑生产级多租户 SaaS 架构。关键不是“有没有内置功能”,而是“能否在请求进入后第一时间绑定 tenant_id,并让后续所有数据操作自动感知该上下文”。
租户识别必须放在最早期中间件
一旦 ORM 查询、缓存读取或日志记录先于租户识别执行,就可能污染上下文或查错库。域名(tenant1.example.com)比 URL 参数(?tenant_id=123)更可靠——前者无法被前端随意篡改,且适用于静态资源、WebSocket 等非 HTTP API 场景。
- 推荐从
$_SERVER['HTTP_HOST']提取主域名,再查tenants表匹配domain字段 - 识别成功后立即调用
TenantContext::setContext(),传入tenant_id、db_name和隔离模式(如schema) - 务必在中间件末尾调用
TenantContext::destroy(),否则协程复用时残留旧租户上下文,这是线上最隐蔽的数据越界来源
数据库连接不能复用全局配置
Webman 默认 PDO 连接池不支持运行时切换 dbname 或 host。若强行修改全局配置,会导致并发请求互相覆盖连接参数,引发跨租户数据写入。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 共享库 + 独立 Schema 模式:在模型基类的
getTableName()中拼接前缀,例如return TenantContext::id() . '.' . $this->table; - 独立数据库模式:在
TenantContext::setContext()后,用原生PDO新建连接,并绑定到容器特定 key(如db.tenant),所有租户内模型强制使用该实例 - 禁用“全局 scope 自动加
tenant_id”逻辑——在 Schema 或 DB 隔离下,该字段无意义,反而增加查询负担和误判风险
RBAC 权限校验必须分层穿透
权限不是一次性配完就结束的事,而是在平台层、租户层、数据层三道关口持续过滤。漏掉任何一层,都可能导致越权访问。
- 平台层:验证租户是否启用、是否欠费、是否被冻结(
tenant.status) - 租户层:检查当前用户在该租户内是否有调用此接口的权限(基于
role_permission关系表) - 数据层:对敏感查询(如订单列表)强制追加
WHERE tenant_id = ?,哪怕已确认租户上下文有效——防止业务代码疏忽绕过中间件
真正难的不是写几行识别租户的代码,而是确保整个请求链路(包括队列任务、定时器回调、异常日志)都在同一协程上下文里正确继承 tenant_id。协程变量泄漏、连接未销毁、缓存 key 未带租户标识——这些细节才是压垮多租户系统的最后一根稻草。










