sql >> Database >  >> RDS >> PostgreSQL

Beheer Connection Pooling in multi-tenant web-app met Spring, Hibernate en C3P0

U kunt kiezen uit 3 verschillende strategieën die van invloed zijn op het pollen van verbindingen. Je moet in ieder geval een implementatie van MultiTenantConnectionProvider . aanleveren . De strategie die u kiest, heeft natuurlijk invloed op uw implementatie.

Algemene opmerking over MultiTenantConnectionProvider.getAnyConnection()

getAnyConnection() is vereist door de slaapstand om metadata te verzamelen en de SessionFactory in te stellen. Meestal heb je in een architectuur met meerdere tenants een speciale/masterdatabase (of schema) die door geen enkele tenant wordt gebruikt. Het is een soort sjabloondatabase (of schema). Het is oké als deze methode een verbinding met deze database (of schema) retourneert.

Strategie 1:elke tenant heeft zijn eigen database. (en dus een eigen verbindingspool)

In dit geval heeft elke tenant zijn eigen verbindingspool die wordt beheerd door C3PO en kunt u een implementatie van MultiTenantConnectionProvider bieden gebaseerd op AbstractMultiTenantConnectionProvider

Elke huurder heeft zijn eigen C3P0ConnectionProvider , dus alles wat u hoeft te doen in selectConnectionProvider(tenantIdentifier) is om de juiste terug te sturen. U kunt een kaart bewaren om ze in de cache op te slaan en u kunt een C3POConnectionProvider lui initialiseren met zoiets als:

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=...
    //     ...) 
}

Strategie 2:elke tenant heeft zijn eigen schema en zijn eigen verbindingspool in één database

Dit geval lijkt erg op de eerste strategie met betrekking tot ConnectionProvider implementatie aangezien u ook AbstractMultiTenantConnectionProvider . kunt gebruiken als basisklasse om uw MultiTenantConnectionProvider . te implementeren

De implementatie lijkt erg op de voorgestelde implementatie voor Strategie 1, behalve dat u het schema moet wijzigen in plaats van de database in de c3po-configuratie

Strategie 3:elke tenant heeft zijn eigen schema in één database, maar gebruikt een gedeelde verbindingspool

Dit geval is iets anders, omdat elke huurder dezelfde verbindingsprovider gebruikt (en dus de verbindingspool wordt gedeeld). In het geval:de verbindingsprovider moet het te gebruiken schema instellen voordat de verbinding wordt gebruikt. d.w.z. u moet MultiTenantConnectionProvider.getConnection(String tenantIdentifier) implementeren (d.w.z. de standaardimplementatie geleverd door AbstractMultiTenantConnectionProvider werkt niet).

Met postgresql kun je het doen met:

 SET search_path to <schema_name_for_tenant>;

of gebruik de alias

 SET schema <schema_name_for_tenant>;

Dus hier is wat uw getConnection(tenant_identifier); ziet eruit als:

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

Nuttige referentie is hier (officieel document)

Andere nuttige link C3POConnectionProvider.java

U kunt strategie 1 en strategie 2 combineren in uw implementatie. Je hebt alleen een manier nodig om de juiste verbindingseigenschappen/verbindings-URL voor de huidige huurder te vinden.

BEWERKEN

Ik denk dat de keuze tussen strategie 2 of 3 afhangt van het verkeer en het aantal tenants op je app. Met afzonderlijke verbindingspools:het aantal beschikbare verbindingen voor één huurder zal veel lager zijn en dus:als om een ​​legitieme reden een huurder plotseling veel verbindingen nodig heeft, zullen de prestaties van deze specifieke huurder drastisch verminderen (terwijl de andere huurder niet beïnvloed).

Aan de andere kant, met strategie 3, als om een ​​legitieme reden een huurder plotseling veel verbindingen nodig heeft:de prestaties die door elke huurder worden gezien, zullen afnemen.

Over het algemeen denk ik dat strategie 2 flexibeler en veiliger is:elke huurder kan niet meer dan een bepaalde hoeveelheid verbinding verbruiken (en dit bedrag kan per huurder worden geconfigureerd als je het nodig hebt)



  1. Dynamisch (op kolommen gebaseerd) interval

  2. MySQL-prestaties:gebruik maken van MySQL-database-indexering

  3. Hoe krijg ik gem-installatie werkend op OS X Lion met Ruby 1.8.7 zonder seg-storingen?

  4. Hoe SUBSTRING() werkt in MariaDB