@zenweb/tenant
v5.2.0
Published
Zenweb Tenant module
Maintainers
Readme
@zenweb/tenant
ZenWeb 多租户多数据库模块 —— 多个租户共享数据库连接池,按请求动态切换数据库。
工作原理
传统多租户方案通常为每个租户创建独立的数据库连接池,当租户数量增多时会耗尽连接资源。本模块采用共享连接池策略:
- 同一台数据库服务器上的所有租户共用一个连接池
- 获取连接后通过
USE <数据库名>切换到对应租户的数据库 - 按服务器维度配置连接池,而非按租户维度
请求进来 → tenantGetter 识别租户 → 从对应服务器的连接池取连接 → USE db_name → 执行查询安装
npm install @zenweb/tenant快速开始
import { create } from 'zenweb';
import modTenant, { Tenant } from '@zenweb/tenant';
const app = create();
// 租户映射表(实际项目中通常从数据库或配置中心读取)
const tenants: Record<string, Tenant> = {
'a.example.com': { server: 'S1', database: 'db_a' },
'b.example.com': { server: 'S1', database: 'db_b' },
'c.example.com': { server: 'S2', database: 'db_c' },
};
app.setup(modTenant({
// 从请求上下文中识别当前租户
tenantGetter: ctx => {
const tenant = tenants[ctx.host];
if (!tenant) throw new Error('租户不存在: ' + ctx.host);
return tenant;
},
// 按数据库服务器配置连接池
pools: {
S1: {
MASTER: {
host: '127.0.0.1',
port: 3306,
user: 'root',
password: '123456',
charset: 'utf8mb4',
timezone: '+08:00',
connectionLimit: 100,
},
},
S2: {
MASTER: {
host: '192.168.1.100',
port: 3306,
user: 'root',
password: 'secret',
charset: 'utf8mb4',
timezone: '+08:00',
connectionLimit: 50,
},
},
},
}));
app.start();配置说明
tenantGetter
tenantGetter: (ctx: Context) => Tenant | Promise<Tenant>从请求上下文中确定当前租户,返回包含 server 和 database 的对象。识别方式由你决定,常见的有:
- 域名区分:
ctx.host - 请求头区分:
ctx.headers['x-tenant-id'] - 路径前缀区分:
ctx.path.split('/')[1] - Token 区分: 解析 JWT 中的租户字段
pools
pools: {
[服务器名称: string]: {
MASTER: PoolOptions; // 必填,读写库
SLAVES?: PoolOptions[]; // 可选,只读从库,可配多个
}
}- 服务器名称是自定义的标识符,与
Tenant.server对应 - MASTER 是必须配置的读写库
- SLAVES 是可选的只读从库列表,配置后读操作会自动路由到从库
可选配置
| 参数 | 说明 |
|------|------|
| bindQuery | ZenORM 的 bindQuery 方法,传入后可直接使用 Model.find() 等方法 |
| cluster | mysql2 的 PoolClusterOptions,连接池集群配置 |
数据库查询
配置完成后,在控制器中通过 ctx.mysql 进行查询:
import { Get, Context } from 'zenweb';
export class DemoController {
@Get()
async list(ctx: Context) {
// 默认使用主库(MASTER)
const rows = await ctx.mysql.query('SELECT * FROM users');
// 显式指定主库
const rows2 = await ctx.mysql.of('MASTER').query('SELECT * FROM users');
// 使用从库(未配置从库时自动回退到主库)
const rows3 = await ctx.mysql.of('SLAVE*').query('SELECT * FROM users');
return rows;
}
}集成 ZenORM
1. 安装依赖
npm install zenorm --save
npm install @zenorm/generate @zenorm/generate-mysql --save-dev2. 添加生成脚本
在 package.json 中添加:
{
"scripts": {
"dbgen": "zenorm-generate .dbgen.js"
}
}3. 创建生成配置 .dbgen.js
/** @type {import("@zenorm/generate").GenerateConfig} */
module.exports = {
host: "localhost",
port: 3306,
user: "root",
password: "",
database: "my_database",
outputDir: "./model",
bindQuery: true,
};运行 npm run dbgen 生成模型代码。
4. 传入 bindQuery
import { bindQuery } from './model';
app.setup(modTenant({
bindQuery, // 传入后 ORM 模型可直接查询
tenantGetter,
pools,
}));5. 使用模型查询
import { Get } from 'zenweb';
import { User } from '../model';
export class UserController {
@Get()
async list() {
return await User.find().all();
}
@Get(':id')
async detail({ id }) {
return await User.find({ id }).get();
}
}相关链接
- ZenWeb — Web 框架
- @zenweb/mysql — MySQL 模块
- ZenORM — ORM 框架
- 主页
