복붙노트

[SPRING] Spring, Hibernate 및 C3P0을 사용하여 다중 사용자 웹 응용 프로그램의 연결 풀 관리

SPRING

Spring, Hibernate 및 C3P0을 사용하여 다중 사용자 웹 응용 프로그램의 연결 풀 관리

나는 (동시에) Database-separated와 Schema-separated 접근을위한 (이상적으로) 가능성을 가진 multi-tenant 웹 어플리케이션을 셋업하려고한다. 스키마 분리로 시작하겠습니다. 현재 다음을 사용 중입니다.

대부분이 스레드를 따라갔습니다 (@Transactional의 해결책 때문에). 하지만 MultiTenantContextConnectionProvider 구현에 다소 분실했습니다. 그래서 여기에 비슷한 질문이 있습니다.하지만 알아낼 수없는 몇 가지 측면이 있습니다.

1) 연결 풀링은 어떻게됩니까? 내 말은, Spring이나 Hibernate에 의해 관리 되는가? 나는 ConnectionProviderBuilder로 - 또는 제안 된대로 - 구현 중 하나 인, Hibernate가 그것을 관리하는 사람이라고 생각한다. 2) Spring이 Connection Pooling을 관리하지 않는 좋은 접근 방법인가? 또는 Spring이 관리하는 것이 가능한가? 3) 데이터베이스와 스키마 분리를 구현하기위한 올바른 경로인가?

모든 의견이나 설명은 완전히 감사드립니다.

application-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>

CurrentTenantContextIdentifierResolver.java

public class CurrentTenantContextIdentifierResolver implements CurrentTenantIdentifierResolver {
    @Override
    public String resolveCurrentTenantIdentifier() {
        return CurrentTenantIdentifier;  // e.g.: public, tid130, tid456, ...
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        return true;
    }
}

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.
    }
}

편집하다

@ ben75의 대답에 관해서 :

이것은 MultiTenantContextConnectionProvider의 새로운 구현입니다. 더 이상 AbstractMultiTenantConnectionProvider를 확장하지 않습니다. [ConnectionProvider] [5] 대신 [Connection] [4]를 반환 할 수 있도록 MultiTenantConnectionProvider를 구현합니다.

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;
    }
}

해결법

  1. ==============================

    1.연결 폴링에 영향을주는 세 가지 전략 중에서 선택할 수 있습니다. 어쨌든 MultiTenantConnectionProvider 구현을 제공해야합니다. 선택하는 전략은 물론 구현에 영향을 미칩니다.

    연결 폴링에 영향을주는 세 가지 전략 중에서 선택할 수 있습니다. 어쨌든 MultiTenantConnectionProvider 구현을 제공해야합니다. 선택하는 전략은 물론 구현에 영향을 미칩니다.

    MultiTenantConnectionProvider.getAnyConnection ()에 대한 일반적인 설명

    메타 데이터를 수집하고 SessionFactory를 설정하기 위해 hibernate에 의해 getAnyConnection ()이 필요합니다. 대개 멀티 테넌트 구조에서는 세입자가 사용하지 않는 특수 / 마스터 데이터베이스 (또는 스키마)가 있습니다. 그것은 일종의 템플릿 데이터베이스 (또는 스키마)입니다. 이 메소드가이 데이터베이스 (또는 스키마)에 대한 연결을 반환하면 괜찮습니다.

    전략 1 : 각 세입자는 자체 데이터베이스를 가지고 있습니다. (그래서 자신의 연결 풀)

    이 경우 각 테넌트는 C3PO가 관리하는 자체 연결 풀을 보유하고 있으며 AbstractMultiTenantConnectionProvider를 기반으로 MultiTenantConnectionProvider 구현을 제공 할 수 있습니다

    모든 임차인은 자신의 C3P0ConnectionProvider를 가지고 있으므로 selectConnectionProvider (tenantIdentifier)에서해야 할 일은 올바른 것을 반환하는 것입니다. 캐시를 유지하기 위해지도를 유지할 수 있으며 다음과 같이 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=...
        //     ...) 
    }
    

    전략 2 : 각 테넌트는 자체 스키마가 있으며 단일 데이터베이스에 자체 연결 풀이 있습니다.

    이 경우는 ConnectionProvider 구현에 관한 첫 번째 전략과 매우 유사합니다. AbstractMultiTenantConnectionProvider를 기본 클래스로 사용하여 MultiTenantConnectionProvider

    구현은 전략 1의 제안 된 구현과 매우 유사하지만 c3po 구성에서 데이터베이스 대신 스키마를 변경해야한다는 점만 다릅니다

    전략 3 : 각 테넌트는 단일 데이터베이스에 자체 스키마가 있지만 공유 연결 풀을 사용합니다.

    이 경우는 모든 임차인이 동일한 연결 제공자를 사용할 것이므로 연결 경로가 공유되므로 약간 다릅니다. 이 경우 연결 제공자는 연결을 사용하기 전에 사용할 스키마를 설정해야합니다. 즉, MultiTenantConnectionProvider.getConnection (String tenantIdentifier)을 구현해야합니다. 즉, AbstractMultiTenantConnectionProvider에서 제공하는 기본 구현이 작동하지 않습니다.

    postgresql을 사용하면 다음과 같이 할 수 있습니다.

     SET search_path to <schema_name_for_tenant>;
    

    또는 별칭 사용

     SET schema <schema_name_for_tenant>;
    

    그래서 여기에 귀하의 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;
    }
    

    유용한 참고 자료는 여기 (공식 문서)

    기타 유용한 링크 C3POConnectionProvider.java

    구현시 전략 1과 전략 2를 결합 할 수 있습니다. 현재 거주자에 대한 올바른 연결 속성 / 연결 URL을 찾는 방법이 필요합니다.

    편집하다

    나는 전략 2 또는 3 사이의 선택은 트래픽 및 귀하의 애플 리케이션에 세입자의 수에 따라 달라집니다 생각합니다. 별도의 연결 풀을 사용할 경우 : 한 명의 임차인이 사용할 수있는 연결 수는 훨씬 적으므로 한 명의 임차인이 갑자기 많은 연결을 필요로하는 일부 정당한 이유가있는 경우이 특정 임차인의 성과는 크게 떨어집니다 (다른 임차인은 영향을 받음).

    반면에 전략 3을 사용하는 경우 합법적 인 이유로 한 거주 인이 갑자기 많은 연결을 필요로하는 경우 모든 임차인이 보는 실적이 감소합니다.

    일반적으로 전략 2는보다 유연하고 안전하다고 생각합니다. 모든 거주자가 주어진 연결량 이상을 소비 할 수 없습니다. (그리고 필요한 경우 세입자 당이 금액을 구성 할 수 있습니다)

  2. ==============================

    2.IMHO, 연결 풀 관리는 SQL Server 자체에서 기본적으로 처리되지만 C #과 같은 일부 프로그래밍 언어는 풀을 제어하는 ​​몇 가지 방법을 제공합니다. 여기를 참조하십시오.

    IMHO, 연결 풀 관리는 SQL Server 자체에서 기본적으로 처리되지만 C #과 같은 일부 프로그래밍 언어는 풀을 제어하는 ​​몇 가지 방법을 제공합니다. 여기를 참조하십시오.

    세입자를위한 (1) 스키마 또는 (2) 별도 데이터베이스의 선택은 세입자가 예상 할 수있는 데이터의 양에 달려 있습니다. 그러나 다음 사항을 고려해 볼 가치가 있습니다.

    위의 사항에 대한 귀하의 의견을 공유하십시오.

  3. from https://stackoverflow.com/questions/21223894/manage-connection-pooling-in-multi-tenant-web-app-with-spring-hibernate-and-c3p by cc-by-sa and MIT license