1. 程式人生 > >阻塞佇列解決日誌入庫

阻塞佇列解決日誌入庫

業務完成後日誌入庫,如果序列操作會影響業務效率。

所以採用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