在用户操作量较大的情况下,如果实时写入日志会导致资源被严重占用,用户长时间获取不到返回结果,影响用户体验,所以日志操作通常是在用户操作完后放入异步队列,等用户操作少的时间段再将日志写入数据库。
此处实现异步日志写入用到了并发包下的ConcurrentLinkedQueue,一个线程安全的队列实现,遵循FIFO原则进行排序,采用CAS操作,来保证元素的一致性。
首先采用模板模式,定义一个异步队列服务的模板类BaseAsyncQueueService,其中包含一个队列log_queue,和异步操作的抽象方法asyncProcess()。
/**
* 基础异步队列服务,用于异步保存日志信息
*/
public abstract class BaseAsyncQueueService<T> {
/**
* 处理,消费
* @param t
* @return
*/
public abstract boolean asyncProcess(T t);
/**
* 队列
*/
private ConcurrentLinkedQueue<Object> log_queue;
public BaseAsyncQueueService() {
log_queue = new ConcurrentLinkedQueue();
}
public synchronized boolean isEmpty() {
return log_queue.isEmpty();
}
public synchronized int getSize() {
return log_queue.size();
}
public synchronized T poll() {
return (T) log_queue.poll();
}
public synchronized void offer(T bean) {
log_queue.offer(bean);
}
}
然后需要异步写入日志的Service只需要继承模板类,并实现asyncProcess()即可
@Service
public class UserService extends BaseAsyncQueueService<User>{
@Override
public boolean asyncProc(User user) {
if(null == user)
return false;
try {
UserLog userLog = new UserLog();
userLog.setUserId(user.getId());
LogService.save(UserLog);
return true;
}catch (Exception e){
log.warn("异步写入日志失败!!!", e);
}
return false;
}
}
然后通过一个统一任务类去触发各个Service中的异步写入任务,这里用到了@PostConstruct注解,即在被注入的UserService实例化时就调用
/**
* 异步消费处理task
*/
public abstract class BaseAsyncQueueThreadTask {
private static final Log log = LogFactory.getLog(BaseAsyncQueueThreadTask.class);
/**
* 是否处理
*
* @return
*/
protected abstract boolean isProc();
protected abstract BaseAsyncQueueService getAsyncQueueService();
public int getSize(){
return this.getAsyncQueueService().getSize();
}
/**
* 开启线程数量
*
* @return
*/
public int getThreadNum() {
return 1;
}
@PostConstruct
public void task() {
for (int i = 0; i < getThreadNum(); i++) {
new TaskThread().start();
}
}
class TaskThread extends Thread {
@Override
public void run() {
while (true) {
try {
Object bean = getAsyncQueueService().poll();
if (!isProc())
return;
if (bean == null) {
// 如果是空,则休息2秒
ThreadUtil.sleepSeccond(2);
} else {
boolean rest = getAsyncQueueService().asyncProc(bean);
if (rest)
addSuccessCount();
else
addErrorCount();
}
} catch (Exception e) {
log.error(e, e);
addErrorCount();
}
}
}
}
}
来源:https://blog.csdn.net/pandalovey/article/details/98738606