mirror of https://github.com/longtai-cn/hippo4j
添加线程池内运行堆栈查看. (#23)
parent
997ca84836
commit
e7b81cf0e8
@ -0,0 +1,23 @@
|
|||||||
|
package cn.hippo4j.common.api;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.model.ThreadDetailStateInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get thread status in thread pool.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2022/1/9 12:47
|
||||||
|
*/
|
||||||
|
public interface ThreadDetailState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get thread status in thread pool.
|
||||||
|
*
|
||||||
|
* @param threadPoolId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<ThreadDetailStateInfo> getThreadDetailStateInfo(String threadPoolId);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package cn.hippo4j.common.function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matcher.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2022/1/9 13:29
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Matcher<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match.
|
||||||
|
*
|
||||||
|
* @param t
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean match(T t);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package cn.hippo4j.common.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread detail state info.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2022/1/9 12:36
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class ThreadDetailStateInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* threadId
|
||||||
|
*/
|
||||||
|
private Long threadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* threadName
|
||||||
|
*/
|
||||||
|
private String threadName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* threadStatus
|
||||||
|
*/
|
||||||
|
private String threadStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* threadStack
|
||||||
|
*/
|
||||||
|
private List<String> threadStack;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package cn.hippo4j.common.toolkit;
|
||||||
|
|
||||||
|
import com.sun.xml.internal.ws.util.UtilException;
|
||||||
|
|
||||||
|
import java.lang.reflect.AccessibleObject;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflect util.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2022/1/9 13:16
|
||||||
|
*/
|
||||||
|
public class ReflectUtil {
|
||||||
|
|
||||||
|
private static final Map<Class<?>, Field[]> FIELDS_CACHE = new ConcurrentHashMap();
|
||||||
|
|
||||||
|
public static Object getFieldValue(Object obj, String fieldName) throws UtilException {
|
||||||
|
if (null == obj || StringUtil.isBlank(fieldName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field field = getField(obj instanceof Class ? (Class<?>) obj : obj.getClass(), fieldName);
|
||||||
|
return getFieldValue(obj, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getFieldValue(Object obj, Field field) throws UtilException {
|
||||||
|
if (null == field) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (obj instanceof Class) {
|
||||||
|
obj = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAccessible(field);
|
||||||
|
Object result;
|
||||||
|
try {
|
||||||
|
result = field.get(obj);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
String exceptionMsg = String.format("IllegalAccess for %s.%s", field.getDeclaringClass(), field.getName());
|
||||||
|
throw new RuntimeException(exceptionMsg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends AccessibleObject> T setAccessible(T accessibleObject) {
|
||||||
|
if (null != accessibleObject && false == accessibleObject.isAccessible()) {
|
||||||
|
accessibleObject.setAccessible(true);
|
||||||
|
}
|
||||||
|
return accessibleObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field getField(Class<?> beanClass, String name) throws SecurityException {
|
||||||
|
final Field[] fields = getFields(beanClass);
|
||||||
|
return ArrayUtil.firstMatch((field) -> name.equals(getFieldName(field)), fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field[] getFields(Class<?> beanClass) throws SecurityException {
|
||||||
|
Field[] allFields = FIELDS_CACHE.get(beanClass);
|
||||||
|
if (null != allFields) {
|
||||||
|
return allFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
allFields = getFieldsDirectly(beanClass, true);
|
||||||
|
FIELDS_CACHE.put(beanClass, allFields);
|
||||||
|
return allFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field[] getFieldsDirectly(Class<?> beanClass, boolean withSuperClassFields) throws SecurityException {
|
||||||
|
Assert.notNull(beanClass);
|
||||||
|
|
||||||
|
Field[] allFields = null;
|
||||||
|
Class<?> searchType = beanClass;
|
||||||
|
Field[] declaredFields;
|
||||||
|
while (searchType != null) {
|
||||||
|
declaredFields = searchType.getDeclaredFields();
|
||||||
|
if (null == allFields) {
|
||||||
|
allFields = declaredFields;
|
||||||
|
} else {
|
||||||
|
int length = allFields.length;
|
||||||
|
allFields = Arrays.copyOf(allFields, length + declaredFields.length);
|
||||||
|
for (int i = 1; i < declaredFields.length; i++) {
|
||||||
|
allFields[length + i] = declaredFields[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchType = withSuperClassFields ? searchType.getSuperclass() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFieldName(Field field) {
|
||||||
|
if (null == field) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package cn.hippo4j.starter.handler;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.api.ThreadDetailState;
|
||||||
|
import cn.hippo4j.common.model.ThreadDetailStateInfo;
|
||||||
|
import cn.hippo4j.common.toolkit.CollectionUtil;
|
||||||
|
import cn.hippo4j.common.toolkit.ReflectUtil;
|
||||||
|
import cn.hippo4j.starter.core.GlobalThreadPoolManage;
|
||||||
|
import cn.hippo4j.starter.wrapper.DynamicThreadPoolWrapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base thread detail state handler.
|
||||||
|
*
|
||||||
|
* <p>The Java 8 implementation is temporarily provided,
|
||||||
|
* {@link ThreadDetailState} interface can be customized.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2022/1/9 13:01
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class BaseThreadDetailStateHandler implements ThreadDetailState {
|
||||||
|
|
||||||
|
private final String WORKERS = "workers";
|
||||||
|
|
||||||
|
private final String THREAD = "thread";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ThreadDetailStateInfo> getThreadDetailStateInfo(String threadPoolId) {
|
||||||
|
DynamicThreadPoolWrapper poolWrapper = GlobalThreadPoolManage.getExecutorService(threadPoolId);
|
||||||
|
ThreadPoolExecutor executor = poolWrapper.getExecutor();
|
||||||
|
|
||||||
|
List<ThreadDetailStateInfo> resultThreadState = new ArrayList();
|
||||||
|
try {
|
||||||
|
// TODO: Should the object be copied deeply to avoid the destruction of the worker
|
||||||
|
HashSet<Object> workers = (HashSet<Object>) ReflectUtil.getFieldValue(executor, WORKERS);
|
||||||
|
if (CollectionUtil.isEmpty(workers)) {
|
||||||
|
return resultThreadState;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object worker : workers) {
|
||||||
|
Thread thread;
|
||||||
|
try {
|
||||||
|
thread = (Thread) ReflectUtil.getFieldValue(worker, THREAD);
|
||||||
|
if (thread == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
long threadId = thread.getId();
|
||||||
|
String threadName = thread.getName();
|
||||||
|
String threadStatus = thread.getState().name();
|
||||||
|
StackTraceElement[] stackTrace = thread.getStackTrace();
|
||||||
|
List<String> stacks = new ArrayList(stackTrace.length);
|
||||||
|
for (int i = 0; i < stackTrace.length; i++) {
|
||||||
|
stacks.add(stackTrace[i].toString());
|
||||||
|
}
|
||||||
|
ThreadDetailStateInfo threadState = new ThreadDetailStateInfo();
|
||||||
|
threadState.setThreadId(threadId)
|
||||||
|
.setThreadName(threadName)
|
||||||
|
.setThreadStatus(threadStatus)
|
||||||
|
.setThreadStack(stacks);
|
||||||
|
resultThreadState.add(threadState);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("Failed to get thread status.", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultThreadState;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in new issue