Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


18.10.3

Connection Pooling



Connection Pooling ist eine Technik, die meist in Middleware-Komponenten wie z. B. EJB-/CORBA-Komponenten oder Servlets/JSP eingesetzt wird.

Unter Connection Pooling versteht man generell die Wiederverwendung physischer Datenbankverbindungen. Zu Middleware-Server-Komponenten verbinden sich üblicherweise in kurzen Zeitabständen verschiedene Clients. Wenn eine Serverkomponente auf eine Datenbank zugreift, muss sie für die Datenbank-Operationen eine Datenbankverbindung bereitstellen. Da der Aufbau einer Datenbankverbindung eine relativ ressourcenintensive Operation darstellt, ist es besser, eine Datenbankverbindung im Server nicht sofort zu schließen, wenn ein Client seine Arbeit beendet hat. Sie kann gespeichert und für nachfolgende Client-Zugriffe wiederverwendet werden. Connection Pooling in JDBC 2.0 soll dem Entwickler genau diese Funktionalität zur Verfügung stellen.

Beim Connection Pooling mit dem Oracle-Treiber kann man zwei Stufen unterscheiden: Eine einzelne Pooled Connection implementiert somit den generellen Mechanismus für die Wiederverwendung von Datenbankverbindungen. Der Connection Pool macht von diesem Mechanismus Gebrauch, indem mehrere dieser Verbindungen in einem Cache bereitgestellt werden. Die direkte Programmierung mit Pooled Connections stellt eine Lowlevel-Schnittstelle dar und wird in der Praxis nur für die Implementierung eines Connection Pools mit einem bestimmten Pooling-Mechanismus verwendet (z. B. bei der Implemetierung von JDBC-Treiber-Erweiterungen). Wesentlich anwendungsorientierter hingegen ist die direkte Verwendung der Highlevel-Schnittstelle eines Connection Pools für die Implementierung einer Middleware-Komponente.

Connection Pooling wird durch einen eigenen JDBC-Datenquellen-Typ unterstützt, die ConnectionPoolDataSource.

Initialisierung der Datenquelle

JDBC 2.0 definiert das Interface ConnectionPoolDataSource, das eine Schnittstelle für die Erzeugung von Pooled Connections zur Verfügung stellt. Für die Verwendung von Pooled Connections und Connection Pools muss man in der Anwendung zunächst ein Exemplar erzeugen, das dieses Interface implementiert. Oracle stellt hierzu die Klasse OracleConnectionPoolDataSource bereit:
  ...
  String url = "jdbc:oracle:thin:@localhost:1521:orcl";
  OracleConnectionPoolDataSource ds;
  ds = new OracleConnectionPoolDataSource();
  ds.setURL(url);
  ds.setUser("scott");
  ds.setPassword("tiger");
  ...
OracleConnectionPoolDataSource implementiert sowohl das Interface PooledConnection als auch DataSource. Da im Interface DataSource bereits die Methode getConnection() definiert ist, kann man über diese Klasse neue Verbindungen zum Datenbankserver erzeugen:
  Connection con = ds.getConnection();
Die hierdurch zurückgelieferten Verbindungen unterstützen jedoch noch kein Connection Pooling. Bei jedem Aufruf von getConnection() wird eine neue Verbindung zum Datenbankserver aufgebaut und beim Aufruf von close() wieder geschlossen. Das entpricht dem Verhalten einer herkömmlichen Verbindung. Connection Pooling wird erst durch die im Folgenden beschriebenen Zwischenschritte ermöglicht.

Damit der Zugriff auf den Connection Pool transparent von den zugrundeliegenden Klassen bleibt (z. B. Oracle), werden vorkonfigurierte Exemplare meist über ein JNDI-Verzeichnis zugänglich gemacht. Dabei wird beim Zugriff nicht direkt ein Exemplar der implementierenden Klasse angelegt, wie im obigen Beispiel, sondern nur über einen JNDI-Pfad ein vorkonfiguriertes Exemplar aus dem Verzeichnis abgerufen.

Pooled Connection

Pooled Connections können zur Implementierung eines Connection Pools eingesetzt werden. Sie implementieren allgemein wiederverwendbare Datenbankverbindungen. Abbildung zeigt die generelle Funktionsweise einer Pooled Connection.

Abbildung 18.24: Wiederverwendung von Verbindungen in einem Connection Cache
Abbildung 18.24

Nachdem man einen Verweis auf ein Exemplar der Klasse ConnectionPoolDataSource erhalten hat, kann man durch Aufruf von getPooledConnection() eine neue Verbindung erzeugen:

PooledConnection pc = ds.getPooledConnection("shop", "shop");
Bei diesem Aufruf wird eine neue Verbindung zur Datenbank aufgebaut und intern in der Klasse PooledConnection zwischengespeichert. Je nachdem, ob man Benutzer und Passwort bereits in der Datenquelle gesetzt hat, muss man beim Aufruf von getPooledConnection() Benutzer und Passwort übergeben (wie hier im Beispiel).

Die Klasse PooledConnection stellt ihrerseits die Methode getConnection() zur Verfügung, die ein Exemplar vom Typ Connection zurückliefert, das für die Datenbankabfrage benutzt werden kann:
  Connection con = pc.getConnection();
  // Hier Datenbankabfragen
  ...
Mit jeder Pooled Connection ist eine physische Datenbankverbindung assoziiert. Beim Aufruf von getConnection() wird ein logisches Connection-Exemplar erzeugt, das intern die physische Verbindung zugewiesen bekommt. Damit ist die physische Verbindung einem Client zugeordnet. Die logische Verbindung hat die Funktion einer Wrapperklasse und leitet intern die Aufrufe an die physische Verbindung weiter. Hat der Client seine Arbeit beendet und die Verbindung wird nicht mehr benötigt, ruft er die close()-Methode der logischen Verbindung auf. Dadurch wird allerdings nicht wirklich die physische Verbindung zur Datenbank geschlossen. Diese wird lediglich wieder freigegeben.

Man sollte beachten, dass Pooled Connections keine Synchronisations-Mechanismen besitzen. Erfolgt ein zweiter Aufruf von getConnection(), bevor die erste Verbindung mit close() geschlossen wurde, wird die physische Verbindung der gerade aktiven logischen Verbindung entzogen und der neu erzeugten Verbindung zugewiesen. Bei nachfolgenden Zugriffen auf die erste Verbindung wird eine SQL-Exception mit der Fehlermeldung »Logisches Handle nicht mehr gültig« ausgelöst.

Connection Pool

Ein Connection Pool enthält eine Menge an physischen Datenbankverbindungen zur selben Datenbank, die auf Abruf zur Ausführung von SQL-Anweisungen bereitgestellt werden. Wenn die Arbeit mit einer Verbindung beendet ist, wird sie nicht geschlossen, sondern wieder zurück in den Pool freier Verbindungen gestellt. Der große Vorteil eines Connection Pools gegenüber einer herkömmlichen Verbindung ist, dass initial einige wenige Verbindungen geöffnet und danach temporär auf Abruf einzelnen Clients zur Verfügung gestellt werden. Durch diese Technik können relativ viele Clients mit relativ wenigen Datenbankverbindungen arbeiten. Das Prinzip wird z. B. auch vom Oracle-Server im MTS-Modus eingesetzt. Der Server startet initial eine vordefinierte Anzahl an Dispatcher-Prozessen, die bei Bedarf bei Client-Zugriffen temporär einzelnen Clients für die Verarbeitung der Anfragen zur Verfügung gestellt werden. Hat der Client seine Arbeit beendet, ist der Dispatcher frei und kann die Anfrage eines anderen Clients bearbeiten.

In Abbildung 18.25 ist der Ablauf bei der Einrichtung eines Connection Pools mit dem Oracle-Treiber dargestellt.

Abbildung 18.25: Wiederverwendung von Verbindungen mit einer PooledConnection
Abbildung 18.25

Beim Oracle-JDBC-Treiber wird eine Implementierung eines Connection Pools mit der Klasse OracleConnectionCacheImpl mitgeliefert. Diese Klasse implementiert das Interface DataSource und kann deshalb nach der Initialisierung wie eine herkömmliche Datenquelle benutzt werden.

Bei der Initialisierung wird zunächst ein neues Exemplar der Klasse OracleConnectionCacheImpl erzeugt und mit den Verbindungsparametern initialisiert:
  ...
  ConnectionPoolDataSource ds;
  ds = (ConnectionPoolDataSource)ic.lookup(
          "jdbc_access://jdbc/shopds");
  OracleConnectionCacheImpl pool =
    new OracleConnectionCacheImpl(ds);
  pool.setUser("shop");
  pool.setPassword("shop");
  pool.setMaxLimit(5);
  Connection con = pool.getConnection();
  // Hier Ausführen von SQL
  // ...
Bei der Initialisierung wird in jedem Fall ein Exemplar der Klasse ConnectionPoolDataSource benötigt. Es kann entweder direkt dem Konstruktor übergeben oder später mit der Methode setConnectionPoolDataSource() gesetzt werden. Das ConnectionPoolDataSource-Exemplar dient dem Connection Pool intern als Factory-Objekt für die Erzeugung von Pooled Connections. Dem Connection Pool kann man außerdem Verbindungsinformationen sowie Einstellungen übergeben, die den Pool selbst betreffen. Im obigen Beispiel wird die generelle Verbindungsinformation (Rechnername, Portnummer, Treiber, SID) aus der übergebenen ConnectionPoolDataSource übernommen. Diese Parameter können jedoch über entsprechende Methoden auch neu gesetzt werden. Mit der Methode setMaxLimit() wird im obigen Beispiel die maximale Anzahl an physischen Verbindungen im Connection Pool gesetzt (und somit auch von Pooled Connections, da jede physische Verbindung in einer Pooled Connection gekapselt wird).

Beim Aufruf von getConnection() liefert ein Connection Pool letztendlich eine logische Verbindung zurück, die bei der Verarbeitung von Client-Anfragen für die Ausführung von SQL-Anweisungen benutzt werden kann. Wird eine logische Verbindung geschlossen, wird die physische Verbindung wieder zurück in den Pool freier Verbindungen gegeben und steht für weitere Anfragen zur Verfügung.

Da ein Connection Pool intern mehrere physische Verbindungen speichert, können gleichzeitig mehrere Verbindungen aktiv sein. Sind alle Verbindungen belegt und wird versucht, mit getConnection() eine weitere Verbindung aus dem Connection Pool zu erhalten, tritt eine Ausnahmesituation ein. Wie sie behandelt wird, kann man mit der Methode setCacheScheme() einstellen, wie folgendes Beispiel zeigt:
  OracleConnectionCacheImpl pool = new OracleConnectionCacheImpl(ds);
  pool.setCacheScheme(OracleConnectionCacheImpl.FIXED_WAIT_SCHEME);
Diese Methode erhält als Parameter eine Konstante übergeben, die stellvertretend für eine der drei Verarbeitungs-Varianten eingesetzt wird. Die Konstanten sind alle in der Klasse OracleConnectionCacheImpl definiert und werden im Folgenden aufgelistet: Betrachtet wird jeweils der Fall, dass bereits die maximale Anzahl an Verbindungen im Connection Pool belegt ist und versucht wird, durch Aufruf von getConnection() eine weitere Verbindung zu bekommen.

Mit der Methode setMinLimit() kann man außerdem eine minimale Verbindungsanzahl im Pool spezifizieren. Die Anzahl der aktuell geöffneten Verbindungen im Pool befindet sich immer zwischen den Werten, die durch setMaxLimit() und setMinLimit() zugewiesen wurden. Nach der Initialisierung beträgt bei der Oracle-Implementierung die minimale Anzahl an Verbindungen 0, die maximale Anzahl 10. Je nachdem, wie häufig Verbindungen aus dem Pool angefordert werden, können zeitweise mehr oder weniger Verbindungen aktiv sein. Die Anzahl der gerade aktiven Verbindungen aus dem Pool kann man mit der Methode getActiveSize(), die aktuelle Pool-Größe mit getCacheSize() ermitteln.


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.