log 4 jの非同期哲学AsyncAppender

8080 ワード

良いコードは、良い注釈習慣から始まります.接触するものが多くなって、ますます感銘を受けます.
最近いくつかのc++のインタフェースをデバッグして、20余りのフィールドのインタフェース、意外にも1字の注釈がなくて、フィールド間の階層の依存関係も1字も言いません.結果はもちろん考えられますが、3、5人のグループをつかんでフィールドごとに聞いて、答えてください.一度呼び出して、一度コミュニケーションして、2日余りかかりましたが、今まで通じなくて、挫折感がとても強いです.
 
/**
 * The AsyncAppender lets users log events asynchronously.
 * 
 * 
 * The AsyncAppender will collect the events sent to it and then dispatch them
 * to all the appenders that are attached to it. You can attach multiple
 * appenders to an AsyncAppender.
 * 
 * 
 * 
 * The AsyncAppender uses a separate thread to serve the events in its buffer.
 * 
 * 
 * Important note: The AsyncAppender can only be script
 * configured using the {@link org.apache.log4j.xml.DOMConfigurator}.
 * 
 *
 * @author Ceki Gülcü
 * @author Curt Arnold
 * @since 0.9.1
 */

 
まず、コンストラクション関数を参照してください.
  /**
   * Create new instance.
   */
  public AsyncAppender() {
    appenders = new AppenderAttachableImpl();

    //
    //   only set for compatibility
    aai = appenders;

    dispatcher =
      new Thread(new Dispatcher(this, buffer, discardMap, appenders));

    // It is the user's responsibility to close appenders before
    // exiting.
    dispatcher.setDaemon(true);

    // set the dispatcher priority to lowest possible value
    //        dispatcher.setPriority(Thread.MIN_PRIORITY);
    dispatcher.setName("Dispatcher-" + dispatcher.getName());
    dispatcher.start();
  }

despatcherという最下位レベルのデーモンプロセスを構築しました. 
 
 
実は唯一注目しなければならない方法はappendです.私たちはそれがどのように非同期appendをするかを見ています.
  /**
   * {@inheritDoc}
   */
  public void append(final LoggingEvent event) {
    //
    //   if dispatcher thread has died then
    //      append subsequent events synchronously
    //   See bug 23021
	//   dispatcher     ,      appender      ,       
    if ((dispatcher == null) || !dispatcher.isAlive() || (bufferSize <= 0)) {
      synchronized (appenders) {
        appenders.appendLoopOnAppenders(event);
      }

      return;
    }

    // Set the NDC and thread name for the calling thread as these
    // LoggingEvent fields were not set at event creation time.
    event.getNDC();
    event.getThreadName();
    // Get a copy of this thread's MDC.
    event.getMDCCopy();
    if (locationInfo) {
      event.getLocationInformation();
    }

    synchronized (buffer) { //   buffer      ,         buffer         。
      while (true) {
        int previousSize = buffer.size();

        if (previousSize < bufferSize) {
          buffer.add(event);

          //
          //   if buffer had been empty
          //       signal all threads waiting on buffer
          //       to check their conditions.
          //
          if (previousSize == 0) {
            buffer.notifyAll();  //notifyAll()   Object     ,   Thread     。
          }

          break;
        }

        //
        //   Following code is only reachable if buffer is full
        //     buffer   ,           
        //
        //   if blocking and thread is not already interrupted
        //      and not the dispatcher then
        //      wait for a buffer notification
        boolean discard = true;
        if (blocking  //buffer        
                && !Thread.interrupted()
                && Thread.currentThread() != dispatcher) {
          try {
            buffer.wait(); //      
            discard = false;
          } catch (InterruptedException e) {
            //
            //  reset interrupt status so
            //    calling code can see interrupt on
            //    their next wait or sleep.
            Thread.currentThread().interrupt(); //    interrupt       :)            
          }
        }

        //
        //   if blocking is false or thread has been interrupted
        //   add event to discard map.
        //
        if (discard) { //        wait(),      map   
          String loggerName = event.getLoggerName();
          DiscardSummary summary = (DiscardSummary) discardMap.get(loggerName);

          if (summary == null) {
            summary = new DiscardSummary(event);
            discardMap.put(loggerName, summary);
          } else {
            summary.add(event);
          }

          break;
        }
      }
    }
  }

 
 
ここに2つの新しいものが現れて、dispatcherとDiscardSummary、以下はdispatcherスレッドの実現を分析します:比較的に簡単に理解して、注釈を書かないでください.、
 
  /**
   * Event dispatcher.
   */
  private static class Dispatcher implements Runnable {
    /**
     * Parent AsyncAppender.
     */
    private final AsyncAppender parent;

    /**
     * Event buffer.
     */
    private final List buffer;

    /**
     * Map of DiscardSummary keyed by logger name.
     */
    private final Map discardMap;

    /**
     * Wrapped appenders.
     */
    private final AppenderAttachableImpl appenders;

    /**
     * Create new instance of dispatcher.
     *
     * @param parent     parent AsyncAppender, may not be null.
     * @param buffer     event buffer, may not be null.
     * @param discardMap discard map, may not be null.
     * @param appenders  appenders, may not be null.
     */
    public Dispatcher(
      final AsyncAppender parent, final List buffer, final Map discardMap,
      final AppenderAttachableImpl appenders) {

      this.parent = parent;
      this.buffer = buffer;
      this.appenders = appenders;
      this.discardMap = discardMap;
    }

    /**
     * {@inheritDoc}
     */
    public void run() {
      boolean isActive = true;

      //
      //   if interrupted (unlikely), end thread
      //
      try {
        //
        //   loop until the AsyncAppender is closed.
        //
        while (isActive) {
          LoggingEvent[] events = null;

          //
          //   extract pending events while synchronized
          //       on buffer
          //
          synchronized (buffer) {
            int bufferSize = buffer.size();
            isActive = !parent.closed;

            while ((bufferSize == 0) && isActive) {
              buffer.wait();
              bufferSize = buffer.size();
              isActive = !parent.closed;
            }

            if (bufferSize > 0) {
              events = new LoggingEvent[bufferSize + discardMap.size()];
              buffer.toArray(events);

              //
              //   add events due to buffer overflow
              //
              int index = bufferSize;

              for (
                Iterator iter = discardMap.values().iterator();
                  iter.hasNext();) {
                events[index++] = ((DiscardSummary) iter.next()).createEvent();
              }

              //
              //    clear buffer and discard map
              //
              buffer.clear();
              discardMap.clear();

              //
              //    allow blocked appends to continue
              buffer.notifyAll();
            }
          }

          //
          //   process events after lock on buffer is released.
          //
          if (events != null) {
            for (int i = 0; i < events.length; i++) {
              synchronized (appenders) {
                appenders.appendLoopOnAppenders(events[i]);
              }
            }
          }
        }
      } catch (InterruptedException ex) {
        Thread.currentThread().interrupt();
      }
    }
  }

 
 
クラス全体の分析を見てみると、実は非常に典型的な「生産消費モデル」であり、3つの役割が必要であることが分かった.
生成者(通常はフロントリアルタイムスレッド)、
中継サービス(バッファbufferでもオフラインストレージでJMSに進化し論理処理を加えることもできます)、
消費者()
 
AsyncAppenderはこのモデルの実践です.
1.生産者は通常、資源の制限と時間性能の考慮に基づいて、生産段階ですぐに呼び出しの戻り値を得る必要がある.
2.生産者、消費者は業務ロジックをすべて剥離し、中継サービスによって行う.
3..中継サービスはシステム設計全体の真髄になります.業務の性能指標、信頼性期待指標、異常プロセス処理(例えば、重さを落とすかどうか、糸を落として再発行するかどうか)に工夫する必要がある. 
4.消費者は通常、生成者と独立して開く必要がある.システムの制御ロジックはすべて中継サービス層にあるため、消費者は低優先度の別のデーモンスレッドを完全に使用することができる.
5.このモデルは必ず定量的に分析しなければならない.消費速度が生成速度に追いつかないと、中継bufferのオーバーフロー処理は非常に面倒なことだからだ.AsyncAppenderのDiscardSummaryはそのために設計されています.