從一個ConnectionPool的實現看design pattern的運用 (七) (轉)

worldblog發表於2007-12-12
從一個ConnectionPool的實現看design pattern的運用 (七) (轉)[@more@]

從一個ConnectionPool的實現看design pattern的運用 (七)


這裡是bonmot對這個Connection pool的一個意見:

pooled Connection可能由於一個client忘記關閉,而導致整個pool阻塞。所以,應該對pooled Connection進行,對於超時的或其他invaild狀態的pooled connection強制回收。

下面,讓我們來分析這個需求。

首先,這樣一個監視程式的邏輯可能是什麼樣的呢?
如果我們對超時的定義是:該連線從被分配出去到現在已經太久了。那麼,我們需要在該連線上記錄它被分配出去的時間。然後,在一個後臺執行的daemon執行緒中定期檢查這些正在使用的Connection.
而如果我們的超時又包括了對該connection使用的頻繁程度,比如說:該連線已經有兩個小時沒人動過了,(這裡,“動過”又需要定義。是隻要某個成員被了就算被“動過”了嗎?還是所有從該連線生成的物件,如Statement, ResultSet等等都算?)那我們就要過載我們該興趣的方法,紀錄該方法被呼叫的時間。

其次,一般來說,監視已分配連線和管理空閒連線之間到底有多大耦合呢?能否對它們解耦呢?經過分析,我感覺,答案是:不能。監視已分配連線的演算法理論上有可能需要知道空閒連線的一些資訊,而反之也是一樣。而且,更討厭的是,它們之間所需要的資訊量無法估計,也就是說,對一些特定的演算法,它們可能是完全的緊耦合。如果按這樣分析,這種ConnectionPool可能還得要求實現者直接實現ConnectionPool, 就象我們第三章裡使用的方法,只能偶爾使用一些utility類,象PooledConnection之類。
不過,雖然我們不能完全把監視演算法和分配演算法分開。但事實上很多監視演算法,分配演算法確實是互不相關的。比如象我們剛才分析的需求。所以我們也許可以寫一個,簡化對這些互不相關的演算法的實現。雖然對完全緊耦合的情況我們無能為力,但對多數普通的情況,我們還是可以有些作為的。而且,這樣一個框架並不影響對複雜的緊耦合情況的特殊實現。


好吧,現在就讓我們著手構建這個框架。我們的目標是:定義一個Monitor的介面,負責監視所有分配出去的連線。然後,把一個Monitor物件,一個ConnectionPooling物件組合成一個ConnectionPool.

演算法決定資料結構,首先是需要紀錄的時間資訊:
public interface Momento{
  .util.Date getTimestamp();
}
其次,我們的監視類需要知道怎樣強行回收一個Connection:
public interface Re{
  Momento getBirthMomento();
  void release();
  boolean isReleased();
}
注意,這裡,我們的ResourceProxy並不與Connection直接相關。這樣,任何的資源,只要實現了這個介面,都可以被我們的監視類所監視。

然後是監視類的介面:
public interface ResourceProxyMonitor{
  public void addResourceProxy(ResourceProxy proxy);
}
這個介面在connection被返回出ConnectionPool之前被呼叫,把分配的Connection註冊給監視類。

下面是監視類的實現:
public class SimpleResourceProxyMonitor implements ResourceProxyMonitor{
 public synchronized void addResourceProxy(ResourceProxy proxy){
 list.add(proxy);
 }
 private final java.util.List list = new java.util.LinkedList();
 private final HeartBeatEngine hb= HeartBeatEngine.instance();
 private final void releaseProxy(ResourceProxy proxy){proxy.release();}
 public final Runnable getMonitor(final long interval, final long ttl){
 return hb.getThreadProc(interval, new HeartBeat(){
 public boolean beat(){
 final java.util.Date now = new java.util.Date();
 synchronized(SimpleResourceProxyMonitor.this){
 for(java.util.Iterator it
 =list.iterator();it.hasNext();){
 final ResourceProxy proxy =
 (ResourceProxy)it.next();
 final java.util.Date then =
 proxy.getMomento().getTimestamp();
 if(now.getTime()-then.getTime()>=ttl){
 releaseProxy(proxy);
 }
 if(proxy.isReleased()){
 it.remove();
 }
 }
 }
 return true;
 } 
 });
 } 
 public final synchronized void clear(){
 turnoffMonitors();
 for(java.util.Iterator it=list.iterator();it.hasNext();){
  final ResourceProxy proxy = (ResourceProxy)it.next();
 releaseProxy(proxy); 
 } 
 list.clear();
 }
 public final synchronized void empty(){
 turnoffMonitors();
 list.clear();
 }
 public final void turnonMonitors(){
 hb.turnon();
 }
 public final void turnoffMonitors(){
 hb.turnoff();
 }
 private SimpleResourceProxyMonitor(){}
 public static SimpleResourceProxyMonitor instance(){
 return new SimpleResourceProxyMonitor();
 }
}

以及兩個輔助介面和類:HeartBeat和HeartBeatEngine, 負責daemon執行緒的睡與醒。
public interface HeartBeat{
 public boolean beat();
  true if continue, false otherwise;
}

public class HeartBeatEngine{
 public Runnable getThreadProc(final long interval, final HeartBeat r){
 return new Runnable(){
 public void run(){
 for(;isOn();){
 try{
 Thread.sleep(interval);
 }catch(InterruptedException e){}
 synchronized(HeartBeatEngine.this){
 if(!isOn())return; 
 }
 if(!r.beat())return;
 }
 }
 };
 }
 private boolean down = false; 
 private HeartBeatEngine(boolean d){
 this.down = d;
 }
 public final synchronized void turnon(){
 down = false;
 }
 public final synchronized void turnoff(){
 down = true;
 }
 private final boolean isOn(){
 return !down;
 }
 public static HeartBeatEngine instance(){
 return new HeartBeatEngine(false);
 } 
}

這裡,getMonitor()僅僅返回一個Runnable, 而不是直接啟動Thread。這樣做更加靈活。使用這個monitor類的客戶可以自由地使用這個Runnable, 比如說,使用一個執行緒池。

 

然後,我們需要一個proxy類來記錄連線被分配的時間:

public class PooledConnectionProxy implements ResourceProxy{
 public final Momento getMomento(){return momento;}
 public void release(){
 try{
 conn.close();
 }catch(Exception e){
 System.err.println(e.getMessage());
 }
 } 
  conn.close() method has to be synchronized
 public boolean isReleased(){
 try{
 return conn.isClosed();
 }catch(SQLException e){return false;} 
 }
 private final Momento momento;
 private final Connection conn;
 private PooledConnectionProxy(Momento momento, Connection conn){
 this.momento = momento;
 this.conn = conn;
 }
 public static ResourceProxy instance(Momento momento, Connection conn){
 return new PooledConnectionProxy(momento, conn);
 }
}

好了,現在我們可以把它們組裝在一起,做出一個ConnectionPool來。
還記得我們的ConnectionPooling2Pool嗎?它負責封裝ConnectionPooling並對每一個連線進行封裝。當時我們把封裝邏輯寫入了ConnectionPooling2Pool, 因為封裝邏輯只有一種。
但現在,我們有了另一種封裝邏輯。所以, refactor.
瞭解我的人應該知道,我是不會用template method pattern, 繼承ConnectionPooling2Pool然後過載wrapup函式的。用組合!

ConnectionPooler是一個代表封裝Connection的介面:
public interface ConnectionPooler{
 public Connection pool(Connection conn, ConnectionHome home)
 throws SQLException;
}
ConnectionPooling2Pool將使用ConnectionPooler進行封裝。
public class ConnectionPooling2Pool implements ConnectionPool{
  public final Connection getConnection()
  throws test.res.ResourceNotAvailableException, SQLException{
   return wrapup(pooling.getConnection());
  }
  public final Connection getConnection(long timeout)
  throws test.res.ResourceTimeOutException, SQLException{
   return wrapup(pooling.getConnection(timeout));
  }
  private final Connection wrapup(Connection conn)
  throws SQLException{
   return pl.pool(conn, pooling);
  } 
  public final void clear(){
   pooling.clear();
  }
  private final ConnectionPooling pooling;
  private final ConnectionPooler pl;
  private ConnectionPooling2Pool(ConnectionPooling pooling, ConnectionPooler pl){
   this.pooling = pooling;
   this.pl = pl;
  }
  public static ConnectionPool bridge(ConnectionPooling pooling, ConnectionPooler pl){
   return new ConnectionPooling2Pool(pooling, pl);
  }
}

原來的封裝邏輯被實現為:
public class SimpleConnectionPooler implements ConnectionPooler{
 public Connection pool(Connection conn, ConnectionHome home)
 throws SQLException{
 return PooledConnection.decorate(conn, home);
 }
 private SimpleConnectionPooler(){}
 private static final ConnectionPooler singleton = new SimpleConnectionPooler();
 public static ConnectionPooler instance(){return singleton;}
}

我們新的封裝邏輯為:
public class MonitoredConnectionPooler implements ConnectionPooler{
 public Connection pool(Connection conn, ConnectionHome home)
 throws SQLException{
   final Connection pooled = PooledConnection.decorate(conn, home);
   monitor.addResourceProxy(
   PooledConnectionProxy.instance(factory.newMomento(), pooled)
   );
   return pooled;
   }
 private final MomentoFactory factory;
 private final ResourceProxyMonitor monitor;
 private MonitoredConnectionPooler(ResourceProxyMonitor mon,
 MomentoFactory factory){
 this.monitor = mon;
 this.factory = factory;
 }
 public static ConnectionPooler instance(ResourceProxyMonitor mon,
 MomentoFactory factory){
 return new MonitoredConnectionPooler(mon, factory);
 }
}
最終的組合程式碼為:
public class TestConnectionPool{
 public static void test(String , String url, String user, String pwd)
 throws java.sql.SQLException, test.res.ResourceNotAvailableException, test.res.ResourceTimeOutException, ClassNotFoundException{
 final ConnectionPool pool = ConnectionPooling2Pool.bridge(
 ConnectionPoolingImpl.instance(
 ConnectionFactoryImpl.instance(
 driver, url, user, pwd),
 1000),
 SimpleConnectionPooler.instance()
 );
 final SimpleResourceProxyMonitor mon =
 SimpleResourceProxyMonitor .instance();

 final ConnectionPool pool2 = ConnectionPooling2Pool.bridge(
 ConnectionPoolingImpl.instance(
 ConnectionFactoryImpl.instance(
 driver, url, user, pwd),
 1000),
 MonitoredConnectionPooler.instance(
 mon, SimpleMomentoFactory.instance())
 );
 final Runnable monproc = mon.getMonitor(1000L, 1000000L);
 new Thread(monproc).start();
 }
}

 

對connection的使用頻繁程度的監視,因為演算法所要求的資料結構會有所不同,所以會需要自己的一套ResourceProxy, ResourceProxyMonitor介面以及對Connection甚至其它Connection生成物件的進行同步處理和記錄存取時間的封裝。但實現的機理是相似的。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992147/,如需轉載,請註明出處,否則將追究法律責任。

相關文章