工作中开发的系统遇到了压测,埋点日志部分没有通过(通过切面把日志数据存到数据库中),针对这块引入生产者消费者模式来进行优化,记录下。
大概流程就是切面日志数据放入队列中(生产者) 然后 弄一个线程去轮询这个队列(消费者),把队列中的日志数据存到数据库中,以这样异步的形式来记录日志数据,来达到埋点日志优化效果。
代码:
日志队列
public class LogDataQueue {
private static final Logger logger = LoggerFactory.getLogger(LogDataQueue.class);
// 用来存放日志的 线程安全队列
private ArrayBlockingQueue<LogItem> logQueue = new ArrayBlockingQueue<LogItem>(5);
// 日志存入队列方法
public void putLogItemData(LogItem logItem) {
try {
logQueue.put(logItem);
logger.debug("LogAllDataQueue: "+ JSON.toJSONString(logItem) + " 存入文件列");
} catch (Exception e) {
logger.error("LogAllDataQueue"+ JSON.toJSONString(logItem) + " 存入队列失败!", e);
}
}
// 从队列取日志方法
public LogItem takeLogData() {
try {
LogItem logItem = logQueue.take();
logger.info("LogDataQueue:从日志队列取日志成功!");
return logItem;
} catch (Exception e) {
e.printStackTrace();
logger.error("LogDataQueue——takeLogData:从日志队列取日志失败!", e);
}
return null;
}
private static class LogDataSingleton {
private static LogDataQueue logDataInstance;
static {
logDataInstance = new LogDataQueue();
}
public static LogDataQueue getInstance() {
return logDataInstance;
}
}
public static LogDataQueue getInstance() {
return LogDataQueue.LogDataSingleton.getInstance();
}
public static void init() {
getInstance();
}
}
消费日志线程
public class LogDataThread implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(LogDataThread.class);
@Override
public void run() {
LogDataQueue logDataQueue = LogDataQueue.getInstance();
ILogDBService logDBService = SpringUtil.getBean(ILogDBService.class);
// 轮询取队列中日志数据
while (true) {
LogItem logItem = logDataQueue.takeLogData();
if (logItem != null) {
try {
// 存入数据库
logDBService.saveLog(logItem);
} catch (Exception e) {
logger.error("从队列获取日志存入数据库失败!logItem={}", JSON.toJSONString(logItem), e);
}
}
}
}
}
然后再日志切面里调putLogItemData方法,把日志数据放入队列,然后在消费日志的线程放到线程池里去轮询取队列中的日志。