django 上多租户应用程序的最佳架构 [英] optimal architecture for multitenant application on django
问题描述
我一直在思考创建基于多租户应用程序的正确/最佳方式在 Django 上.
I've been brooding over the right/optimal way to create a multitenancy application based on Django.
一些解释:
应用程序可以被多个租户使用(tenant1、tenant2、...、).
Application can be used by several tenants (tenant1, tenant2, ...,).
必须保护所有租户个人数据,防止其他租户(及其用户)访问.
All tenant-individual data has to be secured against access of other tenants (and their users).
租户可以选择为应用程序对象创建额外的自定义字段.
Optionally tenants can create additional custom-fields for application-objects.
当然,底层硬件限制了一个系统"上的租户数量.
Of course, underlying hardware limits number of tenants on one "system".
1) 通过例如分隔每个租户子域并在底层使用租户特定的数据库
1) Separating each tenant by e.g. sub-domain and using tenant-specific databases in the underlying layer
2) 使用模型中的一些tenant-ID来分离数据库中的tenant-data
2) Using some tenant-ID in the model to separate the tenant-data in the database
我正在考虑部署流程、系统部件的性能(网络服务器、数据库服务器、工作节点……)
I am thinking about deployment-processes, performance of the system-parts (web-server(s), database-server(s), working-node(s),...)
最好的设置是什么?优点和缺点在哪里?
What would be the best setup ? Where are the pro's and con's?
你怎么看?
推荐答案
我们构建了一个多租户平台使用以下架构.我希望你能找到一些有用的提示.
We built a multitenancy platform using the following architecture. I hope you can find some useful hints.
- 每个租户获得子域 (t1.example.com)
- 使用 url 重写 Django 应用程序的请求被重写为类似 example.com/t1 的内容
- 所有 url 定义都以
(r'^(?P<tenant_id>[\w\-]+)
为前缀 - A 中间件进程并使用租户 ID 并将其添加到请求中(例如 request.tenant = 't1')
- 现在您可以在每个视图中使用当前租户,而无需在每个视图中指定tenant_id 参数
- 在某些情况下,您没有可用的请求.我通过将tenant_id绑定到当前线程解决了这个问题(类似于当前语言 使用
threading.local
) - 创建装饰器(例如租户感知
login_required
)、中间件或工厂以保护视图并选择正确的模型 - 关于数据库,我使用了两种不同的场景:
- 设置多个数据库并配置一个路由 根据当前租户.我首先使用它,但大约一年后切换到一个数据库.原因如下:
- 我们不需要高度安全的解决方案来分离数据
- 不同的租户使用几乎所有相同的模型
- 我们不得不管理大量数据库(并且没有构建简单的更新/迁移流程)
- Each tenant gets sub-domain (t1.example.com)
- Using url rewriting the requests for the Django application are rewritten to something like example.com/t1
- All url definitions are prefixed with something like
(r'^(?P<tenant_id>[\w\-]+)
- A middleware processes and consumes the tenant_id and adds it to the request (e.g. request.tenant = 't1')
- Now you have the current tenant available in each view without specifying the tenant_id argument every view
- In some cases you don't have the request available. I solved this issue by binding the tenant_id to the current thread (similar to the current language using
threading.local
) - Create decorators (e.g a tenant aware
login_required
), middlewares or factories to protect views and select the right models - Regarding to the databases I used two different scenarios:
- Setup multiple databases and configure a routing according to current tenant. I used this first but switched to one database after about one year. The reasons were the following:
- We didn't need a high secure solution to separate the data
- The different tenants used almost all the same models
- We had to manage a lot of databases (and didn't built an easy update/migration process)
关于环境,我们使用以下设置:
Regarding the environment we use the following setup:
在我看来,这种设置有以下优点和缺点:
From my point of view this setup has the following pro's and con's:
专业:
- 一个知道当前租户的应用程序实例
- 项目的大部分内容都不必为租户的特定问题而烦恼
- 在所有租户之间共享实体的简单解决方案(例如消息)
反对:
- 一个相当大的数据库
- 由于模型继承,一些非常相似的表
- 在数据库层没有保护
当然,最佳架构在很大程度上取决于您的要求,如租户数量、模型的增量、安全要求等.
Of course the best architecture strongly depends on your requirements as number of tenants, the delta of your models, security requirements and so on.
更新:在我们审查我们的架构时,我建议不要重写第 2-3 点中所示的 URL.我认为更好的解决方案是将
tenant_id
作为请求标头并使用request.META 之类的内容从请求中提取(第 4 点)
.通过这种方式,您可以获得中性 URL,并且可以更轻松地使用 Django 内置函数(例如tenant_id
.get('TENANT_ID', None){% url ...%}
或reverse()
)或外部应用程序.Update: As we reviewed our architecture, I suggest to not rewrite the URL as indicated in point 2-3. I think a better solutions is to put the
tenant_id
as a Request Header and extract (point 4) thetenant_id
out of the request with something likerequest.META.get('TENANT_ID', None)
. This way you get neutral URLs and it's much easier to use Django built-in functions (e.g.{% url ...%}
orreverse()
) or external apps.这篇关于django 上多租户应用程序的最佳架构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- Setup multiple databases and configure a routing according to current tenant. I used this first but switched to one database after about one year. The reasons were the following:
- 设置多个数据库并配置一个路由 根据当前租户.我首先使用它,但大约一年后切换到一个数据库.原因如下: