package io.anyway.hera.concurrent;
import io.anyway.hera.collector.MetricHandler;
import io.anyway.hera.common.BlockingStackTraceCollector;
import io.anyway.hera.common.MetricQuota;
import io.anyway.hera.collector.MetricCollector;
import io.anyway.hera.service.NonMetricService;
import io.anyway.hera.spring.BeanPostProcessorWrapper;
import io.anyway.hera.spring.BeanPreProcessorWrapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StringUtils;
import java.util.*;
/**
* Created by yangzz on 16/8/19.
*/
@NonMetricService
public class ThreadPoolCollector implements BeanPostProcessorWrapper,BeanPreProcessorWrapper,MetricCollector {
private MetricHandler handler;
private Log logger= LogFactory.getLog(ThreadPoolCollector.class);
private Map<String,ThreadPoolWrapper> threadPools= new LinkedHashMap<String,ThreadPoolWrapper>();
private Set<String> excludedThreadPools= Collections.emptySet();
private BlockingStackTraceCollector blockingStackTraceCollector;
public void setBlockingStackTraceCollector(BlockingStackTraceCollector blockingStackTraceCollector) {
this.blockingStackTraceCollector = blockingStackTraceCollector;
}
public void setHandler(MetricHandler handler){
this.handler= handler;
}
@Override
public boolean interest(Object bean) {
return bean instanceof ThreadPoolTaskExecutor;
}
@Override
public Object preWrapBean(Object bean, String ctxId, String beanName) {
((ThreadPoolTaskExecutor)bean).setThreadGroupName(beanName+"-Group");
return bean;
}
@Override
public synchronized Object wrapBean(Object bean,String appId, String beanName) {
if(!excludedThreadPools.contains(beanName)){
ThreadPoolWrapper threadPoolWrapper= new ThreadPoolWrapper((ThreadPoolTaskExecutor)bean);
threadPools.put((StringUtils.isEmpty(appId)?"":appId+":")+beanName,threadPoolWrapper);
logger.info("Monitor thread pool: "+beanName);
return threadPoolWrapper;
}
return bean;
}
@Override
public synchronized void destroyWrapper(String appId, String beanName) {
threadPools.remove((StringUtils.isEmpty(appId)?"":appId+":")+beanName);
}
public void setExcludedThreadPools(Set<String> excludedThreadPools) {
this.excludedThreadPools = excludedThreadPools;
logger.info("ExcludedThreadPools: "+excludedThreadPools);
}
@Override
public void doCollect() {
//工作线程池收集
for (Map.Entry<String,ThreadPoolWrapper> each: threadPools.entrySet()){
ThreadPoolWrapper executor= each.getValue();
Map<String,String> tags= new LinkedHashMap<String,String>();
Map<String,Object> props= new LinkedHashMap<String, Object>();
//工作线程池的名称
tags.put("threadPoolName",each.getKey());
//最大线程池数
props.put("maxPoolSize",executor.getMaxPoolSize());
//活跃的线程数
props.put("activeCount",executor.getActiveCount());
//核心的线程数
props.put("corePoolSize",executor.getPoolSize());
//超时时间
props.put("keepAliveSeconds",executor.getKeepAliveSeconds());
//最大队列数
props.put("queueCapacity",executor.getQueueCapacity());
//任务数包括队列和正在执行的任务
props.put("taskCount",executor.getThreadPoolExecutor().getTaskCount()-executor.getThreadPoolExecutor().getCompletedTaskCount());
//发送监控数据
handler.handle(MetricQuota.THREAD,tags,props);
//当线程池被占满需要打印堆栈
if(executor.getMaxPoolSize()== executor.getActiveCount()){
ThreadGroup threadGroup= executor.getThreadGroup();
Thread[] threads= new Thread[threadGroup.activeCount()];
threadGroup.enumerate(threads);
List<StackTraceElement[]> stackTraces= new LinkedList<StackTraceElement[]>();
for(Thread thread: threads){
if(thread!= null){
stackTraces.add(thread.getStackTrace());
}
}
blockingStackTraceCollector.collect(MetricQuota.THREAD,each.getKey(),stackTraces);
}
}
}
}