JBOSS接続プール4-接続取得および返却と破棄

14542 ワード

このセクションでは、getconnection(接続の取得)とreturnConnection(接続の解放)の2つの最も重要な方法について説明します.最後に、JBOSSで異常接続がどのように破棄されたかを補足します.
アプリケーションがビジネス処理を行う必要がある場合、まずgetConnectionの操作を実行し、接続プールから接続を取得し、ビジネス処理が完了した後、接続プールに接続を戻し、returnConnectionの操作を実行する必要があります.次にgetConnectionのソースコードを見てみましょう.
 //getConnection               ConnectionListener
public ConnectionListener getConnection(Subject subject, ConnectionRequestInfo cri)
               throws ResourceException
   {

      subject = (subject == null) ? defaultSubject : subject;
      //      
      cri = (cri == null) ? defaultCri : cri;
      //  startWait,     ,     
      long startWait = System.currentTimeMillis();
      try
      {
        /*    xx         (permit), permit  。
         * permit              ,            。
            ,           ,        max     ,
            ,             max    ,           。
             ,             ,         max ,
             ,           ,        。
         */
         if (permits.attempt(poolParams.blockingTimeout))
         {
          //             =    -          
            long poolBlockTime =  System.currentTimeMillis() - startWait ;
            //      。
            connectionCounter.updateBlockTime(poolBlockTime);
            //              ,                   ?
            ConnectionListener cl = null;
            do
            {
              //    ,                 
               synchronized (cls)
               {
                 //          shutdown,   shutdown,
                 //     "The pool has been shutdown",      
                  if (shutdown.get())
                  {
                     permits.release();
                     throw new ResourceException("The pool has been shutdown");
                  }
                  //            arraylist  0,  arraylist        
                  if (cls.size() > 0)
                  {
                     cl = (ConnectionListener) cls.remove(cls.size() - 1);
                     // arraylist         checkdout   hash   .
                     checkedOut.add(cl);
                     //          
                     int size = (int) (maxSize - permits.permits());

                     //              
                     if (size > maxUsedConnections)
                        maxUsedConnections = size;
                  }
               }
               //                    
               if (cl != null)
               {
                //     pool   ManagedConnection,          ?
                  try
                  {
                     Object matchedMC = mcf.matchManagedConnections
                     (Collections.singleton(cl.getManagedConnection())
                        ,subject, cri);
                     if (matchedMC != null)
                     {
                        if (trace)
                         log.trace("supplying ManagedConnection from pool: " + cl);
                        //  connection listener       
                        cl.grantPermit(true);
                        //    ,  
                        return cl;
                     }

                     /*
                      *      ,          。
                      *       ,        ,        
                                  ,    ,     ,       。
                      */
                     log.warn("Destroying connection that could not be
                     successfully matched: " + cl);
                     synchronized (cls)
                     {
                      // checkout hashset          。
                        checkedOut.remove(cl);
                     }
                     //    
                     doDestroy(cl);
                     cl = null;

                  }
                  //       ,     
                  catch (Throwable t)
                  {
                     log.warn("Throwable while trying to match ManagedConnection,
                           destroying connection: " + cl, t);
                     synchronized (cls)
                     {
                        checkedOut.remove(cl);
                     }
                     doDestroy(cl);
                     cl = null;

                  }
                  //      ,                   ,
                  // jboss       useFastFail     ,   false。
                  //  useFastFail   true,     get connection,   。
                  if(poolParams.useFastFail)
                  {
                     log.trace("Fast failing for connection attempt.
                    No more attempts will be made to acquire connection from pool
                     and a new connection will be created immeadiately");
                     break;
                  }

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

            //OK,                 ,     
            try
            {
               //        。             max   
              //            ,          。
               cl = createConnectionEventListener(subject, cri);
               synchronized (cls)
               {  //         checkout   。
                  checkedOut.add(cl);
                  int size = (int) (maxSize - permits.permits());
                //              
                  if (size > maxUsedConnections)
                     maxUsedConnections = size;
               }

               //          ,       ,    started true。
               //            (       ),        。
               if (started == false)
               {

                  started = true;
                  if (poolParams.minSize > 0)
                     PoolFiller.fillPool(this);
               }
               if (trace)
                log.trace("supplying new ManagedConnection: " + cl);
               //  connection listener       
               cl.grantPermit(true);
               return cl;
            }
            catch (Throwable t)
            {
           log.warn("Throwable while attempting to get a new connection: " + cl, t);
               //return permit and rethrow
               synchronized (cls)
               {
                  checkedOut.remove(cl);
               }
               permits.release();
               JBossResourceException.rethrowAsResourceException(
             "Unexpected throwable while trying to create a connection: " + cl, t);
               throw new UnreachableStatementException();
            }
         }
         //   else  ,       ,     ,       。
         else
         {
            // we timed out
            throw new ResourceException("No ManagedConnections available
              within configured blocking timeout ( "
                 + poolParams.blockingTimeout + " [ms] )");
         }

      }
      catch (InterruptedException ie)
      {
         long end = System.currentTimeMillis() - startWait;
       connectionCounter.updateBlockTime(end);
         throw new ResourceException("Interrupted while requesting permit!
         Waited " + end + " ms");
      }
   }

実行プロセスフローチャートは次のとおりです.
getConnetionについてのいくつかの説明1.blockingTimeoutはjbossのパラメータ:5000であり、信号量を取得するタイムアウト時間であり、より正確には接続プールから接続を取得するタイムアウト時間である.5000 msを超えると信号量(接続)が取得できない場合、jbossは「can not get connection,No ManagedConnections available within configured blocking timeout[xx]ms」という異常を投げ出す.もちろん、現在使用されている接続数がMAX値に達していない限り、この信号量は必ず取得できる.信号量は全部でMAXの値があるので、接続プールの現在の接続が足りない場合は、信号量を取得した後、新しい接続を作成すればよい.500 ms程度に設定できるようにすることが推奨され、設計が大きすぎる必要がなく、アプリケーションに複数のデータソースが存在する場合、DB異常スレッドプールによるブロックを防止することができる.ネットワーク環境が悪ければ、もっと高く設定できます.
2.接続プールの内部には、接続傍受キューがあり、毎回キューの末尾から接続を取得します.IdleRemoveスレッドは、このキューヘッダからクリーンアップされます.
3.接続プールの取得、破棄、作成の3つの操作は、スレッドの安全な操作です.
4.トラフィックは、接続を使用する間、この信号量を常に占有し、returnConnectionまたは異常が発生した場合に信号量を解放します.信号量を取得できることは、接続が必ず取得できることを意味する.第2節では,JBOSSは起動時に信号量配列を初期化し,長さは接続プールのmaxパラメータである.
ビジネスシステムが接続を使用した後、接続を接続プールに戻す必要があります.主に下図を参照してください.ソースコードは以下の通りです.
public void returnConnection(ConnectionListener cl, boolean kill) {
    synchronized (cls) {
      /*
			 *          DESTROYED?
			 *          background-validation shuwdown
			 *           DESTORYED  。
			 *
			 */
      if (cl.getState() == ConnectionListener.DESTROYED) {
        if (trace)
          log
          .trace("ManagedConnection is being returned after it was destroyed"
                  + cl);
        //     ,     
        if (cl.hasPermit()) {
          // release semaphore
          cl.grantPermit(false);
          permits.release();
        }

        return;
      }
    }

    if (trace)
      log.trace("putting ManagedConnection back into pool kill=" + kill
          + " cl=" + cl);
    try {
      //          。
      cl.getManagedConnection().cleanup();
    } catch (ResourceException re) {
      log.warn("ResourceException cleaning up ManagedConnection: " + cl,
          re);
      //    ,    ,    。
      kill = true;
    }

    synchronized (cls) {
      //         DESTROY  DESTROYED,   kill true
      if (cl.getState() == ConnectionListener.DESTROY
          || cl.getState() == ConnectionListener.DESTROYED)
        kill = true;
      //checkedOut          。
      checkedOut.remove(cl);

      //  kill==false,     >=    max ,      ,    kill=true
      if (kill == false && cls.size() >= poolParams.maxSize) {
        log
    .warn("Destroying returned connection, maximum pool size exceeded "
                + cl);
        kill = true;
      }

      //kill  
      if (kill) {
        // Adrian Brock: A resource adapter can asynchronously notify us
        // that
        // a connection error occurred.
        // This could happen while the connection is not checked out.
        // e.g. JMS can do this via an ExceptionListener on the
        // connection.
        // I have twice had to reinstate this line of code, PLEASE DO
        // NOT REMOVE IT!
        cls.remove(cl);
      }
      //  kill==false
      else {
        cl.used();
        //               ,   。
        if (cls.contains(cl) == false)
          cls.add(cl);
        else
          log.warn("Attempt to return connection twice (ignored): "
              + cl, new Throwable("STACKTRACE"));
      }

      if (cl.hasPermit()) {
        //     
        cl.grantPermit(false);
        permits.release();
      }
    }

    if (kill) {
      if (trace)
        log.trace("Destroying returned connection " + cl);
      //    。
      doDestroy(cl);
    }
  }

実行プロセスフローチャートは次のとおりです.
ReturnConnetionまとめ:1.接続を解放するのもスレッドの安全な操作です.2.return接続時に既にdestoryの状態になっている可能性があります(前の第3節で述べたSHUTDOWN操作では、接続にDESTORYのマークが付けられます)、この場合、そのままremoveを行えばよいです.3.解放された接続が接続傍受キュー(接続プール)に属さない場合、接続傍受キュー(接続プール)に追加されます.4.接続を解放するには、信号量を解放する必要がある.5.リリース中に例外が発生した場合、接続は接続プールから削除され、強制破棄されます.
JBOSS異常接続の処理:デフォルトでは、JBOSSは無効な接続を破棄しません.異常リストの接続を破棄する必要がある場合は、接続プールのdsが必要です.xmlに次の構成を追加します.
< exception-sorter-class-name>
  	org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter
< /exception-sorter-class-name>

これはORACLEの異常リストです.このクラスに定義されているORACLEの異常リストを表示できます.接続がこのリストのエラーを投げ出すと、破棄されます.破棄できない場合は、異常接続は接続プールに存在します.ORACLE異常リストは以下の通りです.
 public boolean isExceptionFatal(final SQLException e)
    {
    		// I can't remember if the errors are negative or positive.
        final int error_code = Math.abs( e.getErrorCode() );  

        if( ( error_code == 28 )      //session has been killed
         || ( error_code == 600 )     //Internal oracle error
         || ( error_code == 1012 )    //not logged on
         || ( error_code == 1014 )    //Oracle shutdown in progress
         || ( error_code == 1033 )    //Oracle initialization or shutdown in progress
         || ( error_code == 1034 )    //Oracle not available
         || ( error_code == 1035 )    //ORACLE only available to users with RESTRICTED SESSION privilege
         || ( error_code == 1089 )    //immediate shutdown in progress - no operations are permitted
         || ( error_code == 1090 )    //shutdown in progress - connection is not permitted
         || ( error_code == 1092 )    //ORACLE instance terminated. Disconnection forced
         || ( error_code == 1094 )    //ALTER DATABASE CLOSE in progress. Connections not permitted
         || ( error_code == 2396 )    //exceeded maximum idle time, please connect again
         || ( error_code == 3106 )    //fatal two-task communication protocol error
         || ( error_code == 3111 )    //break received on communication channel
         || ( error_code == 3113 )    //end-of-file on communication channel
         || ( error_code == 3114 )    //not connected to ORACLE
         || ( error_code >= 12100 && error_code = 21000 ) &&
              (  (error_text.indexOf("SOCKET") > -1)     //for control socket error
              || (error_text.indexOf("CONNECTION HAS ALREADY BEEN CLOSED") > -1)
              || (error_text.indexOf("BROKEN PIPE") > -1) ) )
        {
            return true;
        }

        return false;
    }

同様に、MYSQLの異常リストも見つけることができます.org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter:
public boolean isExceptionFatal(SQLException e)
   {
      if (e.getSQLState() != null)
      { // per Mark Matthews at MySQL
         if (e.getSQLState().startsWith("08"))
         {
            return true;
         }
      }
      switch (e.getErrorCode())
	  {
      	// Communications Errors
	  	case 1040: // ER_CON_COUNT_ERROR
      	case 1042: // ER_BAD_HOST_ERROR
      	case 1043: // ER_HANDSHAKE_ERROR
      	case 1047: // ER_UNKNOWN_COM_ERROR
      	case 1081: // ER_IPSOCK_ERROR
      	case 1129: // ER_HOST_IS_BLOCKED
      	case 1130: // ER_HOST_NOT_PRIVILEGED

        // Authentication Errors
      	case 1045: // ER_ACCESS_DENIED_ERROR

        // Resource errors
      	case 1004: // ER_CANT_CREATE_FILE
      	case 1005: // ER_CANT_CREATE_TABLE
      	case 1015: // ER_CANT_LOCK
      	case 1021: // ER_DISK_FULL
      	case 1041: // ER_OUT_OF_RESOURCES

        // Out-of-memory errors
      	case 1037: // ER_OUTOFMEMORY
      	case 1038: // ER_OUT_OF_SORTMEMORY

           return true;
      }

      return false;
   }

また、様々なデータベースの例外リストは、自分で構成することができ、このパッケージの下で定義されています:org.jboss.resource.adapter.jdbc.vendor.
この記事は以下のとおりです.http://www.dbafree.net/?p=378