阻塞队列解决日志入库

本文介绍如何利用AOP拦截日志信息,通过阻塞队列实现并发日志入库,避免串行操作影响业务效率。当队列满时,选择抛出异常而非阻塞,确保系统稳定,并自定义消费者数量以优化处理速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

业务完成后日志入库,如果串行操作会影响业务效率。

所以采用AOP进行日志信息的切面拦截,并且使用队列进行入库操作。

package com.java.main.base.aspect;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpSession;

import net.sf.json.JSONObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.java.main.auth.model.MobileUserAuthInfo;
import com.java.main.base.service.LogService;
import com.java.main.base.queue.InterfaceLogSaveQueue;
import com.java.main.utils.TimeUtils;

/**
 * 日志记录-webService
 * @author Administrator
 *
 */
@Component
@Aspect
public class LogServiceAspect {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

	@Autowired
	private HttpSession session;
	@Autowired
	private LogService webServiceLogService;
	
	/**
	 * 系统接口切面
	 * @param pjp
	 * @return
	 * @throws Throwable
	 */
	@Around("@annotation(com.java.main.base.annotation.SystemInterfaceLog)")
	public Object systemInterfaceLogAround(ProceedingJoinPoint pjp) throws Throwable {
		String methodName = pjp.getSignature().getName();
		logger.info("LogServiceAspect SystemInterfaceLog: " + methodName + "----------开始");
		
		Object[] args = pjp.getArgs();
		String className = pjp.getSignature().getDeclaringTypeName();
		String operationStartTime = TimeUtils.getCurrentDate("yyyy-MM-dd hh:mm:ss");
		Object result = pjp.proceed();
		
		Map<String, Object> map = new HashMap<String, Object>();
		//webService
		if(className.equals("com.java.main.base.service.impl.WSInvokeServiceImpl")) {
			MobileUserAuthInfo mus = (MobileUserAuthInfo) session.getAttribute("mobileUserAuthSession");
			map.put("loginName", mus.getLoginName());
			map.put("fullName", mus.getFullName());
			map.put("depId", mus.getDepId());
			map.put("depName", mus.getDepName());
			map.put("operationSystem", mus.getAccessAppName());
			map.put("type", args[0]);
			map.put("returnXml", result);
			map.put("operationStartTime", operationStartTime);
			//处理入参
			Map<String, String> paramsMap = new HashMap<String, String>();
			String[] paramNames = (String[]) args[1];
			String[] params = (String[]) args[2];
			for(int i = 0 ; i < paramNames.length ; i++) {
				paramsMap.put(paramNames[i], params[i]);
			}
			map.put("params", JSONObject.fromObject(paramsMap).toString());
		
		}
		//日志数据入队
		InterfaceLogSaveQueue.getInstance().add(map);
		
		logger.info("LogServiceAspect SystemInterfaceLog: " + methodName + "----------结束");

		return result;
	}
	
	/**
	 * 应用访问切面
	 * @param pjp
	 * @return
	 * @throws Throwable
	 */
	@Around("@annotation(com.java.main.base.annotation.AppAccessLog)")
	public Object appAccessLogAround(ProceedingJoinPoint pjp) throws Throwable {
		String methodName = pjp.getSignature().getName();
		logger.info("LogServiceAspect AppAccessLog: " + methodName + "----------开始");
		
		String className = pjp.getSignature().getDeclaringTypeName();
		String operationStartTime = TimeUtils.getCurrentDate("yyyy-MM-dd hh:mm:ss");
		Object result = pjp.proceed();
		
		Map<String, Object> map = new HashMap<String, Object>();
		
		if(className.equals("com.java.main.email.service.impl.EmailAuthServiceImpl")) {
			MobileUserAuthInfo mus = (MobileUserAuthInfo) session.getAttribute("mobileUserAuthSession");
			map.put("loginName", mus.getLoginName());
			map.put("fullName", mus.getFullName());
			map.put("depId", mus.getDepId());
			map.put("depName", mus.getDepName());
			map.put("operationSystem", mus.getAccessAppName());
			map.put("operationSystem", "邮箱");
			map.put("type", "邮箱入口");
			map.put("operationStartTime", operationStartTime);
		}
		
		//日志数据入队
		InterfaceLogSaveQueue.getInstance().add(map);
		
		logger.info("LogServiceAspect AppAccessLog: " + methodName + "----------结束");

		return result;
	}
}

InterfaceLogSaveQueue队列进行日志数据的add操作。

package com.java.main.base.queue;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.java.main.common.constants.PropertiesUtils;

/**
 * 接口日志队列
 * @author Administrator
 *
 */
public class InterfaceLogSaveQueue {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

	private static final InterfaceLogSaveQueue interfaceLogQueue = new InterfaceLogSaveQueue();
	//队列容量
	private int size = getPoolSize();
	//阻塞队列
	private final BlockingQueue<Map<String, Object>> queue = new LinkedBlockingQueue<Map<String, Object>>(size);
	
	private InterfaceLogSaveQueue() {}
	
	public static InterfaceLogSaveQueue getInstance() {
		return interfaceLogQueue;
	}
	
	private int getPoolSize() {
		String sizeStr = PropertiesUtils.getProperty("queue.interface.log.save.pool.size");
		sizeStr = Optional.ofNullable(sizeStr).filter(s -> !s.equals("")).orElse("0");
		logger.info("接口日志队列-容量:" + sizeStr);
		
		return Integer.parseInt(sizeStr);
	}
	
	public void add(Map<String, Object> map) {
		logger.info("【接口日志队列】【入列操作】=" + map);
		//队列满了抛出异常
		try {
			queue.add(map);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Map<String, Object> take() {
		try {
			Map<String, Object> m = queue.take();
			logger.info("【接口日志队列】【出列操作】=" + m);
			return m;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return null;
	}
	
}

 队列初始化容量自定义(默认1000),add操作队列满抛出异常。如果这里阻塞会影响其他业务操作。抛出异常更为合理,更改适当的容量。

package com.java.main.base.queue;

import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.java.main.base.service.LogService;
import com.java.main.common.constants.PropertiesUtils;
import com.java.main.common.core.util.WebApplicationManager;

/**
 * 接口日志消费类
 * @author Administrator
 *
 */
public class InterfaceLogSaveCustomer implements Runnable {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());

	private InterfaceLogSaveQueue interfaceLogQueue;
	
	@Autowired
	private LogService webServiceLogService;
	
	public void startThread() {
		String sizeStr = PropertiesUtils.getProperty("queue.interface.log.save.customer.size");
		sizeStr = Optional.ofNullable(sizeStr).filter(s -> !s.equals("")).orElse("0");
		logger.info("接口日志队列-消费者启动....");
		logger.info("接口日志队列-消费数量:" + sizeStr);
		
		int size = Integer.parseInt(sizeStr);
		ExecutorService executor = Executors.newFixedThreadPool(size);
		for(int i = 0; i < size; i++) {
			executor.execute(new InterfaceLogSaveCustomer());
		}
	}
	
	public InterfaceLogSaveCustomer() {
		webServiceLogService = (LogService) WebApplicationManager.getBean("webServiceLogService");
		interfaceLogQueue = InterfaceLogSaveQueue.getInstance();
	}
	
	@Override
	public void run() {
		while(true) {
			if(webServiceLogService != null) {
				webServiceLogService.addLog(interfaceLogQueue.take());
			}
		}
	}
	
}

InterfaceLogSaveCustomer类自定义消费者数量。

在系统启动初始化操作(也可以使用注解方式):
new InterfaceLogSaveCustomer().startThread();

这里使用的是ServletContextListener

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值