数据库连接池的实现

Java程序员都很羡慕Windows ADO ,只需要new Connection 就可以直接从数据库连接池中返回Connection。并且 ADO Connection 是线程安全的,多个线程可以共用一个Connection,所以ASP程序一般都把getConnection 放在 Global.asa 文件中,在 IIS 启动时建立数据库连接。ADO 的Connection 和Result 都有很好的缓冲,并且很容易使用。

其实我们可以自己写一个JDBC数据库连接池。

写JDBC connection pool 的注意事项有:

1. 有一个简单的函数从连接池中得到一个 Connection。

2. close 函数必须将connection 放回 数据库连接池。

3. 当数据库连接池中没有空闲的connection,数据库连接池必须能够自动增加connection 个数。

4. 当数据库连接池中的connection 个数在某一个特别的时间变得很大,但是以后很长时间只用其中一小部分,应该可以自动将多余的connection 关闭掉。

5. 如果可能,应该提供debug 信息报告没有关闭的new Connection 。

如果要new Connection 就可以直接从数据库连接池中返回Connection, 可以这样写( Mediator pattern ) (以下代码中使用了中文全角空格):

  1. public class EasyConnection implements java.sql.Connection{
  2. private Connection m_delegate = null;
  3. public EasyConnection(){
  4. m_delegate = getConnectionFromPool();
  5. }
  6.  public void close(){
  7. putConnectionBackToPool(m_delegate);
  8. }
  9. public PreparedStatement prepareStatement(String sql) throws SQLException{
  10. m_delegate.prepareStatement(sql);
  11. }
  12. //...... other method
  13. }

看来并不难。不过不建议这种写法,因为应该尽量避免使用Java Interface, 关于Java Interface 的缺点我另外再写文章讨论。大家关注的是Connection Pool 的实现方法。下面给出一种实现方法。

  1. import java.sql.*;
  2. import java.lang.reflect.*;
  3. import java.util.*;
  4. import java.io.*;
  5. publicclass SimpleConnetionPool {
  6. privatestatic LinkedList m_notUsedConnection = new LinkedList();
  7. privatestatic HashSet m_usedUsedConnection = new HashSet();
  8. privatestatic String m_url = "";
  9. privatestatic String m_user = "";
  10. privatestatic String m_password = "";
  11. staticfinalboolean DEBUG = true;
  12. staticprivatelong m_lastClearClosedConnection = System.currentTimeMillis();
  13. publicstaticlong CHECK_CLOSED_CONNECTION_TIME = 4 * 60 * 60 * 1000; //4 hours
  14. static {
  15. initDriver();
  16. }
  17. private SimpleConnetionPool() {
  18. }
  19. privatestaticvoid initDriver() {
  20. Driver driver = null;
  21. //load mysql driver
  22. try {
  23. driver = (Driver) Class.forName("com.mysql.jdbc.Driver").newInstance();
  24. installDriver(driver);
  25. } catch (Exception e) {
  26. }
  27. //load postgresql driver
  28. try {
  29. driver = (Driver) Class.forName("org.postgresql.Driver").newInstance();
  30. installDriver(driver);
  31. } catch (Exception e) {
  32. }
  33. }
  34. publicstaticvoid installDriver(Driver driver) {
  35. try {
  36. DriverManager.registerDriver(driver);
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. publicstaticsynchronized Connection getConnection() {
  42. clearClosedConnection();
  43. while (m_notUsedConnection.size() > 0) {
  44. try {
  45. ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
  46. if (wrapper.connection.isClosed()) {
  47. continue;
  48. }
  49. m_usedUsedConnection.add(wrapper);
  50. if (DEBUG) {
  51. wrapper.debugInfo = new Throwable("Connection initial statement");
  52. }
  53. return wrapper.connection;
  54. } catch (Exception e) {
  55. }
  56. }
  57. int newCount = getIncreasingConnectionCount();
  58. LinkedList list = new LinkedList();
  59. ConnectionWrapper wrapper = null;
  60. for (int i = 0; i < newCount; i++) {
  61. wrapper = getNewConnection();
  62. if (wrapper != null) {
  63. list.add(wrapper);
  64. }
  65. }
  66. if (list.size() == 0) {
  67. returnnull;
  68. }
  69. wrapper = (ConnectionWrapper) list.removeFirst();
  70. m_usedUsedConnection.add(wrapper);
  71. m_notUsedConnection.addAll(list);
  72. list.clear();
  73. return wrapper.connection;
  74. }
  75. privatestatic ConnectionWrapper getNewConnection() {
  76. try {
  77. Connection con = DriverManager.getConnection(m_url, m_user, m_password);
  78. ConnectionWrapper wrapper = new ConnectionWrapper(con);
  79. return wrapper;
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. }
  83. returnnull;
  84. }
  85. staticsynchronizedvoid pushConnectionBackToPool(ConnectionWrapper con) {
  86. boolean exist = m_usedUsedConnection.remove(con);
  87. if (exist) {
  88. m_notUsedConnection.addLast(con);
  89. }
  90. }
  91. publicstaticint close() {
  92. int count = 0;
  93. Iterator iterator = m_notUsedConnection.iterator();
  94. while (iterator.hasNext()) {
  95. try {
  96. ( (ConnectionWrapper) iterator.next()).close();
  97. count++;
  98. } catch (Exception e) {
  99. }
  100. }
  101. m_notUsedConnection.clear();
  102. iterator = m_usedUsedConnection.iterator();
  103. while (iterator.hasNext()) {
  104. try {
  105. ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
  106. wrapper.close();
  107. if (DEBUG) {
  108. wrapper.debugInfo.printStackTrace();
  109. }
  110. count++;
  111. } catch (Exception e) {
  112. }
  113. }
  114. m_usedUsedConnection.clear();
  115. return count;
  116. }
  117. privatestaticvoid clearClosedConnection() {
  118. long time = System.currentTimeMillis();
  119. //sometimes user change system time,just return
  120. if (time < m_lastClearClosedConnection) {
  121. time = m_lastClearClosedConnection;
  122. return;
  123. }
  124. //no need check very often
  125. if (time - m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) {
  126. return;
  127. }
  128. m_lastClearClosedConnection = time;
  129. //begin check
  130. Iterator iterator = m_notUsedConnection.iterator();
  131. while (iterator.hasNext()) {
  132. ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
  133. try {
  134. if (wrapper.connection.isClosed()) {
  135. iterator.remove();
  136. }
  137. } catch (Exception e) {
  138. iterator.remove();
  139. if (DEBUG) {
  140. System.out.println("connection is closed, this connection initial StackTrace");
  141. wrapper.debugInfo.printStackTrace();
  142. }
  143. }
  144. }
  145. //make connection pool size smaller if too big
  146. int decrease = getDecreasingConnectionCount();
  147. if (m_notUsedConnection.size() < decrease) {
  148. return;
  149. }
  150. while (decrease-- > 0) {
  151. ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
  152. try {
  153. wrapper.connection.close();
  154. } catch (Exception e) {
  155. }
  156. }
  157. }
  158. /**
  159. * get increasing connection count, not just add 1 connection
  160. * @return count
  161. */
  162. publicstaticint getIncreasingConnectionCount() {
  163. int count = 1;
  164. int current = getConnectionCount();
  165. count = current / 4;
  166. if (count < 1) {
  167. count = 1;
  168. }
  169. return count;
  170. }
  171. /**
  172. * get decreasing connection count, not just remove 1 connection
  173. * @return count
  174. */
  175. publicstaticint getDecreasingConnectionCount() {
  176. int count = 0;
  177. int current = getConnectionCount();
  178. if (current < 10) {
  179. return0;
  180. }
  181. return current / 3;
  182. }
  183. publicsynchronizedstaticvoid printDebugMsg() {
  184. printDebugMsg(System.out);
  185. }
  186. publicsynchronizedstaticvoid printDebugMsg(PrintStream out) {
  187. if (DEBUG == false) {
  188. return;
  189. }
  190. StringBuffer msg = new StringBuffer();
  191. msg.append("debug message in " + SimpleConnetionPool.class.getName());
  192. msg.append("\r\n");
  193. msg.append("total count is connection pool: " + getConnectionCount());
  194. msg.append("\r\n");
  195. msg.append("not used connection count: " + getNotUsedConnectionCount());
  196. msg.append("\r\n");
  197. msg.append("used connection, count: " + getUsedConnectionCount());
  198. out.println(msg);
  199. Iterator iterator = m_usedUsedConnection.iterator();
  200. while (iterator.hasNext()) {
  201. ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
  202. wrapper.debugInfo.printStackTrace(out);
  203. }
  204. out.println();
  205. }
  206. publicstaticsynchronizedint getNotUsedConnectionCount() {
  207. return m_notUsedConnection.size();
  208. }
  209. publicstaticsynchronizedint getUsedConnectionCount() {
  210. return m_usedUsedConnection.size();
  211. }
  212. publicstaticsynchronizedint getConnectionCount() {
  213. return m_notUsedConnection.size() + m_usedUsedConnection.size();
  214. }
  215. publicstatic String getUrl() {
  216. return m_url;
  217. }
  218. publicstaticvoid setUrl(String url) {
  219. if (url == null) {
  220. return;
  221. }
  222. m_url = url.trim();
  223. }
  224. publicstatic String getUser() {
  225. return m_user;
  226. }
  227. publicstaticvoid setUser(String user) {
  228. if (user == null) {
  229. return;
  230. }
  231. m_user = user.trim();
  232. }
  233. publicstatic String getPassword() {
  234. return m_password;
  235. }
  236. publicstaticvoid setPassword(String password) {
  237. if (password == null) {
  238. return;
  239. }
  240. m_password = password.trim();
  241. }
  242. }
  243. class ConnectionWrapper implements InvocationHandler {
  244. privatefinalstatic String CLOSE_METHOD_NAME = "close";
  245. public Connection connection = null;
  246. private Connection m_originConnection = null;
  247. publiclong lastAccessTime = System.currentTimeMillis();
  248. Throwable debugInfo = new Throwable("Connection initial statement");
  249. ConnectionWrapper(Connection con) {
  250. this.connection = (Connection) Proxy.newProxyInstance(
  251. con.getClass().getClassLoader(),
  252. con.getClass().getInterfaces(), this);
  253. m_originConnection = con;
  254. }
  255. void close() throws SQLException {
  256. m_originConnection.close();
  257. }
  258. public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
  259. Object obj = null;
  260. if (CLOSE_METHOD_NAME.equals(m.getName())) {
  261. SimpleConnetionPool.pushConnectionBackToPool(this);
  262. }
  263. else {
  264. obj = m.invoke(m_originConnection, args);
  265. }
  266. lastAccessTime = System.currentTimeMillis();
  267. return obj;
  268. }
  269. }

使用方法

  1. public class TestConnectionPool{
  2. public static void main(String[] args) {
  3. SimpleConnetionPool.setUrl(DBTools.getDatabaseUrl());
  4. SimpleConnetionPool.setUser(DBTools.getDatabaseUserName());
  5. SimpleConnetionPool.setPassword(DBTools.getDatabasePassword());
  6. Connection con = SimpleConnetionPool.getConnection();
  7. Connection con1 = SimpleConnetionPool.getConnection();
  8. Connection con2 = SimpleConnetionPool.getConnection();
  9. //do something with con ...
  10. try {
  11. con.close();
  12. } catch (Exception e) {}
  13. try {
  14. con1.close();
  15. } catch (Exception e) {}
  16. try {
  17. con2.close();
  18. } catch (Exception e) {}
  19. con = SimpleConnetionPool.getConnection();
  20. con1 = SimpleConnetionPool.getConnection();
  21. try {
  22. con1.close();
  23. } catch (Exception e) {}
  24. con2 = SimpleConnetionPool.getConnection();
  25. SimpleConnetionPool.printDebugMsg();
  26. }
  27. }

运行测试程序后打印JDBC数据库连接池中Connection状态,以及正在使用的没有关闭Connection信息。

posted @ 2013-03-01 12:06  web开发初级  阅读(138)  评论(0编辑  收藏  举报