com.github.ltsopensource.biz.logger.LazyJobLogger Maven / Gradle / Ivy
package com.github.ltsopensource.biz.logger;
import com.github.ltsopensource.admin.response.PaginationRsp;
import com.github.ltsopensource.biz.logger.domain.JobLogPo;
import com.github.ltsopensource.biz.logger.domain.JobLoggerRequest;
import com.github.ltsopensource.core.AppContext;
import com.github.ltsopensource.core.cluster.Config;
import com.github.ltsopensource.core.commons.utils.CollectionUtils;
import com.github.ltsopensource.core.constant.Constants;
import com.github.ltsopensource.core.factory.NamedThreadFactory;
import com.github.ltsopensource.core.logger.Logger;
import com.github.ltsopensource.core.logger.LoggerFactory;
import com.github.ltsopensource.core.support.NodeShutdownHook;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 内部根据用户参数决定是否采用延迟批量刷盘的策略,来提高吞吐量
* 批量刷盘有两种情况:
* 1. 内存的日志量超过了设置的阀值
* 2. 每3S检查一次内存中是否有日志,如果有就那么刷盘
*
* @author Robert HG ([email protected]) on 10/2/15.
*/
public class LazyJobLogger implements JobLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(SmartJobLogger.class);
private JobLogger delegate;
// 无界Queue
private BlockingQueue memoryQueue = new LinkedBlockingQueue();
// 日志批量刷盘数量
private int batchFlushSize;
private int overflowSize;
// 内存中最大的日志量阀值
private int maxMemoryLogSize;
private AtomicBoolean flushing = new AtomicBoolean(false);
public LazyJobLogger(AppContext appContext, JobLogger delegate) {
this.delegate = delegate;
Config config = appContext.getConfig();
maxMemoryLogSize = config.getParameter(Constants.LAZY_JOB_LOGGER_MEM_SIZE, 1000);
int flushPeriod = config.getParameter(Constants.LAZY_JOB_LOGGER_CHECK_PERIOD, 3);
batchFlushSize = config.getParameter(Constants.LAZY_JOB_LOGGER_BATCH_FLUSH_SIZE, 100);
overflowSize = config.getParameter(Constants.LAZY_JOB_LOGGER_OVERFLOW_SIZE, 10000);
final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(
new NamedThreadFactory("LazyJobLogger", true));
final ScheduledFuture> scheduledFuture = executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
if (flushing.compareAndSet(false, true)) {
checkAndFlush();
}
} catch (Throwable t) {
LOGGER.error("CheckAndFlush log error", t);
}
}
}, flushPeriod, flushPeriod, TimeUnit.SECONDS);
NodeShutdownHook.registerHook(appContext, this.getClass().getName(), new com.github.ltsopensource.core.commons.utils.Callable() {
public void call() throws Exception {
scheduledFuture.cancel(true);
executor.shutdownNow();
}
});
}
/**
* 检查内存中是否有日志,如果有就批量刷盘
*/
private void checkAndFlush() {
try {
int nowSize = memoryQueue.size();
if (nowSize == 0) {
return;
}
List batch = new ArrayList();
for (int i = 0; i < nowSize; i++) {
JobLogPo jobLogPo = memoryQueue.poll();
batch.add(jobLogPo);
if (batch.size() >= batchFlushSize) {
flush(batch);
}
}
if (batch.size() > 0) {
flush(batch);
}
} finally {
flushing.compareAndSet(true, false);
}
}
private void checkOverflowSize() {
if (memoryQueue.size() > overflowSize) {
throw new JobLogException("Memory Log size is " +
memoryQueue.size() + " , please check the JobLogger is available");
}
}
private void flush(List batch) {
boolean flushSuccess = false;
try {
delegate.log(batch);
flushSuccess = true;
} finally {
if (!flushSuccess) {
memoryQueue.addAll(batch);
}
batch.clear();
}
}
/**
* 检查内存中的日志量是否超过阀值,如果超过需要批量刷盘日志
*/
private void checkCapacity() {
if (memoryQueue.size() > maxMemoryLogSize) {
// 超过阀值,需要批量刷盘
if (flushing.compareAndSet(false, true)) {
// 这里可以采用new Thread, 因为这里只会同时new一个
new Thread(new Runnable() {
@Override
public void run() {
try {
checkAndFlush();
} catch (Throwable t) {
LOGGER.error("Capacity full flush error", t);
}
}
}).start();
}
}
}
@Override
public void log(JobLogPo jobLogPo) {
if (jobLogPo == null) {
return;
}
checkOverflowSize();
memoryQueue.offer(jobLogPo);
checkCapacity();
}
@Override
public void log(List jobLogPos) {
if (CollectionUtils.isEmpty(jobLogPos)) {
return;
}
checkOverflowSize();
for (JobLogPo jobLogPo : jobLogPos) {
memoryQueue.offer(jobLogPo);
}
// checkCapacity
checkCapacity();
}
@Override
public PaginationRsp search(JobLoggerRequest request) {
return delegate.search(request);
}
}