Kho*_*row 27 postgresql spring hibernate c3p0 multi-tenant
我正在尝试设置一个多租户Web应用程序,同时具有(理想情况下)数据库分离和模式分离方法的可能性.虽然我将从Schema分离开始.我们目前正在使用:
大部分时间我都遵循这个主题(因为解决方案@Transactional).但我在执行方面有点迷失MultiTenantContextConnectionProvider.在这里也提到了类似的问题,但有些方面我无法弄清楚:
1)连接池会发生什么?我的意思是,它是由Spring还是Hibernate管理的?我猜ConnectionProviderBuilder- 或者按照建议 - 任何实现,Hibernate都是管理它的人.
2)Spring不管理连接池是一种好方法吗?或者Spring是否可以管理它?
3)这是未来实现数据库和模式分离的正确途径吗?
任何评论或描述都完全赞赏.
应用程序的context.xml
<beans>
...
<bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource" ref="c3p0DataSource" />
</bean>
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver" />
... other C3P0 related config
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan" value="com.webapp.domain.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.default_schema">public</prop>
<prop key="hibernate.multiTenancy">SCHEMA</prop>
<prop key="hibernate.tenant_identifier_resolver">com.webapp.persistence.utility.CurrentTenantContextIdentifierResolver</prop>
<prop key="hibernate.multi_tenant_connection_provider">com.webapp.persistence.utility.MultiTenantContextConnectionProvider</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="autodetectDataSource" value="false" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
...
</beans>
Run Code Online (Sandbox Code Playgroud)
CurrentTenantContextIdentifierResolver.java
public class CurrentTenantContextIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
return CurrentTenantIdentifier; // e.g.: public, tid130, tid456, ...
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
MultiTenantContextConnectionProvider.java
public class MultiTenantContextConnectionProvider extends AbstractMultiTenantConnectionProvider {
// Do I need this and its configuratrion?
//private C3P0ConnectionProvider connectionProvider = null;
@Override
public ConnectionProvider getAnyConnectionProvider() {
// the main question is here.
}
@Override
public ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
// and of course here.
}
}
Run Code Online (Sandbox Code Playgroud)
编辑
关于@ ben75 的答案:
这是一个新的实现MultiTenantContextConnectionProvider.它不再延伸AbstractMultiTenantConnectionProvider.它宁愿实现MultiTenantConnectionProvider,能够返回[Connection][4]而不是[ConnectionProvider][5]
public class MultiTenantContextConnectionProvider implements MultiTenantConnectionProvider, ServiceRegistryAwareService {
private DataSource lazyDatasource;;
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings();
lazyDatasource = (DataSource) lSettings.get( Environment.DATASOURCE );
}
@Override
public Connection getAnyConnection() throws SQLException {
return lazyDatasource.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
connection.createStatement().execute("SET SCHEMA '" + tenantIdentifier + "'");
}
catch (SQLException e) {
throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
}
return connection;
}
}
Run Code Online (Sandbox Code Playgroud)
ben*_*n75 37
您可以选择3种不同的策略来影响连接轮询.无论如何,你必须提供一个实现MultiTenantConnectionProvider.您选择的策略当然会影响您的实施.
一般评论 MultiTenantConnectionProvider.getAnyConnection()
getAnyConnection()hibernate需要收集元数据并设置SessionFactory.通常在多租户架构中,您有一个未被任何租户使用的特殊/主数据库(或架构).它是一种模板数据库(或模式).如果此方法返回与此数据库(或架构)的连接,则可以.
策略1:每个租户都拥有自己的数据库.(以及它自己的连接池)
在这种情况下,每个租户都有自己的C3PO管理的连接池,您可以提供MultiTenantConnectionProvider基于的实现AbstractMultiTenantConnectionProvider
每个租户都拥有它C3P0ConnectionProvider,所以你所要做的selectConnectionProvider(tenantIdentifier)就是返回正确的租户.你可以保留一个Map来缓存它们,你可以用以下方法延迟初始化C3POConnectionProvider:
private ConnectionProvider lazyInit(String tenantIdentifier){
C3P0ConnectionProvider connectionProvider = new C3P0ConnectionProvider();
connectionProvider.configure(getC3POProperties(tenantIdentifier));
return connectionProvider;
}
private Map getC3POProperties(String tenantIdentifier){
// here you have to get the default hibernate and c3po config properties
// from a file or from Spring application context (there are good chances
// that those default properties point to the special/master database)
// and alter them so that the datasource point to the tenant database
// i.e. : change the property hibernate.connection.url
// (and any other tenant specific property in your architecture like :
// hibernate.connection.username=tenantIdentifier
// hibernate.connection.password=...
// ...)
}
Run Code Online (Sandbox Code Playgroud)
策略2:每个租户都拥有自己的架构,并且它在一个数据库中拥有自己的连接池
这种情况与第一个有关ConnectionProvider实现的策略非常相似,因为您也可以使用它AbstractMultiTenantConnectionProvider作为基类来实现您的MultiTenantConnectionProvider
该实现与策略1的建议实现非常相似,除了您必须在c3po配置中更改模式而不是数据库
策略3:每个租户在单个数据库中拥有自己的架构,但使用共享连接池
这种情况略有不同,因为每个租户都将使用相同的连接提供程序(因此将共享连接池).在这种情况下:连接提供程序必须在使用任何连接之前设置要使用的架构.即你必须实现MultiTenantConnectionProvider.getConnection(String tenantIdentifier)(即提供的默认实现AbstractMultiTenantConnectionProvider将不起作用).
使用postgresql,您可以使用:
SET search_path to <schema_name_for_tenant>;
Run Code Online (Sandbox Code Playgroud)
或使用别名
SET schema <schema_name_for_tenant>;
Run Code Online (Sandbox Code Playgroud)
所以这就是你的意思getConnection(tenant_identifier);:
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
final Connection connection = getAnyConnection();
try {
connection.createStatement().execute( "SET search_path TO " + tenanantIdentifier );
}
catch ( SQLException e ) {
throw new HibernateException(
"Could not alter JDBC connection to specified schema [" +
tenantIdentifier + "]",
e
);
}
return connection;
}
Run Code Online (Sandbox Code Playgroud)
有用的参考在这里 (官方文档)
其他有用的链接C3POConnectionProvider.java
您可以在实施中结合策略1和策略2.您只需要一种方法来为当前租户找到正确的连接属性/连接URL.
编辑
我认为策略2或3之间的选择取决于您的应用程序的流量和租户数量.使用单独的连接池:一个租户可用的连接数量将会低很多,因此:如果出于某种合法原因,一个租户突然需要很多连接,这个特定租户所看到的性能将大幅下降(而另一个租户将不会影响).
另一方面,对于策略3,如果出于某种合法的原因,一个租户突然需要很多联系:每个租户看到的表现都会降低.
一般来说,我认为策略2更灵活,更安全:每个租户不能消耗超过给定数量的连接(如果需要,可以为每个租户配置此数量)
| 归档时间: |
|
| 查看次数: |
19927 次 |
| 最近记录: |