JBOSS连接池4-连接获取及返还和销毁

https://blog.csdn.net/miklechun/article/details/38924437

本节介绍JBOSS的两个最重要的方法:getconnection(获取连接)和returnConnection(释放连接)。最后,补充介绍一下JBOSS中对于异常连接是如何销毁的。

当应用需要进行业务处理时,首先会执行一个getConnection的操作,用于从连接池中获取连接,当业务处理完成后,需要把连接放回到连接池中,执行一个returnConnection的操作。
下面先看一下getConnection的源码:

 
  1. //getConnection方法返回的值是一个连接监听对象ConnectionListener

  2. public ConnectionListener getConnection(Subject subject, ConnectionRequestInfo cri)

  3. throws ResourceException

  4. {

  1. subject = (subject == null) ? defaultSubject : subject;

  2. //获取连接信息

  3. cri = (cri == null) ? defaultCri : cri;

  4. //打印startWait,即当前时间,精确到毫秒

  5. long startWait = System.currentTimeMillis();

  6. try

  7. {

  8. /*等待最多xx毫秒获取一个信号量(permit),即permit操作。

  9. * permit操作用于获取当前可用的信号量,即是否有可以使用的信号量。

  10. 因此,因为在创建连接池的时候,我们也创建了一个max值的信号集,

  11. 所以,对于连接池中的连接数未达到max值的时候,肯定有可以使用的信号量。

  12. 除非,所有的连接都已经在使用状态,并且连接数已经达到max值,

  13. 这时,才有可能出现没有信号量,并出现超时的情况。

  14. */

  15. if (permits.attempt(poolParams.blockingTimeout))

  16. {

  17. //计算本次获取连接的阻塞时间=当前时间-开始获取信号量的时间

  18. long poolBlockTime = System.currentTimeMillis() - startWait ;

  19. //累加阻塞时间。

  20. connectionCounter.updateBlockTime(poolBlockTime);

  21. //我们有一个权限去获取一个连接,判断是否在连接池中已经有一个可用的连接?

  22. ConnectionListener cl = null;

  23. do

  24. {

  25. //线程安全,即从连接池中获取连接是一个串行操作

  26. synchronized (cls)

  27. {

  28. //判断连接池是否已经被shutdown,如果被shutdown,

  29. //则抛出异常"The pool has been shutdown",并释放信号量

  30. if (shutdown.get())

  31. {

  32. permits.release();

  33. throw new ResourceException("The pool has been shutdown");

  34. }

  35. //如果可用的连接事件监听的arraylist大于0,则从arraylist的尾部取一个连接

  36. if (cls.size() > 0)

  37. {

  38. cl = (ConnectionListener) cls.remove(cls.size() - 1);

  39. //将arraylist中获取的连接加到到checkdout的一个hash结构中.

  40. checkedOut.add(cl);

  41. //计算已经使用的连接数

  42. int size = (int) (maxSize - permits.permits());

  43.  
  44. //更新当前已经使用的最大连接数

  45. if (size > maxUsedConnections)

  46. maxUsedConnections = size;

  47. }

  48. }

  49. //如果已经从连接事件监听数组中获取到了连接

  50. if (cl != null)

  51. {

  52. //我们从一个pool取一个ManagedConnection,并检查它是否符合要求?

  53. try

  54. {

  55. Object matchedMC = mcf.matchManagedConnections

  56. (Collections.singleton(cl.getManagedConnection())

  57. ,subject, cri);

  58. if (matchedMC != null)

  59. {

  60. if (trace)

  61. log.trace("supplying ManagedConnection from pool: " + cl);

  62. //通知connection listener是否它拥有权限

  63. cl.grantPermit(true);

  64. //返回连接,结束

  65. return cl;

  66. }

  67.  
  68. /*

  69. * 匹配不成功,并且没有异常信息抛出。

  70. * 在检查的时候,要么我们匹配错误,要么连接已经死亡

  71. 我们需要去辨别这些场景,但是现在,不管怎么样,我们都销毁连接。

  72. */

  73. log.warn("Destroying connection that could not be

  74. successfully matched: " + cl);

  75. synchronized (cls)

  76. {

  77. //从checkout的hashset中删除已经获取的连接。

  78. checkedOut.remove(cl);

  79. }

  80. //销毁连接

  81. doDestroy(cl);

  82. cl = null;

  83.  
  84. }

  85. //不管发生任何事,都销毁连接

  86. catch (Throwable t)

  87. {

  88. log.warn("Throwable while trying to match ManagedConnection,

  89. destroying connection: " + cl, t);

  90. synchronized (cls)

  91. {

  92. checkedOut.remove(cl);

  93. }

  94. doDestroy(cl);

  95. cl = null;

  96.  
  97. }

  98. //如果发生意外,我们应该决定是否应该继续尝试去建立连接,

  99. //由jboss配置文件中的的useFastFail参数来决定,默认为false。

  100. //这个useFastFail设置为true,则立刻跳出get connection,并报错。

  101. if(poolParams.useFastFail)

  102. {

  103. log.trace("Fast failing for connection attempt.

  104. No more attempts will be made to acquire connection from pool

  105. and a new connection will be created immeadiately");

  106. break;

  107. }

  108.  
  109. }

  110. }

  111. //当连接监听队列>0,即还有可用的连接监听器

  112. while (cls.size() > 0);//end of do loop

  113.  
  114. //OK, 我们不能够找到一个可以使用的连接,则新建一个

  115. try

  116. {

  117. //创建一个新的连接。这里不需要判断是否已经到达max连接数

  118. //因为前面已经获取了信号量,所以肯定可以创建连接。

  119. cl = createConnectionEventListener(subject, cri);

  120. synchronized (cls)

  121. { //将创建的连接加入到checkout数组中。

  122. checkedOut.add(cl);

  123. int size = (int) (maxSize - permits.permits());

  124. //更新当前已经使用的最大连接数

  125. if (size > maxUsedConnections)

  126. maxUsedConnections = size;

  127. }

  128.  
  129. //如果连接池还没有启动,则初始化连接池,并设置值started为true。

  130. //这里连接池可能被启用多次(因为非线程安全),但是这里没有危害。

  131. if (started == false)

  132. {

  133.  
  134. started = true;

  135. if (poolParams.minSize > 0)

  136. PoolFiller.fillPool(this);

  137. }

  138. if (trace)

  139. log.trace("supplying new ManagedConnection: " + cl);

  140. //通知connection listener是否它拥有权限

  141. cl.grantPermit(true);

  142. return cl;

  143. }

  144. catch (Throwable t)

  145. {

  146. log.warn("Throwable while attempting to get a new connection: " + cl, t);

  147. //return permit and rethrow

  148. synchronized (cls)

  149. {

  150. checkedOut.remove(cl);

  151. }

  152. permits.release();

  153. JBossResourceException.rethrowAsResourceException(

  154. "Unexpected throwable while trying to create a connection: " + cl, t);

  155. throw new UnreachableStatementException();

  156. }

  157. }

  158. //这里的else操作,不能获取信号量,则抛出异常,报错连接池超时。

  159. else

  160. {

  161. // we timed out

  162. throw new ResourceException("No ManagedConnections available

  163. within configured blocking timeout ( "

  164. + poolParams.blockingTimeout + " [ms] )");

  165. }

  166.  
  167. }

  168. catch (InterruptedException ie)

  169. {

  170. long end = System.currentTimeMillis() - startWait;

  171. connectionCounter.updateBlockTime(end);

  172. throw new ResourceException("Interrupted while requesting permit!

  173. Waited " + end + " ms");

  174. }

  175. }

执行过程流程图如下:

关于getConnetion的几点说明
1.blockingTimeout是一个jboss的参数:< blocking-timeout-millis >5000< /blocking-timeout-millis >,它是一个获取信号量的超时时间,更确切的说,是从连接池中获取一个连接的超时时间。如果超过5000ms,不能够获取到信号量(连接),则jboss会抛出异常: “can not get connection,No ManagedConnections available within configured blocking timeout [xx] ms”。当然,只要当前正在使用的连接数没有到达MAX值,这个信号量一定能够被获取到。因为信号量一共有MAX值个,如果连接池中当前的连接不够用时,在获取信号量之后,新创建一个连接即可。建议可以设置成可以设成500ms左右,不需要设计过大,当应用中存在多个数据源时,可以防止因DB异常线程池带来的阻塞。如果网络环境不好的话,可以设置的更高一点。

2.连接池内部就是一个连接监听队列,每次都从队列的尾部获取连接。而IdleRemove线程,则是从这个队列头部开始进行清理。

3.连接池的获取,销毁,创建,这三个操作都是线程安全的操作。

4.业务在使用连接的过程中,会一直占有这个信号量,在returnConnection或者发生异常时释放信号量。能够获取信号量,则意味着肯定可以获取到连接。第二节中,我们讲到JBOSS在启动时会初始化一个信号量数组,长度为连接池的max参数。

当业务系统使用完连接后,需要把连接放回到连接池中它的主要见下图,源代码如下:

 
  1. public void returnConnection(ConnectionListener cl, boolean kill) {

  2. synchronized (cls) {

  3. /*

  4. * 判断连接是否已经被DESTROYED?

  5. * 可能有其它的线程如background-validation及shuwdown

  6. * 标记这个连接临听器为DESTORYED状态。

  7. *

  8. */

  9. if (cl.getState() == ConnectionListener.DESTROYED) {

  10. if (trace)

  11. log

  12. .trace("ManagedConnection is being returned after it was destroyed"

  13. + cl);

  14. //释放信号量,并直接返回

  15. if (cl.hasPermit()) {

  16. // release semaphore

  17. cl.grantPermit(false);

  18. permits.release();

  19. }

  20.  
  21. return;

  22. }

  23. }

  24.  
  25. if (trace)

  26. log.trace("putting ManagedConnection back into pool kill=" + kill

  27. + " cl=" + cl);

  28. try {

  29. //前台应用强制清理连接。

  30. cl.getManagedConnection().cleanup();

  31. } catch (ResourceException re) {

  32. log.warn("ResourceException cleaning up ManagedConnection: " + cl,

  33. re);

  34. //清理失败,抛出异常,清理失败。

  35. kill = true;

  36. }

  37.  
  38. synchronized (cls) {

  39. // 连接监听的状态为DESTROY或者DESTROYED,则设置kill为true

  40. if (cl.getState() == ConnectionListener.DESTROY

  41. || cl.getState() == ConnectionListener.DESTROYED)

  42. kill = true;

  43. //checkedOut队列中移除连接监听器。

  44. checkedOut.remove(cl);

  45.  
  46. //如果kill==false,并且连接数>=最大连接max值,说明异常发生,再次设置kill=true

  47. if (kill == false && cls.size() >= poolParams.maxSize) {

  48. log

  49. .warn("Destroying returned connection, maximum pool size exceeded "

  50. + cl);

  51. kill = true;

  52. }

  53.  
  54. //kill连接

  55. if (kill) {

  56. // Adrian Brock: A resource adapter can asynchronously notify us

  57. // that

  58. // a connection error occurred.

  59. // This could happen while the connection is not checked out.

  60. // e.g. JMS can do this via an ExceptionListener on the

  61. // connection.

  62. // I have twice had to reinstate this line of code, PLEASE DO

  63. // NOT REMOVE IT!

  64. cls.remove(cl);

  65. }

  66. //如果kill==false

  67. else {

  68. cl.used();

  69. //这个连接监听不属于连接监听队列,则加入。

  70. if (cls.contains(cl) == false)

  71. cls.add(cl);

  72. else

  73. log.warn("Attempt to return connection twice (ignored): "

  74. + cl, new Throwable("STACKTRACE"));

  75. }

  76.  
  77. if (cl.hasPermit()) {

  78. //释放信号量

  79. cl.grantPermit(false);

  80. permits.release();

  81. }

  82. }

  83.  
  84. if (kill) {

  85. if (trace)

  86. log.trace("Destroying returned connection " + cl);

  87. //销毁连接。

  88. doDestroy(cl);

  89. }

  90. }

执行过程流程图如下:

ReturnConnetion总结:
1.释放连接也是一个线程安全的操作。
2.在连接return时,有可能已经是destory的状态(前面第三节中讲到的SHUTDOWN操作,会对连接打上DESTORY的标记),这时,直接进行remove即可。
3.释放的连接若不属于连接监听队列(连接池),即加入到连接监听队列中(即连接池中)。
4.释放连接需要释放信号量。
5.在释放过程中,出现任何异常,则将连接从连接池中移除,并进行强制销毁。

JBOSS对于异常连接的处理:
默认情况下,JBOSS不会对无效的连接进行销毁。
如果我们需要对异常列表中的连接进行销毁,则需要在连接池的ds.xml中添加以下配置:

 
  1. < exception-sorter-class-name>

  2. org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter

  3. < /exception-sorter-class-name>

这个是ORACLE的异常列表,可以查看这个类里面定义了ORACLE的异常列表,当连接抛出这个列表中的错误时,即会进行销毁,如不能销毁,异常连接会一直存在连接池中。ORACLE异常列表如下:

 
  1. public boolean isExceptionFatal(final SQLException e)

  2. {

  3. // I can't remember if the errors are negative or positive.

  4. final int error_code = Math.abs( e.getErrorCode() );

  5.  
  6. if( ( error_code == 28 ) //session has been killed

  7. || ( error_code == 600 ) //Internal oracle error

  8. || ( error_code == 1012 ) //not logged on

  9. || ( error_code == 1014 ) //Oracle shutdown in progress

  10. || ( error_code == 1033 ) //Oracle initialization or shutdown in progress

  11. || ( error_code == 1034 ) //Oracle not available

  12. || ( error_code == 1035 ) //ORACLE only available to users with RESTRICTED SESSION privilege

  13. || ( error_code == 1089 ) //immediate shutdown in progress - no operations are permitted

  14. || ( error_code == 1090 ) //shutdown in progress - connection is not permitted

  15. || ( error_code == 1092 ) //ORACLE instance terminated. Disconnection forced

  16. || ( error_code == 1094 ) //ALTER DATABASE CLOSE in progress. Connections not permitted

  17. || ( error_code == 2396 ) //exceeded maximum idle time, please connect again

  18. || ( error_code == 3106 ) //fatal two-task communication protocol error

  19. || ( error_code == 3111 ) //break received on communication channel

  20. || ( error_code == 3113 ) //end-of-file on communication channel

  21. || ( error_code == 3114 ) //not connected to ORACLE

  22. || ( error_code >= 12100 && error_code = 21000 ) &&

  23. ( (error_text.indexOf("SOCKET") > -1) //for control socket error

  24. || (error_text.indexOf("CONNECTION HAS ALREADY BEEN CLOSED") > -1)

  25. || (error_text.indexOf("BROKEN PIPE") > -1) ) )

  26. {

  27. return true;

  28. }

  29.  
  30. return false;

  31. }

类似的,我们也可以找到一个MYSQL的异常列表, org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter:

 
  1. public boolean isExceptionFatal(SQLException e)

  2. {

  3. if (e.getSQLState() != null)

  4. { // per Mark Matthews at MySQL

  5. if (e.getSQLState().startsWith("08"))

  6. {

  7. return true;

  8. }

  9. }

  10. switch (e.getErrorCode())

  11. {

  12. // Communications Errors

  13. case 1040: // ER_CON_COUNT_ERROR

  14. case 1042: // ER_BAD_HOST_ERROR

  15. case 1043: // ER_HANDSHAKE_ERROR

  16. case 1047: // ER_UNKNOWN_COM_ERROR

  17. case 1081: // ER_IPSOCK_ERROR

  18. case 1129: // ER_HOST_IS_BLOCKED

  19. case 1130: // ER_HOST_NOT_PRIVILEGED

  20.  
  21. // Authentication Errors

  22. case 1045: // ER_ACCESS_DENIED_ERROR

  23.  
  24. // Resource errors

  25. case 1004: // ER_CANT_CREATE_FILE

  26. case 1005: // ER_CANT_CREATE_TABLE

  27. case 1015: // ER_CANT_LOCK

  28. case 1021: // ER_DISK_FULL

  29. case 1041: // ER_OUT_OF_RESOURCES

  30.  
  31. // Out-of-memory errors

  32. case 1037: // ER_OUTOFMEMORY

  33. case 1038: // ER_OUT_OF_SORTMEMORY

  34.  
  35. return true;

  36. }

  37.  
  38. return false;

  39. }

还有各种数据库的异常列表,都可以在自行配置,都定义在这个包下: org.jboss.resource.adapter.jdbc.vendor。

猜你喜欢

转载自blog.csdn.net/gangsijay888/article/details/89177314