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