多租户应用程序是一个正在运行的实例为许多客户服务的应用程序。 多租户的替代方法是托管服务,其中为每个客户设置一个正在运行的实例。 下表显示了两种方法之间的比较。
特征 | 多租户申请 | 管理服务 |
成本结构 | 支持基于使用情况的定价以及分层或统一定价。 | 只能支持分层或统一定价。 |
资源资源 | 共享资源。 资源是根据租户的实际负载分配的。 | 专用资源。 资源是根据承租人的感知负载分配的,不能自动重新分配给其他承租人。 |
运维 | 为多个客户管理和管理单个实例 | 管理和管理与客户一样多的实例 |
可扩展模型 | 可扩展,因为一个实例可为许多客户提供服务 | 不可扩展。 随着客户数量的增加,维护要求也成比例增加。 |
对于SaaS产品,可以看到,如果该产品是多租户产品,则有许多好处。 但是,要使应用程序受益于多租户,它的体系结构应注意以下事项:
- 资料隔离
- 功能定制
- 执行环境隔离
资料隔离
在本地解决方案或托管解决方案中,每个租户都有一个专用的数据库实例。 但是,当产品为多租户时,可以在不同的租户之间共享同一数据库。 这引入了阻止一个租户访问另一个租户的数据的复杂性。 数据隔离可以通过不同的方式进行构建:
- 将公司ID添加到所有表中,并使用公司ID限定所有查询。
- 为不同的租户维护不同的数据库,并在运行时连接到正确的数据库。
- 维护一个数据库,但是针对不同租户的不同表,这些表由公司ID限定
下面是这三种方法的比较。
特征 | 添加公司ID | 单独的数据库 | 分开的桌子 |
数据定制 | 不可能,因为对于所有客户仅存在一个表定义。 | 可能的,因为每个客户都有一个表定义。 | 因为每个客户都有一个表定义,所以可能。 |
安全 | 非常低。 独立开发人员需要确保他们在查询中添加正确的子句。 可以创建视图来模仿单独的表设计。 | 很高。 由于它不依赖于单个开发人员来访问正确的数据。 | 高。 使用标准化的表名,可以添加库以访问正确的表。 如果未访问正确的表,则无可用数据。 |
相互依存和绩效 | 一个客户的数据大小会影响其他客户的性能 | 每个客户仅受其数据大小的影响。 | 每个客户仅受其数据大小的影响。 |
可扩展模型 | 部分可扩展。 只需要维护和配置一个数据库。 但是随着客户数量的增加,表的大小也随之增加。 | 不可扩展,随着客户数量的增加,要维护的数据库数量也随之增长。 | 可扩展,仅需维护一个数据库,并且随着客户的增长,数据库中表的数量也随之增长。 |
客户入职 | 简单。 无需完成与数据库相关的任务。 所有表和列均适用于所有客户。 | 难。 必须创建和设置数据库。 可以根据需要创建表。 | 中,必须创建表。 这些可以在表的第一次访问时创建,从而减少了工作量。 |
功能定制
在内部部署或托管服务中,每个客户都有专用的运行时环境。 客户所需的任何定制都可以在该环境上部署的客户代码中完成。 但是,由于一个运行时环境为多个客户提供服务,因此多租户应用程序不能遵循相同的原则。 因此,多租户应用程序已被设计用于功能定制。 租户的自定义通常属于以下类别之一:
- 数据定制–在要存储的数据中添加或删除列
- 功能自定义–针对特定业务事件执行的功能可能因客户而异。 例如,在批准费用后,一个客户可能要求发送电子邮件,而另一个客户可能不需要。
- 流程自定义–业务流程可能因每个客户而异。 例如,一个客户可能希望将货物集成到托运人的跟踪系统中,而另一客户可能希望将货物集成到车队管理系统中。
- 许可功能–产品具有多个许可,这些许可定义了为客户启用的功能。
在多租户应用程序中进行自定义架构时,应考虑以下几点:
- 为一个客户添加,修改或删除功能不应影响其他客户
- 为一个客户添加的功能可能适用于其他客户。 因此,定制不能绑定到单个客户。 该体系结构应允许其他客户轻松启用和禁用相同功能。
- 许可证可以升级或降级。 应该对功能进行分组,以便可以轻松地在客户执行环境中添加或删除功能。
可以使用标志和配置参数来实现功能定制,但是随着客户规模和定制需求的增加,它们变得笨拙并且很容易变得难以维护。
可用于定制的另一种体系结构是将业务功能拆分为离散的模块,并在运行时将它们串在一起。 在SMART中,我们使用即插即用体系结构来实现不同级别的定制。
执行环境隔离
数据隔离和功能自定义的要求意味着每个租户都必须执行环境隔离。 例如,如果我们要实现“数据定制”,则意味着用于加载和保存数据的pojo类需要针对不同的客户进行不同的定义。 如果将相同的执行环境用于不同的租户,则无法完成此操作,因为不能将同一类的两个版本加载到同一类加载器中。 这也意味着不能将部署代码的环境用作所有租户的执行环境。 必须为每个租户创建一个新的执行环境,并且必须在该环境中启用正确的数据源和代码库。
在Java中,可以使用非委托类加载器来实现。 可以为每个租户创建一个新的类加载器,并将许可代码加载到为租户分配的类加载器中。 这使我们有机会
- 部署代码而不影响环境中的其他租户
- 在不打扰其他租户的情况下为单个租户启用功能
- 仅通过为该租户重新创建启用了正确功能的类加载器,即可删除功能。
综上所述,我们可以设计一种可能的架构,如下所示:
翻译自: https://www.javacodegeeks.com/2013/11/architecting-a-multi-tenant-application.html