PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Verwalten Sie Connection Pooling in mandantenfähigen Web-Apps mit Spring, Hibernate und C3P0

Sie können zwischen 3 verschiedenen Strategien wählen, die sich auf die Verbindungsabfrage auswirken. In jedem Fall müssen Sie eine Implementierung von MultiTenantConnectionProvider bereitstellen . Die von Ihnen gewählte Strategie wirkt sich natürlich auf Ihre Implementierung aus.

Allgemeine Anmerkung zu MultiTenantConnectionProvider.getAnyConnection()

getAnyConnection() wird von Hibernate benötigt, um Metadaten zu sammeln und die SessionFactory einzurichten. Normalerweise haben Sie in einer Multi-Tenant-Architektur eine spezielle/Master-Datenbank (oder ein Schema), die von keinem Tenant verwendet wird. Es ist eine Art Vorlagendatenbank (oder Schema). Es ist in Ordnung, wenn diese Methode eine Verbindung zu dieser Datenbank (oder diesem Schema) zurückgibt.

Strategie 1:Jeder Mieter hat seine eigene Datenbank. (und somit ein eigener Verbindungspool)

In diesem Fall hat jeder Mandant seinen eigenen Verbindungspool, der von C3PO verwaltet wird, und Sie können eine Implementierung von MultiTenantConnectionProvider bereitstellen basierend auf AbstractMultiTenantConnectionProvider

Jeder Mandant hat seinen eigenen C3P0ConnectionProvider , also alles, was Sie in selectConnectionProvider(tenantIdentifier) tun müssen ist, das richtige zurückzugeben. Sie können eine Map behalten, um sie zwischenzuspeichern, und Sie können einen C3POConnectionProvider mit so etwas wie :

lazy-initialisieren
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:Jeder Mandant hat sein eigenes Schema und seinen eigenen Verbindungspool in einer einzigen Datenbank

Dieser Fall ist der ersten Strategie bezüglich ConnectionProvider sehr ähnlich da Sie auch AbstractMultiTenantConnectionProvider verwenden können als Basisklasse zum Implementieren Ihres MultiTenantConnectionProvider

Die Implementierung ist der vorgeschlagenen Implementierung für Strategie 1 sehr ähnlich, außer dass Sie das Schema anstelle der Datenbank in der c3po-Konfiguration ändern müssen

Strategie 3:Jeder Mandant hat sein eigenes Schema in einer einzelnen Datenbank, verwendet aber einen gemeinsam genutzten Verbindungspool

Dieser Fall ist etwas anders, da jeder Mandant denselben Verbindungsanbieter verwendet (und somit der Verbindungspool gemeinsam genutzt wird). In dem Fall:Der Verbindungsanbieter muss das zu verwendende Schema vor jeder Verwendung der Verbindung festlegen. d.h. Sie müssen MultiTenantConnectionProvider.getConnection(String tenantIdentifier) implementieren (d. h. die von AbstractMultiTenantConnectionProvider bereitgestellte Standardimplementierung funktioniert nicht).

Mit postgresql können Sie dies mit :

tun
 SET search_path to <schema_name_for_tenant>;

oder mit dem Alias

 SET schema <schema_name_for_tenant>;

Hier ist also Ihr getConnection(tenant_identifier); sieht so aus:

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

Nützliche Referenzen finden Sie hier (offizielles Dokument)

Anderer nützlicher Link C3POConnectionProvider.java

Sie können Strategie 1 und Strategie 2 in Ihrer Implementierung kombinieren. Sie brauchen nur eine Möglichkeit, die richtigen Verbindungseigenschaften/Verbindungs-URL für den aktuellen Mandanten zu finden.

BEARBEITEN

Ich denke, dass die Wahl zwischen Strategie 2 oder 3 vom Datenverkehr und der Anzahl der Mandanten in Ihrer App abhängt. Mit getrennten Verbindungspools:Die Anzahl der für einen Mandanten verfügbaren Verbindungen ist viel geringer, und so:Wenn aus irgendeinem berechtigten Grund ein Mandant plötzlich viele Verbindungen benötigt, wird die Leistung, die dieser bestimmte Mandant sieht, drastisch abnehmen (während dies beim anderen Mandant nicht der Fall ist). betroffen).

Wenn andererseits bei Strategie 3 aus irgendeinem berechtigten Grund ein Mandant plötzlich viele Verbindungen benötigt:Die Leistung, die jeder Mandant sieht, wird abnehmen.

Im Allgemeinen denke ich, dass Strategie 2 flexibler und sicherer ist:Jeder Mandant kann nicht mehr als eine bestimmte Verbindungsmenge verbrauchen (und diese Menge kann pro Mandant konfiguriert werden, wenn Sie es brauchen)