阻塞佇列解決日誌入庫
阿新 • • 發佈:2018-12-09
業務完成後日誌入庫,如果序列操作會影響業務效率。
所以採用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