启用多租户的方法有很多,我不想做一一的介绍,但是有两个极端我们可以考虑一下:
极端一:
是所有租户共享单一应用程序实例,也就是相同的服务器、中间件和应用程序。实现的方法是用租户标识参数对应用程序的单一实例进行参数化。
图1 在多个租户之间共享应用程序和中间件的单一实例
极端二:
是租户在单独的服务器上运行自己的应用程序实例(当前许多 Application Service Provider [ASP] 采用这种方法)。
租户只共享数据中心的基础结构(比如供电和制冷),但是使用应用程序、中间件、操作系统和服务器的不同实例。在图 2所示的示例中,租户 A、B 和 C 使用三个不同的应用程序实例 AppA、AppB 和 AppC,它们在与租户相关的中间件实例、操作系统实例和物理服务器上运行。这种方法最适合那些要求为不同的租户提供充分隔离和定制的工作负载和场景
图2 ASP 通过在多个单独的服务器上运行多个实例启用多租户
其实我们经常在这样做,我们为某个A企业开发了一个OA办公系统,当另一个B企业也需要类似的系统时。我们往往在重新发布一个Application实例,供B企业使用,但我们的目的往往是为了简单方便,而不会去考虑为不同的租户提供充分隔离和定制的工作负载和场景。
由于极端二对于我们实现上是没有任何问题的,即使是需要一些个性化的配置,由于是两套完全独立的Application,我就就可以随意的改动而不影响其他的企业。所以我们今天的重点是分析极端一的实现方式。极端一的方式中Application只发布一个实例,但可以为多个承租者提供服务。实现这种方式最关键的技术是仙子按多租户的数据隔离。
共享数据库、共享 Schema、共享数据表
独立数据库是一个租户独享一个数据库实例,它提供了最强的分离度,租户的数据彼此物理不可见,备份与恢复都很灵活;共享数据库、独立 Schema 将每个租户关联到同一个数据库的不同 Schema,租户间数据彼此逻辑不可见,上层应用程序的实现和独立数据库一样简单,但备份恢复稍显复杂; 最后一种模式则是租户数据在数据表级别实现共享,它提供了最低的成本,但引入了额外的编程复杂性(程序的数据访问需要用 tenantId 来区分不同租户),备份与恢复也更复杂。这三种模式的特点可以用一张图来概括:
图 3. 三种部署模式的异同
上图所总结的是一般性的结论,而在常规场景下需要综合考虑才能决定那种方式是合适的。例如,在占用成本上,认为独立数据库会高,共享模式较低。但如果考虑到大租户潜在的数据扩展需求,有时也许会有相反的成本耗用结论。
目前市面上各类数据厂商在多租户的支持上,大抵都是遵循上文所述的这几类模式,或者混合了几种策略,具体的介绍,可以看下面这篇博客进行的详细的介绍:
数据层的多租户浅谈
在这篇文章中比较系统全面的介绍了hibernate和eclipselink对于多租户的具体的实现,文章的最后也有源码可以下载,经过简单的配置就能正常的运行。是学习多租户的很好的文章。
public class SchemaBasedMultiTenantConnectionProvider implements MultiTenantConnectionProvider, Stoppable,
Configurable, ServiceRegistryAwareService {
private final DriverManagerConnectionProviderImpl connectionProvider = new DriverManagerConnectionProviderImpl();
//得到数据库连接
@Override
public Connection getAnyConnection() throws SQLException {
return connectionProvider.getConnection();
}
//关闭数据库连接
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
connectionProvider.closeConnection(connection);
}
//根据不同用户,Use对应用户的库的链接
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
connection.createStatement().execute("USE " + tenantIdentifier);//重点关注1
} catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier
+ "]", e);
}
return connection;//重点关注2
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
try {
connection.createStatement().execute("USE main");
} catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier
+ "]", e);
}
connectionProvider.closeConnection(connection);
}
……
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/hy6688_/article/details/47170481