package com.taobao.tddl.monitor.stat;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Map.Entry;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* 带内存汇总功能的日志输出工具的另一种实现, 使用 SoftReference/WeakReferece 解决 BufferedLogWriter
* OldGen 内存占用和 FullGC的问题。 <br />
* 这个工具的风险是 SoftReference 的清理时机完全由 JVM 确定, 在大部分情况下都不会回收 SoftReference 除非内存不足,
* 这样会造成不必要的内存占用。 <br />
* 如果使用 WeakReference 的风险是频繁的 minor GC 会回收统计记录, 造成大量统计记录的丢失。 <br />
* 因此建议的使用方法是:
*
* <pre>
* // 创建 counter 对象, 并且保存在内存
* LogCounter counter = SoftRefLogWriter.getCounter(
* new Object[] { key1, key2, key3, ... }); // 统计的目标
*
* // 创建 counter 对象, 并且保存在内存
* LogCounter counter = SoftRefLogWriter.getCounter(
* new Object[] { key1, key2, key3, ... }, // 统计的目标
* new Object[] { obj1, obj2, obj3, ... }); // 附加的对象
*
* counter.stat(count, value); // 输出统计值
* </pre>
*
* @author changyuan.lh
*/
public class SoftRefLogWriter extends AbstractStatLogWriter {
protected static final Logger logger = LoggerFactory.getLogger(SoftRefLogWriter.class);
protected volatile int flushInterval = 300; // 单位秒,默认5分钟全量刷出一次
protected volatile boolean softRef = true;
protected static final class WeakRefLogCounter extends WeakReference<LogCounter> {
final LogKey logKey;
public WeakRefLogCounter(LogKey logKey, LogCounter counter, ReferenceQueue<LogCounter> queue){
super(counter, queue);
this.logKey = logKey;
}
}
protected static final class SoftRefLogCounter extends SoftReference<LogCounter> {
final LogKey logKey;
public SoftRefLogCounter(LogKey logKey, LogCounter counter, ReferenceQueue<LogCounter> queue){
super(counter, queue);
this.logKey = logKey;
}
}
protected volatile ConcurrentHashMap<LogKey, Reference<LogCounter>> map;
/**
* Reference queue for cleared WeakEntries
*/
private final ReferenceQueue<LogCounter> queue = new ReferenceQueue<LogCounter>();
protected final StatLogWriter nestLog;
public SoftRefLogWriter(boolean softRef, int flushInterval, StatLogWriter nestLog){
this.map = new ConcurrentHashMap<LogKey, Reference<LogCounter>>( // NL
1024,
0.75f,
32);
this.flushInterval = flushInterval;
this.softRef = softRef;
this.nestLog = nestLog;
schdeuleFlush();
}
public SoftRefLogWriter(boolean softRef, StatLogWriter nestLog){
this.map = new ConcurrentHashMap<LogKey, Reference<LogCounter>>( // NL
1024,
0.75f,
32);
this.softRef = softRef;
this.nestLog = nestLog;
schdeuleFlush();
}
public SoftRefLogWriter(StatLogWriter nestLog){
this.map = new ConcurrentHashMap<LogKey, Reference<LogCounter>>( // NL
1024,
0.75f,
32);
this.nestLog = nestLog;
schdeuleFlush();
}
public void setFlushInterval(int flushInterval) {
if (this.flushInterval != flushInterval) {
this.flushInterval = flushInterval;
schdeuleFlush();
}
}
public int getFlushInterval() {
return flushInterval;
}
public boolean isSoftRef() {
return softRef;
}
public void setSoftRef(boolean softRef) {
this.softRef = softRef;
}
/**
* 创建记录对象的 SoftReference/WeakReference.
*/
protected final Reference<LogCounter> createLogRef(LogKey logKey, LogCounter counter) {
return softRef ? new SoftRefLogCounter(logKey, counter, queue) : new WeakRefLogCounter(logKey, counter, queue);
}
/**
* 清理已经回收的对象占据的 map entry.
*/
protected final void expungeLogRef() {
final long expungeMillis = System.currentTimeMillis();
int count = 0;
Reference<? extends LogCounter> entry;
while ((entry = queue.poll()) != null) {
if (entry instanceof SoftRefLogCounter) {
map.remove(((SoftRefLogCounter) entry).logKey);
count++;
} else if (entry instanceof WeakRefLogCounter) {
map.remove(((WeakRefLogCounter) entry).logKey);
count++;
}
}
if (count > 0 && logger.isDebugEnabled()) {
logger.debug("expungeLogRef: " + count + " logs in " + (System.currentTimeMillis() - expungeMillis)
+ " milliseconds.");
}
}
/**
* 创建一个记录对象, 或者返回缓存中已有的记录。
*/
public LogCounter getCounter(Object[] keys, Object[] fields) {
ConcurrentHashMap<LogKey, Reference<LogCounter>> map = this.map;
LogKey logKey = new LogKey(keys);
LogCounter counter;
for (;;) {
Reference<LogCounter> entry = map.get(logKey);
if (entry == null) {
LogCounter newCounter = new LogCounter(logKey, (fields == null) ? keys : fields);
entry = map.putIfAbsent(logKey, createLogRef(logKey, newCounter));
if (entry == null) {
expungeLogRef();
return newCounter;
}
}
counter = entry.get();
if (counter != null) {
return counter;
}
map.remove(logKey);
}
}
/**
* 在内存中汇总统计信息。
*/
public void write(Object[] keys, Object[] fields, long... values) {
if (values.length != 2) {
// XXX: 这里限制 BufferedLogWriter 只接受 count + value 的输入
throw new IllegalArgumentException("Only support 2 values!");
}
ConcurrentHashMap<LogKey, Reference<LogCounter>> map = this.map;
LogKey logKey = new LogKey(keys);
LogCounter counter;
for (;;) {
Reference<LogCounter> entry = map.get(logKey);
if (entry == null) {
LogCounter newCounter = new LogCounter(logKey, (fields == null) ? keys : fields);
newCounter.stat(values[0], values[1]);
entry = map.putIfAbsent(logKey, createLogRef(logKey, newCounter));
if (entry == null) {
expungeLogRef();
return;
}
}
counter = entry.get();
if (counter != null) {
counter.stat(values[0], values[1]);
return;
}
map.remove(logKey);
}
}
protected volatile TimerTask flushTask = null;
protected final Lock flushLock = new ReentrantLock();
protected volatile boolean flushing = false;
public boolean flush() {
if (!flushing && flushLock.tryLock()) {
try {
flushing = true;
flushExecutor.execute(new Runnable() {
public void run() {
try {
flushAll();
} finally {
flushing = false;
}
}
});
} finally {
flushLock.unlock();
}
return true;
}
return false;
}
private final synchronized void schdeuleFlush() {
TimerTask cancelTask = this.flushTask;
this.flushTask = new TimerTask() {
public void run() {
// XXX: 定时器的执行应当耗时非常短
flush();
}
};
if (cancelTask != null) {
cancelTask.cancel();
}
final long flushPriod = flushInterval * 1000;
flushTimer.scheduleAtFixedRate(flushTask, flushPriod, flushPriod);
}
/**
* 刷出所有的日志统计信息。
*/
protected void flushAll() {
final long flushMillis = System.currentTimeMillis();
// 清理已经回收的对象
expungeLogRef();
// XXX: 输出的日志按 Key 进行排序 -- 先取消
// TreeMap<LogKey, Reference<LogCounter>> map = new TreeMap<LogKey,
// SoftReference<LogCounter>>(map);
ConcurrentHashMap<LogKey, Reference<LogCounter>> map = this.map;
int count = 0;
for (Entry<LogKey, Reference<LogCounter>> entry : map.entrySet()) {
LogCounter counter = entry.getValue().get();
if (counter != null && counter.getCount() > 0) {
LogKey logKey = entry.getKey();
nestLog.write(logKey.getKeys(), counter.getFields(), counter.getValues());
counter.clear();
count++;
}
}
if (count > 0 && logger.isDebugEnabled()) {
logger.debug("flushAll: " + count + " logs in " + (System.currentTimeMillis() - flushMillis)
+ " milliseconds.");
}
}
}