最近在对一个系统做性能优化!
现状分析:
该系统有三类线程:
A类-接收线程: 从mq中获取消息(消息字符数大约1千至3千个字符);该类线程可以启动多个,因为可以从多个mq队列获取消息;
B类-处理线程: 获取A类线程接收的消息并进行解析和逻辑处理,最后生成新的消息传递给C类线程,B类线程是单线程;
C类-发送线程: 将B类线程生成的新消息(新消息字符数大约2千至9千个字符),发送给多个mq队列,C类线程为多个线程;
A类线程调用A_Logger日志对象打印接收到的消息(有1+个A类线程共享A_Logger日志对象);
B类线程调用B_Logger日志对象打印处理过程中的日志以便跟踪处理流程(单个B线程会在处理过程中多次打印处理日志);
C类线程调用C_Logger日志对象打印B类线程处理后生成的消息(有1+个C类线程共享C_Logger对象);
//注释:这里的logger日志对象用的是log4j
分析:
- 因为对一个logger对象进行调用的时候,logger是会被加锁的,所以多线程同时调用一个logger的时候,其实它们是串行执行的;
- 打印日志这种事,完全跟当前的业务处理逻辑没有任何关系,所以可以考虑使用单独的线程去做这种事;
改造方案:
- 为每一类Logger日志对象创建单独的线程,在线程里面负责真正的日志打印(因为日志对象不是很多,所以这样创建的线程也不会很多);
- 将在程序中调用Logger对象打印日志的操作修改为把需要打印的日志内容传递给相应的日志打印线程来处理;
- 尽量避免对程序中打印日志部分代码的修改;这个时候就能使用装饰者模式(Decorator)了;
Decorator定义:
动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.
package kpicomm;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import DBUtil.MethodTime;
/**
* 异步日志打印组件!
*
* 该类用作将日志操作从当前的业务线程中分离出来,交给单独的日志打印线程来操作;
* 该类的实现依赖于slf4j;
*
* 使用方法:
* 1.调用静态的LoggerAsyn.getLogger获取日志组件;(初始调用会创建一个异步日志组件,并作为线程运行);
* 2.像log4j,slf4j一样调用isDebugEnabled,debug等方法;
* 3.LoggerAsyn.removeLogger删除对应日志组件,并且停止线程;
*
* @author LostHu
*
*/
public class LoggerAsyn extends LoggerAsynAdapter implements Runnable{
static private Map<Logger ,Logger > loggerMap = new HashMap<Logger, Logger>();
private MethodTime methodTime = new MethodTime();
/**
* 根据日志组件名称获取异步日志打印组件
* @param loggerName 日志组件名称
* @return
*/
public static Logger getLogger(String loggerName){
Logger logger = LoggerFactory.getLogger(loggerName);
return getLogger(logger);
}
/**
* 根据slf4j日志组件对象获取异步日志打印组件
* @param logger slf4j日志组件对象
* @return
*/
public static Logger getLogger(Logger logger){
Logger loggerReturn = null;
synchronized (loggerMap) {
loggerReturn = loggerMap.get(logger);
if (loggerReturn == null){
loggerReturn = new LoggerAsyn(logger);
loggerMap.put(logger, loggerReturn);
new Thread((LoggerAsyn)loggerReturn).start();
}
}
return loggerReturn;
}
/**
* 删除日志组件名称对应的异步日志打印组件,并停止该日志打印线程
* @param loggerName 日志组件名称
* @return
*/
public static boolean removeLogger(String loggerName){
Logger logger = LoggerFactory.getLogger(loggerName);
return removeLogger(logger);
}
/**
* 删除slf4j日志组件对象对应的异步日志打印组件,并停止该日志打印线程
* @param loggerName slf4j日志组件对象
* @return
*/
public static boolean removeLogger(Logger logger){
if (logger == null) return true;
Logger loggerReturn = null;
synchronized (loggerMap) {
loggerReturn = loggerMap.get(logger);
if (loggerReturn != null){
((LoggerAsyn)loggerReturn).isRunning = false;
}
loggerMap.remove(logger);
}
return true;
}
/**
* 真实被委托打印的日志对象
*/
private Logger realLogger ;
/**
* 日志组件线程是否运行
*/
private volatile boolean isRunning = true;
public Logger getRealLogger() {
return realLogger;
}
/**
* 私有的构造方法,只有在静态的getLogger才被调用;
* 传入的日志打印组件为null的时候,会默认一个名字为LoggerAsyn的日志对象(避免这种情况)
* @param logger 真实的日志打印组件;
*/
private LoggerAsyn(Logger logger) {
super();
this.realLogger = logger;
if (this.realLogger==null){
this.realLogger = LoggerFactory.getLogger("LoggerAsyn");
}
}
LinkedBlockingQueue<LogEvent> clq = new LinkedBlockingQueue<LogEvent>();
LinkedBlockingQueue<LogEvent> getClq() {
return clq;
}
public void run(){
Thread.currentThread().setName(getRealLogger().getName());
LogEvent logEvent = null;
while(isRunning){
try {
logEvent = getClq().take();
if (isRunning){
if (logEvent!=null){
methodTime.start(getRealLogger().getName());
switch (logEvent.getLog_level()) {
case LoggerAsyn.DEBUG_LEVEL:
getRealLogger().debug(logEvent.getLogString());
break;
case LoggerAsyn.INFO_LEVEL:
getRealLogger().info(logEvent.getLogString());
break;
case LoggerAsyn.WARN_LEVEL:
getRealLogger().warn(logEvent.getLogString());
break;
case LoggerAsyn.ERROR_LEVEL:
getRealLogger().error(logEvent.getLogString());
break;
default:
getRealLogger().info(logEvent.getLogString());
break;
}
methodTime.end(getRealLogger().getName());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 日志打印事件对象
* @author LostHu
*
*/
class LogEvent{
/**
* 日志打印的级别
*/
int log_level;
/**
* 日志打印的内容
*/
String logString;
public LogEvent(int log_level, String logString) {
super();
this.log_level = log_level;
this.logString = logString;
}
public int getLog_level() {
return log_level;
}
public void setLog_level(int log_level) {
this.log_level = log_level;
}
public String getLogString() {
return logString;
}
public void setLogString(String logString) {
this.logString = logString;
}
}
@Override
public void debug(String arg0) {
LogEvent le = new LogEvent(LoggerAsyn.DEBUG_LEVEL,arg0);
clq.add(le);
}
@Override
public void info(String arg0) {
LogEvent le = new LogEvent(LoggerAsyn.INFO_LEVEL,arg0);
clq.add(le);
}
@Override
public void warn(String arg0) {
LogEvent le = new LogEvent(LoggerAsyn.WARN_LEVEL,arg0);
clq.add(le);
}
@Override
public void error(String arg0) {
LogEvent le = new LogEvent(LoggerAsyn.ERROR_LEVEL,arg0);
clq.add(le);
}
@Override
public boolean isDebugEnabled() {
return getRealLogger().isDebugEnabled();
}
@Override
public boolean isInfoEnabled() {
return getRealLogger().isInfoEnabled();
}
@Override
public boolean isWarnEnabled() {
return getRealLogger().isWarnEnabled();
}
@Override
public boolean isErrorEnabled() {
return getRealLogger().isErrorEnabled();
}
}
代码解释:
LoggerAsyn是实现org.slf4j.Logger接口,是对真实Logger对象的装饰者(因为真正的日志打印还是委托给真实的Logger对象)
使用LoggerAsyn对象去改造程序:
改造前代码:
private static Logger log = LoggerFactory.getLogger("xxxx");
改造后代码
private static Logger log_proc = LoggerAsyn.getLogger("xxxx");
非常简单,改动量非常非常小!
源代码请见附件!