django多租户应用的最佳体系结构
我一直沉迷于正确/最佳的方式来创build一个基于Django的多租户应用程序。
一些解释:
-
应用程序可以由几个租户(tenant1,tenant2,…)使用。
-
所有的租户个人数据必须得到保护,防止其他租户(及其用户)的访问。
-
租户可以为应用程序对象创build额外的自定义字段。
-
当然,底层硬件限制了一个“系统”上租户的数量。
1)通过例如子域分隔每个租户,并使用底层的租户特定数据库
2)使用模型中的一些租户ID分离数据库中的租户数据
我正在考虑部署过程,系统部分(networking服务器,数据库服务器,工作节点等)的性能。
什么是最好的设置? 亲和骗子在哪里?
你怎么看?
我们使用以下架构构build了多租户平台 。 我希望你能find一些有用的提示。
- 每个租户都获得子域(t1.example.com)
- 使用url重写Django应用程序的请求被重写为example.com/t1
- 所有的url定义都以
(r'^(?P<tenant_id>[\w\-]+)
- 中间件处理并使用tenant_id并将其添加到请求中(例如,request.tenant ='t1')
- 现在,您可以在每个视图中使用当前租户,而无需在每个视图中指定tenant_id参数
- 在某些情况下,您没有可用的请求。 我通过将tenant_id绑定到当前线程来解决这个问题(类似于使用
threading.local
的当前语言 ) - 创build装饰器(例如承租人知道的
login_required
请求),中间件或工厂来保护视图并select正确的模型 - 关于数据库,我使用了两种不同的情况:
- 根据当前租户设置多个数据库并configuration路由 。 我首先使用了这个function,但是大约一年后就切换到一个数据库了 原因如下:
- 我们不需要高度安全的解决scheme来分离数据
- 不同的租户使用几乎所有相同的模型
- 我们必须pipe理很多数据库(并没有build立一个简单的更新/迁移过程)
- 使用一个数据库与一些简单的映射表,即用户和不同的模型。 要添加其他特定于租户的模型字段,我们使用模型inheritance 。
- 根据当前租户设置多个数据库并configuration路由 。 我首先使用了这个function,但是大约一年后就切换到一个数据库了 原因如下:
关于环境我们使用以下设置:
- Nginx的
- uWSGI
- PostgreSQL的
- Memcached的
从我的angular度来看,这个设置有以下的亲和骗局:
优点:
- 一个应用程序实例知道当前的租户
- 项目的大部分内容都不必担心佃户的具体问题
- 所有租户之间共享实体的简单解决scheme(例如,消息)
魂斗罗:
- 一个相当大的数据库
- 一些非常相似的表由于模型的inheritance
- 在数据库层不安全
当然,最好的架构在很大程度上取决于您的租户数量,模型的增量,安全要求等等。
更新 :正如我们回顾我们的架构,我build议不要重写如第2-3点所示的URL。 我认为更好的解决scheme是将tenant_id
作为请求标头,并将请求中的tenant_id
从request.META.get('TENANT_ID', None)
提取出来(第4点request.META.get('TENANT_ID', None)
。 这样你就可以获得中立的URL,使用Django内置函数(例如{% url ...%}
或reverse()
)或外部应用程序要容易得多。
以下是有关讨论的一些指示:
- Django票#15089 :“contrib.sites和multitenancy ”
- 讨论Google网上论坛 ,详见esp。 贾里·潘纳恩的解决scheme在最后
- 在Mezzanine中引入基于线程位置的多租户的补丁 ; 见esp。
mezzanine.utils.sites.current_site_id
,mezzanine.core.models.SiteRelated
和mezzanine.core.request
- PyPI上的django-simple-multitenant可重用应用程序
我build议看一下https://github.com/bcarneiro/django-tenant-schemas 。 它会解决你的问题有点像Reto提到的,只是它使用postgresql模式。