log 4 jの非同期哲学AsyncAppender
8080 ワード
良いコードは、良い注釈習慣から始まります.接触するものが多くなって、ますます感銘を受けます.
最近いくつかのc++のインタフェースをデバッグして、20余りのフィールドのインタフェース、意外にも1字の注釈がなくて、フィールド間の階層の依存関係も1字も言いません.結果はもちろん考えられますが、3、5人のグループをつかんでフィールドごとに聞いて、答えてください.一度呼び出して、一度コミュニケーションして、2日余りかかりましたが、今まで通じなくて、挫折感がとても強いです.
まず、コンストラクション関数を参照してください.
despatcherという最下位レベルのデーモンプロセスを構築しました.
実は唯一注目しなければならない方法はappendです.私たちはそれがどのように非同期appendをするかを見ています.
ここに2つの新しいものが現れて、dispatcherとDiscardSummary、以下はdispatcherスレッドの実現を分析します:比較的に簡単に理解して、注釈を書かないでください.、
クラス全体の分析を見てみると、実は非常に典型的な「生産消費モデル」であり、3つの役割が必要であることが分かった.
生成者(通常はフロントリアルタイムスレッド)、
中継サービス(バッファbufferでもオフラインストレージでJMSに進化し論理処理を加えることもできます)、
消費者()
AsyncAppenderはこのモデルの実践です.
1.生産者は通常、資源の制限と時間性能の考慮に基づいて、生産段階ですぐに呼び出しの戻り値を得る必要がある.
2.生産者、消費者は業務ロジックをすべて剥離し、中継サービスによって行う.
3..中継サービスはシステム設計全体の真髄になります.業務の性能指標、信頼性期待指標、異常プロセス処理(例えば、重さを落とすかどうか、糸を落として再発行するかどうか)に工夫する必要がある.
4.消費者は通常、生成者と独立して開く必要がある.システムの制御ロジックはすべて中継サービス層にあるため、消費者は低優先度の別のデーモンスレッドを完全に使用することができる.
5.このモデルは必ず定量的に分析しなければならない.消費速度が生成速度に追いつかないと、中継bufferのオーバーフロー処理は非常に面倒なことだからだ.AsyncAppenderのDiscardSummaryはそのために設計されています.
最近いくつかの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はそのために設計されています.