mirror of https://github.com/longtai-cn/hippo4j
commit
59c15dba5d
@ -1,86 +0,0 @@
|
||||
package com.github.dynamic.threadpool.auth.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.github.dynamic.threadpool.auth.mapper.UserMapper;
|
||||
import com.github.dynamic.threadpool.auth.model.UserInfo;
|
||||
import com.github.dynamic.threadpool.auth.model.biz.user.UserQueryPageReqDTO;
|
||||
import com.github.dynamic.threadpool.auth.model.biz.user.UserRespDTO;
|
||||
import com.github.dynamic.threadpool.auth.service.UserService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* User service impl.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/10/30 21:40
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private final UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public IPage<UserRespDTO> listUser(int pageNo, int pageSize) {
|
||||
UserQueryPageReqDTO queryPage = new UserQueryPageReqDTO(pageNo, pageSize);
|
||||
IPage<UserInfo> selectPage = userMapper.selectPage(queryPage, null);
|
||||
|
||||
return selectPage.convert(each -> BeanUtil.toBean(each, UserRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUser(String userName, String password) {
|
||||
LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery(UserInfo.class)
|
||||
.eq(UserInfo::getUserName, userName);
|
||||
UserInfo existUserInfo = userMapper.selectOne(queryWrapper);
|
||||
if (existUserInfo != null) {
|
||||
throw new RuntimeException("用户名重复");
|
||||
}
|
||||
|
||||
UserInfo insertUser = new UserInfo();
|
||||
insertUser.setUserName(userName);
|
||||
// TODO 暂定为 Md5 加密
|
||||
insertUser.setPassword(SecureUtil.md5(password));
|
||||
userMapper.insert(insertUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUser(String userName, String password) {
|
||||
UserInfo userInfo = new UserInfo();
|
||||
userInfo.setUserName(userName);
|
||||
userInfo.setPassword(SecureUtil.md5(password));
|
||||
|
||||
LambdaUpdateWrapper<UserInfo> updateWrapper = Wrappers.lambdaUpdate(UserInfo.class)
|
||||
.eq(UserInfo::getUserName, userName);
|
||||
userMapper.update(userInfo, updateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUser(String userName) {
|
||||
LambdaUpdateWrapper<UserInfo> updateWrapper = Wrappers.lambdaUpdate(UserInfo.class)
|
||||
.eq(UserInfo::getUserName, userName);
|
||||
userMapper.delete(updateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserLikeUsername(String userName) {
|
||||
LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery(UserInfo.class)
|
||||
.like(UserInfo::getUserName, userName)
|
||||
.select(UserInfo::getUserName);
|
||||
|
||||
List<UserInfo> userInfos = userMapper.selectList(queryWrapper);
|
||||
List<String> userNames = userInfos.stream().map(UserInfo::getUserName).collect(Collectors.toList());
|
||||
|
||||
return userNames;
|
||||
}
|
||||
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
package com.github.dynamic.threadpool.common.toolkit;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Group key.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/24 21:12
|
||||
*/
|
||||
public class GroupKey {
|
||||
|
||||
public static String getKey(String dataId, String group) {
|
||||
return getKey(dataId, group, "");
|
||||
}
|
||||
|
||||
public static String getKey(String dataId, String group, String datumStr) {
|
||||
return doGetKey(dataId, group, datumStr);
|
||||
}
|
||||
|
||||
public static String getKeyTenant(String dataId, String group, String tenant) {
|
||||
return doGetKey(dataId, group, tenant);
|
||||
}
|
||||
|
||||
private static String doGetKey(String dataId, String group, String datumStr) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
urlEncode(dataId, sb);
|
||||
sb.append('+');
|
||||
urlEncode(group, sb);
|
||||
if (!StringUtils.isEmpty(datumStr)) {
|
||||
sb.append('+');
|
||||
urlEncode(datumStr, sb);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String[] parseKey(String groupKey) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String dataId = null;
|
||||
String group = null;
|
||||
String tenant = null;
|
||||
|
||||
for (int i = 0; i < groupKey.length(); ++i) {
|
||||
char c = groupKey.charAt(i);
|
||||
if ('+' == c) {
|
||||
if (null == dataId) {
|
||||
dataId = sb.toString();
|
||||
sb.setLength(0);
|
||||
} else if (null == group) {
|
||||
group = sb.toString();
|
||||
sb.setLength(0);
|
||||
} else {
|
||||
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
|
||||
}
|
||||
} else if ('%' == c) {
|
||||
char next = groupKey.charAt(++i);
|
||||
char nextnext = groupKey.charAt(++i);
|
||||
if ('2' == next && 'B' == nextnext) {
|
||||
sb.append('+');
|
||||
} else if ('2' == next && '5' == nextnext) {
|
||||
sb.append('%');
|
||||
} else {
|
||||
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(group)) {
|
||||
group = sb.toString();
|
||||
if (group.length() == 0) {
|
||||
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
|
||||
}
|
||||
} else {
|
||||
tenant = sb.toString();
|
||||
if (group.length() == 0) {
|
||||
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
|
||||
}
|
||||
}
|
||||
|
||||
return new String[]{dataId, group, tenant};
|
||||
}
|
||||
|
||||
public static void urlEncode(String str, StringBuilder sb) {
|
||||
for (int idx = 0; idx < str.length(); ++idx) {
|
||||
char c = str.charAt(idx);
|
||||
if ('+' == c) {
|
||||
sb.append("%2B");
|
||||
} else if ('%' == c) {
|
||||
sb.append("%25");
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package com.github.dynamic.threadpool.common.web.base;
|
||||
|
||||
import com.github.dynamic.threadpool.common.web.exception.ErrorCodeEnum;
|
||||
import com.github.dynamic.threadpool.common.web.exception.ServiceException;
|
||||
|
||||
/**
|
||||
* Results.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/3/19 16:12
|
||||
*/
|
||||
public final class Results {
|
||||
|
||||
public static Result<Void> success() {
|
||||
return new Result<Void>()
|
||||
.setCode(Result.SUCCESS_CODE);
|
||||
}
|
||||
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<T>()
|
||||
.setCode(Result.SUCCESS_CODE)
|
||||
.setData(data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> failure(ServiceException serviceException) {
|
||||
return new Result<T>().setCode(ErrorCodeEnum.SERVICE_ERROR.getCode())
|
||||
.setMessage(serviceException.getMessage());
|
||||
}
|
||||
|
||||
public static <T> Result<T> failure(String code, String message) {
|
||||
return new Result<T>()
|
||||
.setCode(code)
|
||||
.setMessage(message);
|
||||
}
|
||||
|
||||
public static <T> Result<T> failure(ErrorCodeEnum errorCode) {
|
||||
return new Result<T>()
|
||||
.setCode(errorCode.getCode())
|
||||
.setMessage(errorCode.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package com.github.dynamic.threadpool.common.web.exception;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* Service exception.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/3/19 16:14
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ServiceException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -8667394300356618037L;
|
||||
|
||||
private String detail;
|
||||
|
||||
public ServiceException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServiceException(String message, String detail) {
|
||||
super(message);
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public ServiceException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.detail = cause.getMessage();
|
||||
}
|
||||
|
||||
public ServiceException(String message, String detail, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServiceException{" +
|
||||
"message='" + getMessage() + "'," +
|
||||
"detail='" + getDetail() + "'" +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.config;
|
||||
|
||||
import com.github.dynamic.threadpool.common.config.ApplicationContextHolder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Common config.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/7/19 21:03
|
||||
*/
|
||||
@Configuration
|
||||
public class CommonConfig {
|
||||
|
||||
@Bean
|
||||
public ApplicationContextHolder simpleApplicationContextHolder() {
|
||||
return new ApplicationContextHolder();
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.event;
|
||||
|
||||
/**
|
||||
* Local data change event.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/23 19:13
|
||||
*/
|
||||
public class LocalDataChangeEvent extends Event {
|
||||
|
||||
public final String groupKey;
|
||||
|
||||
public LocalDataChangeEvent(String groupKey) {
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.notify;
|
||||
|
||||
import cn.hutool.core.collection.ConcurrentHashSet;
|
||||
import com.github.dynamic.threadpool.config.notify.listener.Subscriber;
|
||||
import com.github.dynamic.threadpool.config.event.Event;
|
||||
import com.github.dynamic.threadpool.config.event.SlowEvent;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Default share publisher.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/23 19:05
|
||||
*/
|
||||
public class DefaultSharePublisher extends DefaultPublisher {
|
||||
|
||||
private final Map<Class<? extends SlowEvent>, Set<Subscriber>> subMappings = new ConcurrentHashMap();
|
||||
|
||||
protected final ConcurrentHashSet<Subscriber> subscribers = new ConcurrentHashSet();
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
public void addSubscriber(Subscriber subscriber, Class<? extends Event> subscribeType) {
|
||||
Class<? extends SlowEvent> subSlowEventType = (Class<? extends SlowEvent>) subscribeType;
|
||||
subscribers.add(subscriber);
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
Set<Subscriber> sets = subMappings.get(subSlowEventType);
|
||||
if (sets == null) {
|
||||
Set<Subscriber> newSet = new ConcurrentHashSet();
|
||||
newSet.add(subscriber);
|
||||
subMappings.put(subSlowEventType, newSet);
|
||||
return;
|
||||
}
|
||||
sets.add(subscriber);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.notify.listener;
|
||||
|
||||
import com.github.dynamic.threadpool.config.event.Event;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Subscribers to multiple events can be listened to.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/23 19:02
|
||||
*/
|
||||
public abstract class SmartSubscriber extends Subscriber {
|
||||
|
||||
/**
|
||||
* Subscribe types.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract List<Class<? extends Event>> subscribeTypes();
|
||||
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Config servlet inner.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/22 23:13
|
||||
*/
|
||||
@Service
|
||||
public class ConfigServletInner {
|
||||
|
||||
@Autowired
|
||||
private LongPollingService longPollingService;
|
||||
|
||||
public String doPollingConfig(HttpServletRequest request, HttpServletResponse response, Map<String, String> clientMd5Map, int probeRequestSize) {
|
||||
if (LongPollingService.isSupportLongPolling(request)) {
|
||||
longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize);
|
||||
return HttpServletResponse.SC_OK + "";
|
||||
}
|
||||
return HttpServletResponse.SC_OK + "";
|
||||
}
|
||||
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.service.biz.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
import com.github.dynamic.threadpool.common.toolkit.ContentUtil;
|
||||
import com.github.dynamic.threadpool.common.toolkit.Md5Util;
|
||||
import com.github.dynamic.threadpool.config.event.LocalDataChangeEvent;
|
||||
import com.github.dynamic.threadpool.config.mapper.ConfigInfoMapper;
|
||||
import com.github.dynamic.threadpool.config.model.ConfigAllInfo;
|
||||
import com.github.dynamic.threadpool.config.service.ConfigChangePublisher;
|
||||
import com.github.dynamic.threadpool.config.service.biz.ConfigService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Config service impl.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/20 15:21
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class ConfigServiceImpl implements ConfigService {
|
||||
|
||||
private final ConfigInfoMapper configInfoMapper;
|
||||
|
||||
@Override
|
||||
public ConfigAllInfo findConfigAllInfo(String tpId, String itemId, String tenantId) {
|
||||
LambdaQueryWrapper<ConfigAllInfo> wrapper = Wrappers.lambdaQuery(ConfigAllInfo.class)
|
||||
.eq(!StringUtils.isBlank(tpId), ConfigAllInfo::getTpId, tpId)
|
||||
.eq(!StringUtils.isBlank(itemId), ConfigAllInfo::getItemId, itemId)
|
||||
.eq(!StringUtils.isBlank(tenantId), ConfigAllInfo::getTenantId, tenantId);
|
||||
ConfigAllInfo configAllInfo = configInfoMapper.selectOne(wrapper);
|
||||
return configAllInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertOrUpdate(ConfigAllInfo configAllInfo) {
|
||||
try {
|
||||
addConfigInfo(configAllInfo);
|
||||
} catch (Exception ex) {
|
||||
updateConfigInfo(configAllInfo);
|
||||
}
|
||||
|
||||
ConfigChangePublisher.notifyConfigChange(new LocalDataChangeEvent(ContentUtil.getGroupKey(configAllInfo)));
|
||||
}
|
||||
|
||||
private Integer addConfigInfo(ConfigAllInfo config) {
|
||||
config.setContent(ContentUtil.getPoolContent(config));
|
||||
config.setMd5(Md5Util.getTpContentMd5(config));
|
||||
try {
|
||||
if (SqlHelper.retBool(configInfoMapper.insert(config))) {
|
||||
return config.getId();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("[db-error] message :: {}", ex.getMessage(), ex);
|
||||
throw ex;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updateConfigInfo(ConfigAllInfo config) {
|
||||
LambdaUpdateWrapper<ConfigAllInfo> wrapper = Wrappers.lambdaUpdate(ConfigAllInfo.class)
|
||||
.eq(ConfigAllInfo::getTpId, config.getTpId())
|
||||
.eq(ConfigAllInfo::getItemId, config.getItemId())
|
||||
.eq(ConfigAllInfo::getTenantId, config.getTenantId());
|
||||
|
||||
config.setGmtCreate(null);
|
||||
config.setContent(ContentUtil.getPoolContent(config));
|
||||
config.setMd5(Md5Util.getTpContentMd5(config));
|
||||
|
||||
try {
|
||||
configInfoMapper.update(config, wrapper);
|
||||
} catch (Exception ex) {
|
||||
log.error("[db-error] message :: {}", ex.getMessage(), ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.service.biz.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.github.dynamic.threadpool.config.enums.DelEnum;
|
||||
import com.github.dynamic.threadpool.config.mapper.ConfigInfoMapper;
|
||||
import com.github.dynamic.threadpool.config.model.ConfigAllInfo;
|
||||
import com.github.dynamic.threadpool.config.model.biz.threadpool.ThreadPoolQueryReqDTO;
|
||||
import com.github.dynamic.threadpool.config.model.biz.threadpool.ThreadPoolRespDTO;
|
||||
import com.github.dynamic.threadpool.config.model.biz.threadpool.ThreadPoolSaveOrUpdateReqDTO;
|
||||
import com.github.dynamic.threadpool.config.service.biz.ConfigService;
|
||||
import com.github.dynamic.threadpool.config.service.biz.ThreadPoolService;
|
||||
import com.github.dynamic.threadpool.config.toolkit.BeanUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Thread pool service impl.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/30 21:26
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class ThreadPoolServiceImpl implements ThreadPoolService {
|
||||
|
||||
private final ConfigService configService;
|
||||
|
||||
private final ConfigInfoMapper configInfoMapper;
|
||||
|
||||
@Override
|
||||
public IPage<ThreadPoolRespDTO> queryThreadPoolPage(ThreadPoolQueryReqDTO reqDTO) {
|
||||
LambdaQueryWrapper<ConfigAllInfo> wrapper = Wrappers.lambdaQuery(ConfigAllInfo.class)
|
||||
.eq(!StringUtils.isBlank(reqDTO.getTenantId()), ConfigAllInfo::getTenantId, reqDTO.getTenantId())
|
||||
.eq(!StringUtils.isBlank(reqDTO.getItemId()), ConfigAllInfo::getItemId, reqDTO.getItemId())
|
||||
.eq(!StringUtils.isBlank(reqDTO.getTpId()), ConfigAllInfo::getTpId, reqDTO.getTpId())
|
||||
.eq(ConfigAllInfo::getDelFlag, DelEnum.NORMAL);
|
||||
return configInfoMapper.selectPage(reqDTO, wrapper).convert(each -> BeanUtil.convert(each, ThreadPoolRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadPoolRespDTO getThreadPool(ThreadPoolQueryReqDTO reqDTO) {
|
||||
ConfigAllInfo configAllInfo = configService.findConfigAllInfo(reqDTO.getTpId(), reqDTO.getItemId(), reqDTO.getTenantId());
|
||||
return BeanUtil.convert(configAllInfo, ThreadPoolRespDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThreadPoolRespDTO> getThreadPoolByItemId(String itemId) {
|
||||
List<ConfigAllInfo> selectList = configInfoMapper
|
||||
.selectList(Wrappers.lambdaUpdate(ConfigAllInfo.class).eq(ConfigAllInfo::getItemId, itemId));
|
||||
return BeanUtil.convert(selectList, ThreadPoolRespDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrUpdateThreadPoolConfig(ThreadPoolSaveOrUpdateReqDTO reqDTO) {
|
||||
configService.insertOrUpdate(BeanUtil.convert(reqDTO, ConfigAllInfo.class));
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.toolkit;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Map util.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/23 19:09
|
||||
*/
|
||||
public class MapUtil {
|
||||
|
||||
public static Object computeIfAbsent(Map target, Object key, BiFunction mappingFunction, Object param1, Object param2) {
|
||||
Objects.requireNonNull(target, "target");
|
||||
Objects.requireNonNull(key, "key");
|
||||
Objects.requireNonNull(mappingFunction, "mappingFunction");
|
||||
Objects.requireNonNull(param1, "param1");
|
||||
Objects.requireNonNull(param2, "param2");
|
||||
|
||||
Object val = target.get(key);
|
||||
if (val == null) {
|
||||
Object ret = mappingFunction.apply(param1, param2);
|
||||
target.put(key, ret);
|
||||
return ret;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package com.github.dynamic.threadpool.config.toolkit;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Request util.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/23 18:28
|
||||
*/
|
||||
public class RequestUtil {
|
||||
|
||||
private static final String X_REAL_IP = "X-Real-IP";
|
||||
|
||||
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
|
||||
|
||||
private static final String X_FORWARDED_FOR_SPLIT_SYMBOL = ",";
|
||||
|
||||
public static String getRemoteIp(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader(X_FORWARDED_FOR);
|
||||
if (!StringUtils.isEmpty(xForwardedFor)) {
|
||||
return xForwardedFor.split(X_FORWARDED_FOR_SPLIT_SYMBOL)[0].trim();
|
||||
}
|
||||
String nginxHeader = request.getHeader(X_REAL_IP);
|
||||
return StringUtils.isEmpty(nginxHeader) ? request.getRemoteAddr() : nginxHeader;
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.github.dynamic-threadpool</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>console</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>${project.artifactId}</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.dynamic-threadpool</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.dynamic-threadpool</groupId>
|
||||
<artifactId>discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.dynamic-threadpool</groupId>
|
||||
<artifactId>auth</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -1,56 +0,0 @@
|
||||
package com.github.dynamic.threadpool.console.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.dynamic.threadpool.common.constant.Constants;
|
||||
import com.github.dynamic.threadpool.common.web.base.Result;
|
||||
import com.github.dynamic.threadpool.common.web.base.Results;
|
||||
import com.github.dynamic.threadpool.config.model.biz.tenant.TenantQueryReqDTO;
|
||||
import com.github.dynamic.threadpool.config.model.biz.tenant.TenantRespDTO;
|
||||
import com.github.dynamic.threadpool.config.model.biz.tenant.TenantSaveReqDTO;
|
||||
import com.github.dynamic.threadpool.config.model.biz.tenant.TenantUpdateReqDTO;
|
||||
import com.github.dynamic.threadpool.config.service.biz.TenantService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* Tenant controller.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/25 18:31
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping(Constants.BASE_PATH + "/tenant")
|
||||
public class TenantController {
|
||||
|
||||
private final TenantService tenantService;
|
||||
|
||||
@PostMapping("/query/page")
|
||||
public Result<IPage<TenantRespDTO>> queryNameSpacePage(@RequestBody TenantQueryReqDTO reqDTO) {
|
||||
return Results.success(tenantService.queryTenantPage(reqDTO));
|
||||
}
|
||||
|
||||
@GetMapping("/query/{tenantId}")
|
||||
public Result<TenantRespDTO> queryNameSpace(@PathVariable("tenantId") String tenantId) {
|
||||
return Results.success(tenantService.getTenantByTenantId(tenantId));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public Result saveNameSpace(@RequestBody TenantSaveReqDTO reqDTO) {
|
||||
tenantService.saveTenant(reqDTO);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@PostMapping("/update")
|
||||
public Result updateNameSpace(@RequestBody TenantUpdateReqDTO reqDTO) {
|
||||
tenantService.updateTenant(reqDTO);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete/{tenantId}")
|
||||
public Result deleteNameSpace(@PathVariable("tenantId") String tenantId) {
|
||||
tenantService.deleteTenantById(tenantId);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package com.github.dynamic.threadpool.console.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.dynamic.threadpool.common.constant.Constants;
|
||||
import com.github.dynamic.threadpool.common.model.InstanceInfo;
|
||||
import com.github.dynamic.threadpool.common.web.base.Result;
|
||||
import com.github.dynamic.threadpool.common.web.base.Results;
|
||||
import com.github.dynamic.threadpool.config.model.biz.threadpool.ThreadPoolQueryReqDTO;
|
||||
import com.github.dynamic.threadpool.config.model.biz.threadpool.ThreadPoolRespDTO;
|
||||
import com.github.dynamic.threadpool.config.model.biz.threadpool.ThreadPoolSaveOrUpdateReqDTO;
|
||||
import com.github.dynamic.threadpool.config.service.biz.ThreadPoolService;
|
||||
import com.github.dynamic.threadpool.discovery.core.BaseInstanceRegistry;
|
||||
import com.github.dynamic.threadpool.discovery.core.Lease;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Thread pool controller.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/30 20:54
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping(Constants.BASE_PATH + "/thread")
|
||||
public class ThreadPoolController {
|
||||
|
||||
private final ThreadPoolService threadPoolService;
|
||||
|
||||
private final BaseInstanceRegistry baseInstanceRegistry;
|
||||
|
||||
@PostMapping("/pool/query/page")
|
||||
public Result<IPage<ThreadPoolRespDTO>> queryNameSpacePage(@RequestBody ThreadPoolQueryReqDTO reqDTO) {
|
||||
return Results.success(threadPoolService.queryThreadPoolPage(reqDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/pool/query")
|
||||
public Result<ThreadPoolRespDTO> queryNameSpace(@RequestBody ThreadPoolQueryReqDTO reqDTO) {
|
||||
return Results.success(threadPoolService.getThreadPool(reqDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/pool/save_or_update")
|
||||
public Result saveOrUpdateThreadPoolConfig(@RequestBody ThreadPoolSaveOrUpdateReqDTO reqDTO) {
|
||||
threadPoolService.saveOrUpdateThreadPoolConfig(reqDTO);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@GetMapping("/pool/list/instance/{itemId}")
|
||||
public Result<List<Lease<InstanceInfo>>> listInstance(@PathVariable("itemId") String itemId) {
|
||||
List<Lease<InstanceInfo>> leases = baseInstanceRegistry.listInstance(itemId);
|
||||
return Results.success(leases);
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package com.github.dynamic.threadpool.console.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.dynamic.threadpool.auth.model.biz.user.UserRespDTO;
|
||||
import com.github.dynamic.threadpool.auth.service.UserService;
|
||||
import com.github.dynamic.threadpool.common.web.base.Result;
|
||||
import com.github.dynamic.threadpool.common.web.base.Results;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User controller.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/10/30 21:15
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping({"/v1/auth", "/v1/auth/users"})
|
||||
public class UserController {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
@GetMapping("/{pageNo}/{pageSize}")
|
||||
public Result<IPage<UserRespDTO>> listUser(@PathVariable("pageNo") int pageNo, @PathVariable("pageSize") int pageSize) {
|
||||
IPage<UserRespDTO> resultUserPage = userService.listUser(pageNo, pageSize);
|
||||
return Results.success(resultUserPage);
|
||||
}
|
||||
|
||||
@PostMapping("/{userName}/{password}")
|
||||
public Result<Void> addUser(@PathVariable("userName") String userName, @PathVariable("password") String password) {
|
||||
userService.addUser(userName, password);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@PutMapping("/{userName}/{password}")
|
||||
public Result<Void> updateUser(@PathVariable("userName") String userName, @PathVariable("password") String password) {
|
||||
userService.updateUser(userName, password);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{userName}")
|
||||
public Result<Void> deleteUser(@PathVariable("userName") String userName) {
|
||||
userService.deleteUser(userName);
|
||||
return Results.success();
|
||||
}
|
||||
|
||||
@GetMapping("/search/{userName}")
|
||||
public Result<List<String>> searchUsersLikeUserName(@PathVariable("userName") String userName) {
|
||||
List<String> resultUserNames = userService.getUserLikeUsername(userName);
|
||||
return Results.success(resultUserNames);
|
||||
}
|
||||
|
||||
}
|
@ -1,289 +0,0 @@
|
||||
package com.github.dynamic.threadpool.discovery.core;
|
||||
|
||||
import com.github.dynamic.threadpool.common.model.InstanceInfo;
|
||||
import com.github.dynamic.threadpool.common.model.InstanceInfo.InstanceStatus;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import static com.github.dynamic.threadpool.common.constant.Constants.EVICTION_INTERVAL_TIMER_IN_MS;
|
||||
import static com.github.dynamic.threadpool.common.constant.Constants.SCHEDULED_THREAD_CORE_NUM;
|
||||
|
||||
/**
|
||||
* Base instance registry.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/8 22:46
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class BaseInstanceRegistry implements InstanceRegistry<InstanceInfo> {
|
||||
|
||||
private final int CONTAINER_SIZE = 1024;
|
||||
|
||||
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
|
||||
private final Lock read = readWriteLock.readLock();
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry = new ConcurrentHashMap(CONTAINER_SIZE);
|
||||
|
||||
protected volatile int expectedNumberOfClientsSendingRenews;
|
||||
|
||||
private final CircularQueue<Pair<Long, String>> recentRegisteredQueue;
|
||||
|
||||
private final CircularQueue<Pair<Long, String>> recentCanceledQueue;
|
||||
|
||||
private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue();
|
||||
|
||||
protected final ConcurrentMap<String, InstanceStatus> overriddenInstanceStatusMap = CacheBuilder
|
||||
.newBuilder().initialCapacity(512)
|
||||
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||
.<String, InstanceStatus>build().asMap();
|
||||
|
||||
public BaseInstanceRegistry() {
|
||||
this.recentRegisteredQueue = new CircularQueue(CONTAINER_SIZE);
|
||||
this.recentCanceledQueue = new CircularQueue(CONTAINER_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Lease<InstanceInfo>> listInstance(String appName) {
|
||||
Map<String, Lease<InstanceInfo>> appNameLeaseMap = registry.get(appName);
|
||||
if (CollectionUtils.isEmpty(appNameLeaseMap)) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
List<Lease<InstanceInfo>> appNameLeaseList = Lists.newArrayList();
|
||||
appNameLeaseMap.values().forEach(each -> appNameLeaseList.add(each));
|
||||
return appNameLeaseList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(InstanceInfo registrant) {
|
||||
read.lock();
|
||||
try {
|
||||
Map<String, Lease<InstanceInfo>> registerMap = registry.get(registrant.getAppName());
|
||||
if (registerMap == null) {
|
||||
ConcurrentHashMap<String, Lease<InstanceInfo>> registerNewMap = new ConcurrentHashMap(12);
|
||||
registerMap = registry.putIfAbsent(registrant.getAppName(), registerNewMap);
|
||||
if (registerMap == null) {
|
||||
registerMap = registerNewMap;
|
||||
}
|
||||
}
|
||||
|
||||
Lease<InstanceInfo> existingLease = registerMap.get(registrant.getInstanceId());
|
||||
if (existingLease != null && (existingLease.getHolder() != null)) {
|
||||
Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
|
||||
Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
|
||||
|
||||
if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
|
||||
registrant = existingLease.getHolder();
|
||||
}
|
||||
}
|
||||
|
||||
Lease<InstanceInfo> lease = new Lease(registrant);
|
||||
if (existingLease != null) {
|
||||
lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
|
||||
}
|
||||
registerMap.put(registrant.getInstanceId(), lease);
|
||||
|
||||
recentRegisteredQueue.add(new Pair(
|
||||
System.currentTimeMillis(),
|
||||
registrant.getAppName() + "(" + registrant.getInstanceId() + ")"));
|
||||
|
||||
InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getInstanceId());
|
||||
if (overriddenStatusFromMap != null) {
|
||||
log.info("Storing overridden status :: {} from map", overriddenStatusFromMap);
|
||||
registrant.setOverriddenStatus(overriddenStatusFromMap);
|
||||
}
|
||||
|
||||
if (InstanceStatus.UP.equals(registrant.getStatus())) {
|
||||
lease.serviceUp();
|
||||
}
|
||||
|
||||
registrant.setActionType(InstanceInfo.ActionType.ADDED);
|
||||
recentlyChangedQueue.add(new RecentlyChangedItem(lease));
|
||||
registrant.setLastUpdatedTimestamp();
|
||||
} finally {
|
||||
read.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean renew(InstanceInfo.InstanceRenew instanceRenew) {
|
||||
String appName = instanceRenew.getAppName();
|
||||
String instanceId = instanceRenew.getInstanceId();
|
||||
|
||||
Map<String, Lease<InstanceInfo>> registryMap = registry.get(appName);
|
||||
Lease<InstanceInfo> leaseToRenew = null;
|
||||
if (registryMap == null || (leaseToRenew = registryMap.get(instanceId)) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
leaseToRenew.renew();
|
||||
return true;
|
||||
}
|
||||
|
||||
static class CircularQueue<E> extends AbstractQueue<E> {
|
||||
|
||||
private final ArrayBlockingQueue<E> delegate;
|
||||
|
||||
public CircularQueue(int capacity) {
|
||||
this.delegate = new ArrayBlockingQueue(capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return delegate.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E e) {
|
||||
while (!delegate.offer(e)) {
|
||||
delegate.poll();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E poll() {
|
||||
return delegate.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peek() {
|
||||
return delegate.peek();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return delegate.toArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class RecentlyChangedItem {
|
||||
private long lastUpdateTime;
|
||||
|
||||
private Lease<InstanceInfo> leaseInfo;
|
||||
|
||||
public RecentlyChangedItem(Lease<InstanceInfo> lease) {
|
||||
this.leaseInfo = lease;
|
||||
lastUpdateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getLastUpdateTime() {
|
||||
return this.lastUpdateTime;
|
||||
}
|
||||
|
||||
public Lease<InstanceInfo> getLeaseInfo() {
|
||||
return this.leaseInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public void evict(long additionalLeaseMs) {
|
||||
List<Lease<InstanceInfo>> expiredLeases = new ArrayList();
|
||||
for (Map.Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) {
|
||||
Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue();
|
||||
if (leaseMap != null) {
|
||||
for (Map.Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) {
|
||||
Lease<InstanceInfo> lease = leaseEntry.getValue();
|
||||
if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) {
|
||||
expiredLeases.add(lease);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Lease<InstanceInfo> expiredLease : expiredLeases) {
|
||||
String appName = expiredLease.getHolder().getAppName();
|
||||
String id = expiredLease.getHolder().getInstanceId();
|
||||
internalCancel(appName, id, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean internalCancel(String appName, String id, boolean isReplication) {
|
||||
read.lock();
|
||||
try {
|
||||
Map<String, Lease<InstanceInfo>> registerMap = registry.get(appName);
|
||||
if (!CollectionUtils.isEmpty(registerMap)) {
|
||||
registerMap.remove(id);
|
||||
}
|
||||
} finally {
|
||||
read.unlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public class EvictionTask extends TimerTask {
|
||||
|
||||
private final AtomicLong lastExecutionNanosRef = new AtomicLong(0L);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long compensationTimeMs = getCompensationTimeMs();
|
||||
log.info("Running the evict task with compensationTime {} ms", compensationTimeMs);
|
||||
evict(compensationTimeMs);
|
||||
} catch (Throwable e) {
|
||||
log.error("Could not run the evict task", e);
|
||||
}
|
||||
}
|
||||
|
||||
long getCompensationTimeMs() {
|
||||
long currNanos = getCurrentTimeNano();
|
||||
long lastNanos = lastExecutionNanosRef.getAndSet(currNanos);
|
||||
if (lastNanos == 0L) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
long elapsedMs = TimeUnit.NANOSECONDS.toMillis(currNanos - lastNanos);
|
||||
long compensationTime = elapsedMs - EVICTION_INTERVAL_TIMER_IN_MS;
|
||||
return compensationTime <= 0L ? 0L : compensationTime;
|
||||
}
|
||||
|
||||
long getCurrentTimeNano() {
|
||||
return System.nanoTime();
|
||||
}
|
||||
}
|
||||
|
||||
private final ScheduledExecutorService scheduledExecutorService =
|
||||
new ScheduledThreadPoolExecutor(
|
||||
SCHEDULED_THREAD_CORE_NUM,
|
||||
new ThreadFactoryBuilder()
|
||||
.setNameFormat("registry-eviction")
|
||||
.setDaemon(true)
|
||||
.build()
|
||||
);
|
||||
|
||||
private final AtomicReference<EvictionTask> evictionTaskRef = new AtomicReference();
|
||||
|
||||
public void postInit() {
|
||||
evictionTaskRef.set(new BaseInstanceRegistry.EvictionTask());
|
||||
scheduledExecutorService.scheduleWithFixedDelay(evictionTaskRef.get(),
|
||||
EVICTION_INTERVAL_TIMER_IN_MS, EVICTION_INTERVAL_TIMER_IN_MS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package com.github.dynamic.threadpool.discovery.core;
|
||||
|
||||
/**
|
||||
* Pair.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/8 23:04
|
||||
*/
|
||||
public class Pair<E1, E2> {
|
||||
|
||||
public E1 first() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public void setFirst(E1 first) {
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
public E2 second() {
|
||||
return second;
|
||||
}
|
||||
|
||||
public void setSecond(E2 second) {
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
private E1 first;
|
||||
|
||||
private E2 second;
|
||||
|
||||
public Pair(E1 first, E2 second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package com.github.dynamic.threadpool.discovery.core;
|
||||
|
||||
import com.github.dynamic.threadpool.common.model.InstanceInfo.InstanceStatus;
|
||||
|
||||
/**
|
||||
* Status override result.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/8 23:11
|
||||
*/
|
||||
public class StatusOverrideResult {
|
||||
|
||||
public static StatusOverrideResult NO_MATCH = new StatusOverrideResult(false, null);
|
||||
|
||||
public static StatusOverrideResult matchingStatus(InstanceStatus status) {
|
||||
return new StatusOverrideResult(true, status);
|
||||
}
|
||||
|
||||
private final boolean matches;
|
||||
|
||||
private final InstanceStatus status;
|
||||
|
||||
private StatusOverrideResult(boolean matches, InstanceStatus status) {
|
||||
this.matches = matches;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public boolean matches() {
|
||||
return matches;
|
||||
}
|
||||
|
||||
public InstanceStatus status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 报警控制组件.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/10/28 21:24
|
||||
*/
|
||||
public class AlarmControlHandler {
|
||||
|
||||
private final Cache<String, String> cache;
|
||||
|
||||
public AlarmControlHandler(long alarmInterval) {
|
||||
cache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(alarmInterval, TimeUnit.MINUTES)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制消息推送报警频率.
|
||||
*
|
||||
* @param alarmControl
|
||||
* @return
|
||||
*/
|
||||
public boolean isSend(AlarmControlDTO alarmControl) {
|
||||
String pkId = cache.getIfPresent(alarmControl.buildPk());
|
||||
|
||||
if (StrUtil.isBlank(pkId)) {
|
||||
// val 无意义
|
||||
cache.put(alarmControl.buildPk(), IdUtil.simpleUUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
import com.github.dynamic.threadpool.common.config.ApplicationContextHolder;
|
||||
import com.github.dynamic.threadpool.common.model.PoolParameterInfo;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base send message service.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 15:34
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class BaseSendMessageService implements InitializingBean, SendMessageService {
|
||||
|
||||
@NonNull
|
||||
private final List<NotifyConfig> notifyConfigs;
|
||||
|
||||
private final List<SendMessageHandler> sendMessageHandlers = new ArrayList(4);
|
||||
|
||||
@Override
|
||||
public void sendAlarmMessage(CustomThreadPoolExecutor threadPoolExecutor) {
|
||||
for (SendMessageHandler messageHandler : sendMessageHandlers) {
|
||||
try {
|
||||
messageHandler.sendAlarmMessage(notifyConfigs, threadPoolExecutor);
|
||||
} catch (Exception ex) {
|
||||
log.warn("Failed to send thread pool alarm notification.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChangeMessage(PoolParameterInfo parameter) {
|
||||
for (SendMessageHandler messageHandler : sendMessageHandlers) {
|
||||
try {
|
||||
messageHandler.sendChangeMessage(notifyConfigs, parameter);
|
||||
} catch (Exception ex) {
|
||||
log.warn("Failed to send thread pool change notification.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Map<String, SendMessageHandler> sendMessageHandlerMap =
|
||||
ApplicationContextHolder.getBeansOfType(SendMessageHandler.class);
|
||||
sendMessageHandlerMap.values().forEach(each -> sendMessageHandlers.add(each));
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
/**
|
||||
* Message type enum.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/16 20:50
|
||||
*/
|
||||
public enum MessageTypeEnum {
|
||||
|
||||
/**
|
||||
* 通知类型
|
||||
*/
|
||||
CHANGE,
|
||||
|
||||
/**
|
||||
* 容量报警
|
||||
*/
|
||||
CAPACITY,
|
||||
|
||||
/**
|
||||
* 活跃度报警
|
||||
*/
|
||||
LIVENESS,
|
||||
|
||||
/**
|
||||
* 拒绝策略报警
|
||||
*/
|
||||
REJECT
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Alarm config.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 16:09
|
||||
*/
|
||||
@Data
|
||||
public class NotifyConfig {
|
||||
|
||||
/**
|
||||
* type
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* url
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* token
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* receives
|
||||
*/
|
||||
private String receives;
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
/**
|
||||
* Send message enum.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 15:50
|
||||
*/
|
||||
public enum SendMessageEnum {
|
||||
|
||||
DING
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
import com.github.dynamic.threadpool.common.model.PoolParameterInfo;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Send message handler.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 15:44
|
||||
*/
|
||||
public interface SendMessageHandler {
|
||||
|
||||
/**
|
||||
* Get type.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* Send alarm message.
|
||||
*
|
||||
* @param notifyConfigs
|
||||
* @param threadPoolExecutor
|
||||
*/
|
||||
void sendAlarmMessage(List<NotifyConfig> notifyConfigs, CustomThreadPoolExecutor threadPoolExecutor);
|
||||
|
||||
/**
|
||||
* Send change message.
|
||||
*
|
||||
* @param notifyConfigs
|
||||
* @param parameter
|
||||
*/
|
||||
void sendChangeMessage(List<NotifyConfig> notifyConfigs, PoolParameterInfo parameter);
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
import com.github.dynamic.threadpool.common.model.PoolParameterInfo;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* Send msg.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 15:31
|
||||
*/
|
||||
public interface SendMessageService {
|
||||
|
||||
/**
|
||||
* Send alarm message.
|
||||
*
|
||||
* @param threadPoolExecutor
|
||||
*/
|
||||
void sendAlarmMessage(CustomThreadPoolExecutor threadPoolExecutor);
|
||||
|
||||
/**
|
||||
* Send change message.
|
||||
*
|
||||
* @param parameter
|
||||
*/
|
||||
void sendChangeMessage(PoolParameterInfo parameter);
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Thread pool alarm.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 13:13
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ThreadPoolAlarm {
|
||||
|
||||
/**
|
||||
* isAlarm
|
||||
*/
|
||||
private Boolean isAlarm;
|
||||
|
||||
/**
|
||||
* livenessAlarm
|
||||
*/
|
||||
private Integer livenessAlarm;
|
||||
|
||||
/**
|
||||
* capacityAlarm
|
||||
*/
|
||||
private Integer capacityAlarm;
|
||||
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.alarm;
|
||||
|
||||
import com.github.dynamic.threadpool.common.config.ApplicationContextHolder;
|
||||
import com.github.dynamic.threadpool.common.model.PoolParameterInfo;
|
||||
import com.github.dynamic.threadpool.starter.config.MessageAlarmConfig;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.CalculateUtil;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.ResizableCapacityLinkedBlockIngQueue;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Alarm manage.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 14:13
|
||||
*/
|
||||
@Slf4j
|
||||
public class ThreadPoolAlarmManage {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
private static final SendMessageService SEND_MESSAGE_SERVICE;
|
||||
|
||||
/**
|
||||
* 报警间隔控制
|
||||
*/
|
||||
private static final AlarmControlHandler ALARM_CONTROL_HANDLER;
|
||||
|
||||
static {
|
||||
SEND_MESSAGE_SERVICE = Optional.ofNullable(ApplicationContextHolder.getInstance())
|
||||
.map(each -> each.getBean(MessageAlarmConfig.SEND_MESSAGE_BEAN_NAME, SendMessageService.class))
|
||||
.orElse(null);
|
||||
|
||||
ALARM_CONTROL_HANDLER = Optional.ofNullable(ApplicationContextHolder.getInstance())
|
||||
.map(each -> each.getBean(AlarmControlHandler.class))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check thread pool capacity alarm.
|
||||
*
|
||||
* @param threadPoolExecutor
|
||||
*/
|
||||
public static void checkPoolCapacityAlarm(CustomThreadPoolExecutor threadPoolExecutor) {
|
||||
if (SEND_MESSAGE_SERVICE == null) {
|
||||
return;
|
||||
}
|
||||
ThreadPoolAlarm threadPoolAlarm = threadPoolExecutor.getThreadPoolAlarm();
|
||||
ResizableCapacityLinkedBlockIngQueue blockIngQueue =
|
||||
(ResizableCapacityLinkedBlockIngQueue) threadPoolExecutor.getQueue();
|
||||
|
||||
int queueSize = blockIngQueue.size();
|
||||
int capacity = queueSize + blockIngQueue.remainingCapacity();
|
||||
int divide = CalculateUtil.divide(queueSize, capacity);
|
||||
boolean isSend = divide > threadPoolAlarm.getCapacityAlarm()
|
||||
&& isSendMessage(threadPoolExecutor, MessageTypeEnum.CAPACITY);
|
||||
if (isSend) {
|
||||
SEND_MESSAGE_SERVICE.sendAlarmMessage(threadPoolExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check thread pool activity alarm.
|
||||
*
|
||||
* @param isCore
|
||||
* @param threadPoolExecutor
|
||||
*/
|
||||
public static void checkPoolLivenessAlarm(boolean isCore, CustomThreadPoolExecutor threadPoolExecutor) {
|
||||
if (isCore || SEND_MESSAGE_SERVICE == null || !isSendMessage(threadPoolExecutor, MessageTypeEnum.LIVENESS)) {
|
||||
return;
|
||||
}
|
||||
int activeCount = threadPoolExecutor.getActiveCount();
|
||||
int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();
|
||||
int divide = CalculateUtil.divide(activeCount, maximumPoolSize);
|
||||
|
||||
boolean isSend = divide > threadPoolExecutor.getThreadPoolAlarm().getLivenessAlarm()
|
||||
&& isSendMessage(threadPoolExecutor, MessageTypeEnum.CAPACITY);
|
||||
if (isSend) {
|
||||
SEND_MESSAGE_SERVICE.sendAlarmMessage(threadPoolExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check thread pool reject policy alarm.
|
||||
*
|
||||
* @param threadPoolExecutor
|
||||
*/
|
||||
public static void checkPoolRejectAlarm(CustomThreadPoolExecutor threadPoolExecutor) {
|
||||
if (SEND_MESSAGE_SERVICE == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSendMessage(threadPoolExecutor, MessageTypeEnum.REJECT)) {
|
||||
SEND_MESSAGE_SERVICE.sendAlarmMessage(threadPoolExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send thread pool configuration change message.
|
||||
*
|
||||
* @param parameter
|
||||
*/
|
||||
public static void sendPoolConfigChange(PoolParameterInfo parameter) {
|
||||
if (SEND_MESSAGE_SERVICE == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SEND_MESSAGE_SERVICE.sendChangeMessage(parameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is send message.
|
||||
*
|
||||
* @param threadPoolExecutor
|
||||
* @param typeEnum
|
||||
* @return
|
||||
*/
|
||||
private static boolean isSendMessage(CustomThreadPoolExecutor threadPoolExecutor, MessageTypeEnum typeEnum) {
|
||||
AlarmControlDTO alarmControl = AlarmControlDTO.builder()
|
||||
.threadPool(threadPoolExecutor.getThreadPoolId())
|
||||
.typeEnum(typeEnum)
|
||||
.build();
|
||||
return ALARM_CONTROL_HANDLER.isSend(alarmControl);
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.common;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.QueueTypeEnum;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.RejectedPolicies;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.ThreadPoolBuilder;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Common threadPool.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/16 22:35
|
||||
*/
|
||||
public class CommonThreadPool {
|
||||
|
||||
public static CustomThreadPoolExecutor getInstance(String threadPoolId) {
|
||||
CustomThreadPoolExecutor poolExecutor = (CustomThreadPoolExecutor) ThreadPoolBuilder.builder()
|
||||
.isCustomPool(true)
|
||||
.threadPoolId(threadPoolId)
|
||||
.threadFactory(threadPoolId)
|
||||
.poolThreadSize(3, 5)
|
||||
.keepAliveTime(1000L, TimeUnit.SECONDS)
|
||||
.rejected(RejectedPolicies.runsOldestTaskPolicy())
|
||||
.alarmConfig(1, 80, 80)
|
||||
.workQueue(QueueTypeEnum.RESIZABLE_LINKED_BLOCKING_QUEUE, 512)
|
||||
.build();
|
||||
return poolExecutor;
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.config;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.alarm.NotifyConfig;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bootstrap properties.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/22 09:14
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = BootstrapProperties.PREFIX)
|
||||
public class BootstrapProperties {
|
||||
|
||||
public static final String PREFIX = "spring.dynamic.thread-pool";
|
||||
|
||||
/**
|
||||
* serverAddr
|
||||
*/
|
||||
private String serverAddr;
|
||||
|
||||
/**
|
||||
* namespace
|
||||
*/
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* itemId
|
||||
*/
|
||||
private String itemId;
|
||||
|
||||
/**
|
||||
* Enable banner
|
||||
*/
|
||||
private boolean banner = true;
|
||||
|
||||
/**
|
||||
* Alarm interval
|
||||
*/
|
||||
private Long alarmInterval;
|
||||
|
||||
/**
|
||||
* notifys
|
||||
*/
|
||||
private List<NotifyConfig> notifys;
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.config;
|
||||
|
||||
import com.github.dynamic.threadpool.common.model.InstanceInfo;
|
||||
import com.github.dynamic.threadpool.starter.core.DiscoveryClient;
|
||||
import com.github.dynamic.threadpool.starter.remote.HttpAgent;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import static com.github.dynamic.threadpool.starter.toolkit.CloudCommonIdUtil.getDefaultInstanceId;
|
||||
import static com.github.dynamic.threadpool.starter.toolkit.CloudCommonIdUtil.getIpApplicationName;
|
||||
|
||||
/**
|
||||
* Dynamic threadPool discovery config.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/6 21:35
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class DiscoveryConfig {
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Bean
|
||||
@SneakyThrows
|
||||
public InstanceInfo instanceConfig() {
|
||||
InstanceInfo instanceInfo = new InstanceInfo();
|
||||
instanceInfo.setInstanceId(getDefaultInstanceId(environment))
|
||||
.setIpApplicationName(getIpApplicationName(environment))
|
||||
.setHostName(InetAddress.getLocalHost().getHostAddress())
|
||||
.setAppName(environment.getProperty("spring.application.name"))
|
||||
.setClientBasePath(environment.getProperty("server.servlet.context-path"));
|
||||
String callBackUrl = new StringBuilder().append(instanceInfo.getHostName()).append(":")
|
||||
.append(environment.getProperty("server.port")).append(instanceInfo.getClientBasePath())
|
||||
.toString();
|
||||
instanceInfo.setCallBackUrl(callBackUrl);
|
||||
|
||||
return instanceInfo;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiscoveryClient discoveryClient(HttpAgent httpAgent, InstanceInfo instanceInfo) {
|
||||
return new DiscoveryClient(httpAgent, instanceInfo);
|
||||
}
|
||||
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.config;
|
||||
|
||||
import com.github.dynamic.threadpool.common.config.ApplicationContextHolder;
|
||||
import com.github.dynamic.threadpool.starter.controller.PoolRunStateController;
|
||||
import com.github.dynamic.threadpool.starter.core.*;
|
||||
import com.github.dynamic.threadpool.starter.enable.MarkerConfiguration;
|
||||
import com.github.dynamic.threadpool.starter.handler.DynamicThreadPoolBannerHandler;
|
||||
import com.github.dynamic.threadpool.starter.remote.HttpAgent;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* DynamicTp auto configuration.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/22 09:20
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnBean(MarkerConfiguration.Marker.class)
|
||||
@EnableConfigurationProperties(BootstrapProperties.class)
|
||||
@ImportAutoConfiguration({HttpClientConfig.class, DiscoveryConfig.class, MessageAlarmConfig.class})
|
||||
public class DynamicThreadPoolAutoConfiguration {
|
||||
|
||||
private final BootstrapProperties properties;
|
||||
|
||||
@Bean
|
||||
public DynamicThreadPoolBannerHandler threadPoolBannerHandler() {
|
||||
return new DynamicThreadPoolBannerHandler(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public ApplicationContextHolder applicationContextHolder() {
|
||||
return new ApplicationContextHolder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("all")
|
||||
public ConfigService configService(HttpAgent httpAgent) {
|
||||
return new ThreadPoolConfigService(httpAgent);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThreadPoolOperation threadPoolOperation(ConfigService configService) {
|
||||
return new ThreadPoolOperation(properties, configService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("all")
|
||||
public DynamicThreadPoolPostProcessor threadPoolBeanPostProcessor(HttpAgent httpAgent, ThreadPoolOperation threadPoolOperation,
|
||||
ApplicationContextHolder applicationContextHolder) {
|
||||
return new DynamicThreadPoolPostProcessor(properties, httpAgent, threadPoolOperation);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PoolRunStateController poolRunStateController() {
|
||||
return new PoolRunStateController();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,49 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.config;
|
||||
|
||||
import com.github.dynamic.threadpool.common.model.InstanceInfo;
|
||||
import com.github.dynamic.threadpool.starter.alarm.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Message alarm config.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 15:39
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class MessageAlarmConfig {
|
||||
|
||||
private final BootstrapProperties properties;
|
||||
|
||||
private final InstanceInfo instanceInfo;
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
public static final String SEND_MESSAGE_BEAN_NAME = "sendMessageService";
|
||||
|
||||
@DependsOn("applicationContextHolder")
|
||||
@Bean(MessageAlarmConfig.SEND_MESSAGE_BEAN_NAME)
|
||||
public SendMessageService sendMessageService() {
|
||||
return new BaseSendMessageService(properties.getNotifys());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SendMessageHandler dingSendMessageHandler() {
|
||||
String active = environment.getProperty("spring.profiles.active", Strings.EMPTY);
|
||||
Long alarmInterval = Optional.ofNullable(properties.getAlarmInterval()).orElse(5L);
|
||||
return new DingSendMessageHandler(active, alarmInterval, instanceInfo);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AlarmControlHandler alarmControlHandler() {
|
||||
Long alarmInterval = properties.getAlarmInterval();
|
||||
return new AlarmControlHandler(alarmInterval);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.controller;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.handler.ThreadPoolRunStateHandler;
|
||||
import com.github.dynamic.threadpool.common.model.PoolRunStateInfo;
|
||||
import com.github.dynamic.threadpool.common.web.base.Result;
|
||||
import com.github.dynamic.threadpool.common.web.base.Results;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Pool run state controller.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/7/7 21:34
|
||||
*/
|
||||
@RestController
|
||||
public class PoolRunStateController {
|
||||
|
||||
@GetMapping("/run/state/{tpId}")
|
||||
public Result<PoolRunStateInfo> getPoolRunState(@PathVariable("tpId") String tpId) {
|
||||
PoolRunStateInfo poolRunState = ThreadPoolRunStateHandler.getPoolRunState(tpId);
|
||||
return Results.success(poolRunState);
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.core;
|
||||
|
||||
/**
|
||||
* Config adapter.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/22 21:29
|
||||
*/
|
||||
public class ConfigAdapter {
|
||||
|
||||
/**
|
||||
* Callback Config.
|
||||
*
|
||||
* @param config
|
||||
*/
|
||||
public void callbackConfig(String config) {
|
||||
ThreadPoolDynamicRefresh.refreshDynamicPool(config);
|
||||
}
|
||||
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.core;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.github.dynamic.threadpool.common.config.ApplicationContextHolder;
|
||||
import com.github.dynamic.threadpool.common.constant.Constants;
|
||||
import com.github.dynamic.threadpool.common.model.PoolParameterInfo;
|
||||
import com.github.dynamic.threadpool.common.web.base.Result;
|
||||
import com.github.dynamic.threadpool.starter.common.CommonThreadPool;
|
||||
import com.github.dynamic.threadpool.starter.config.BootstrapProperties;
|
||||
import com.github.dynamic.threadpool.starter.remote.HttpAgent;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.QueueTypeEnum;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.RejectedTypeEnum;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.ThreadPoolBuilder;
|
||||
import com.github.dynamic.threadpool.starter.wrap.DynamicThreadPoolWrap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.var;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.github.dynamic.threadpool.common.constant.Constants.*;
|
||||
|
||||
/**
|
||||
* Dynamic threadPool post processor.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/2 20:40
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public final class DynamicThreadPoolPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final BootstrapProperties properties;
|
||||
|
||||
private final HttpAgent httpAgent;
|
||||
|
||||
private final ThreadPoolOperation threadPoolOperation;
|
||||
|
||||
private final ExecutorService executorService = ThreadPoolBuilder.builder()
|
||||
.poolThreadSize(2, 4)
|
||||
.keepAliveTime(0L, TimeUnit.MILLISECONDS)
|
||||
.workQueue(QueueTypeEnum.ARRAY_BLOCKING_QUEUE, 1)
|
||||
.threadFactory("dynamic-threadPool-config")
|
||||
.rejected(new ThreadPoolExecutor.DiscardOldestPolicy())
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof CustomThreadPoolExecutor) {
|
||||
var dynamicThreadPool = ApplicationContextHolder.findAnnotationOnBean(beanName, DynamicThreadPool.class);
|
||||
if (Objects.isNull(dynamicThreadPool)) {
|
||||
return bean;
|
||||
}
|
||||
var customExecutor = (CustomThreadPoolExecutor) bean;
|
||||
var wrap = new DynamicThreadPoolWrap(customExecutor.getThreadPoolId(), customExecutor);
|
||||
CustomThreadPoolExecutor remoteExecutor = fillPoolAndRegister(wrap);
|
||||
subscribeConfig(wrap);
|
||||
return remoteExecutor;
|
||||
} else if (bean instanceof DynamicThreadPoolWrap) {
|
||||
var wrap = (DynamicThreadPoolWrap) bean;
|
||||
registerAndSubscribe(wrap);
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register and subscribe.
|
||||
*
|
||||
* @param dynamicThreadPoolWrap
|
||||
*/
|
||||
protected void registerAndSubscribe(DynamicThreadPoolWrap dynamicThreadPoolWrap) {
|
||||
fillPoolAndRegister(dynamicThreadPoolWrap);
|
||||
subscribeConfig(dynamicThreadPoolWrap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the thread pool and register.
|
||||
*
|
||||
* @param dynamicThreadPoolWrap
|
||||
*/
|
||||
protected CustomThreadPoolExecutor fillPoolAndRegister(DynamicThreadPoolWrap dynamicThreadPoolWrap) {
|
||||
String tpId = dynamicThreadPoolWrap.getTpId();
|
||||
Map<String, String> queryStrMap = new HashMap(3);
|
||||
queryStrMap.put(TP_ID, tpId);
|
||||
queryStrMap.put(ITEM_ID, properties.getItemId());
|
||||
queryStrMap.put(NAMESPACE, properties.getNamespace());
|
||||
|
||||
Result result;
|
||||
boolean isSubscribe = false;
|
||||
CustomThreadPoolExecutor poolExecutor = null;
|
||||
PoolParameterInfo ppi = new PoolParameterInfo();
|
||||
|
||||
try {
|
||||
result = httpAgent.httpGetByConfig(Constants.CONFIG_CONTROLLER_PATH, null, queryStrMap, 3000L);
|
||||
if (result.isSuccess() && result.getData() != null && (ppi = JSON.toJavaObject((JSON) result.getData(), PoolParameterInfo.class)) != null) {
|
||||
// 使用相关参数创建线程池
|
||||
BlockingQueue workQueue = QueueTypeEnum.createBlockingQueue(ppi.getQueueType(), ppi.getCapacity());
|
||||
poolExecutor = (CustomThreadPoolExecutor) ThreadPoolBuilder.builder()
|
||||
.isCustomPool(true)
|
||||
.workQueue(workQueue)
|
||||
.threadPoolId(tpId)
|
||||
.threadFactory(tpId)
|
||||
.poolThreadSize(ppi.getCoreSize(), ppi.getMaxSize())
|
||||
.keepAliveTime(ppi.getKeepAliveTime(), TimeUnit.SECONDS)
|
||||
.rejected(RejectedTypeEnum.createPolicy(ppi.getRejectedType()))
|
||||
.alarmConfig(ppi.getIsAlarm(), ppi.getCapacityAlarm(), ppi.getLivenessAlarm())
|
||||
.build();
|
||||
|
||||
dynamicThreadPoolWrap.setPool(poolExecutor);
|
||||
isSubscribe = true;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
poolExecutor = dynamicThreadPoolWrap.getPool() != null ? dynamicThreadPoolWrap.getPool() : CommonThreadPool.getInstance(tpId);
|
||||
dynamicThreadPoolWrap.setPool(poolExecutor);
|
||||
|
||||
log.error("[Init pool] Failed to initialize thread pool configuration. error message :: {}", ex.getMessage());
|
||||
} finally {
|
||||
if (Objects.isNull(dynamicThreadPoolWrap.getPool())) {
|
||||
dynamicThreadPoolWrap.setPool(CommonThreadPool.getInstance(tpId));
|
||||
}
|
||||
|
||||
// 设置是否订阅远端线程池配置
|
||||
dynamicThreadPoolWrap.setSubscribeFlag(isSubscribe);
|
||||
}
|
||||
|
||||
GlobalThreadPoolManage.register(dynamicThreadPoolWrap.getTpId(), ppi, dynamicThreadPoolWrap);
|
||||
return poolExecutor;
|
||||
}
|
||||
|
||||
protected void subscribeConfig(DynamicThreadPoolWrap dynamicThreadPoolWrap) {
|
||||
if (dynamicThreadPoolWrap.isSubscribeFlag()) {
|
||||
threadPoolOperation.subscribeConfig(dynamicThreadPoolWrap.getTpId(), executorService, config -> ThreadPoolDynamicRefresh.refreshDynamicPool(config));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.core;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.wrap.DynamicThreadPoolWrap;
|
||||
import com.github.dynamic.threadpool.common.model.PoolParameter;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Global threadPool manage.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/20 15:57
|
||||
*/
|
||||
public class GlobalThreadPoolManage {
|
||||
|
||||
private static final Map<String, PoolParameter> POOL_PARAMETER = new ConcurrentHashMap();
|
||||
|
||||
private static final Map<String, DynamicThreadPoolWrap> EXECUTOR_MAP = new ConcurrentHashMap();
|
||||
|
||||
public static DynamicThreadPoolWrap getExecutorService(String tpId) {
|
||||
return EXECUTOR_MAP.get(tpId);
|
||||
}
|
||||
|
||||
public static PoolParameter getPoolParameter(String tpId) {
|
||||
return POOL_PARAMETER.get(tpId);
|
||||
}
|
||||
|
||||
public static void register(String tpId, PoolParameter poolParameter, DynamicThreadPoolWrap executor) {
|
||||
registerPool(tpId, executor);
|
||||
registerPoolParameter(tpId, poolParameter);
|
||||
}
|
||||
|
||||
public static void registerPool(String tpId, DynamicThreadPoolWrap executor) {
|
||||
EXECUTOR_MAP.put(tpId, executor);
|
||||
}
|
||||
|
||||
public static void registerPoolParameter(String tpId, PoolParameter poolParameter) {
|
||||
POOL_PARAMETER.put(tpId, poolParameter);
|
||||
}
|
||||
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.core;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.remote.HttpAgent;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* ThreadPool config service.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/21 21:50
|
||||
*/
|
||||
public class ThreadPoolConfigService implements ConfigService {
|
||||
|
||||
private final HttpAgent httpAgent;
|
||||
|
||||
private final ClientWorker clientWorker;
|
||||
|
||||
public ThreadPoolConfigService(HttpAgent httpAgent) {
|
||||
this.httpAgent = httpAgent;
|
||||
clientWorker = new ClientWorker(httpAgent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(String tenantId, String itemId, String tpId, Listener listener) {
|
||||
clientWorker.addTenantListeners(tenantId, itemId, tpId, Arrays.asList(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerStatus() {
|
||||
if (clientWorker.isHealthServer()) {
|
||||
return "UP";
|
||||
} else {
|
||||
return "DOWN";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.core;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.github.dynamic.threadpool.common.model.PoolParameterInfo;
|
||||
import com.github.dynamic.threadpool.starter.alarm.ThreadPoolAlarmManage;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.QueueTypeEnum;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.RejectedTypeEnum;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.ResizableCapacityLinkedBlockIngQueue;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* ThreadPool dynamic refresh.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/20 15:51
|
||||
*/
|
||||
@Slf4j
|
||||
public class ThreadPoolDynamicRefresh {
|
||||
|
||||
public static void refreshDynamicPool(String content) {
|
||||
PoolParameterInfo parameter = JSON.parseObject(content, PoolParameterInfo.class);
|
||||
ThreadPoolAlarmManage.sendPoolConfigChange(parameter);
|
||||
ThreadPoolDynamicRefresh.refreshDynamicPool(parameter);
|
||||
}
|
||||
|
||||
public static void refreshDynamicPool(PoolParameterInfo parameter) {
|
||||
String threadPoolId = parameter.getTpId();
|
||||
ThreadPoolExecutor executor = GlobalThreadPoolManage.getExecutorService(threadPoolId).getPool();
|
||||
|
||||
int originalCoreSize = executor.getCorePoolSize();
|
||||
int originalMaximumPoolSize = executor.getMaximumPoolSize();
|
||||
int originalQueryType = parameter.getQueueType();
|
||||
int originalCapacity = executor.getQueue().remainingCapacity() + executor.getQueue().size();
|
||||
long originalKeepAliveTime = executor.getKeepAliveTime(TimeUnit.MILLISECONDS);
|
||||
int originalRejectedType = parameter.getRejectedType();
|
||||
|
||||
changePoolInfo(executor, parameter);
|
||||
ThreadPoolExecutor afterExecutor = GlobalThreadPoolManage.getExecutorService(threadPoolId).getPool();
|
||||
|
||||
log.info("[🔥 {}] Changed thread pool. coreSize :: [{}], maxSize :: [{}], queueType :: [{}], capacity :: [{}], keepAliveTime :: [{}], rejectedType :: [{}]",
|
||||
threadPoolId.toUpperCase(),
|
||||
String.format("%s=>%s", originalCoreSize, afterExecutor.getCorePoolSize()),
|
||||
String.format("%s=>%s", originalMaximumPoolSize, afterExecutor.getMaximumPoolSize()),
|
||||
String.format("%s=>%s", originalQueryType, parameter.getQueueType()),
|
||||
String.format("%s=>%s", originalCapacity,
|
||||
(afterExecutor.getQueue().remainingCapacity() + afterExecutor.getQueue().size())),
|
||||
String.format("%s=>%s", originalKeepAliveTime, afterExecutor.getKeepAliveTime(TimeUnit.MILLISECONDS)),
|
||||
String.format("%s=>%s", originalRejectedType, parameter.getRejectedType()));
|
||||
}
|
||||
|
||||
public static void changePoolInfo(ThreadPoolExecutor executor, PoolParameterInfo parameter) {
|
||||
if (parameter.getCoreSize() != null) {
|
||||
executor.setCorePoolSize(parameter.getCoreSize());
|
||||
}
|
||||
|
||||
if (parameter.getMaxSize() != null) {
|
||||
executor.setMaximumPoolSize(parameter.getMaxSize());
|
||||
}
|
||||
|
||||
if (parameter.getCapacity() != null
|
||||
&& Objects.equals(QueueTypeEnum.RESIZABLE_LINKED_BLOCKING_QUEUE.type, parameter.getQueueType())) {
|
||||
if (executor.getQueue() instanceof ResizableCapacityLinkedBlockIngQueue) {
|
||||
ResizableCapacityLinkedBlockIngQueue queue = (ResizableCapacityLinkedBlockIngQueue) executor.getQueue();
|
||||
queue.setCapacity(parameter.getCapacity());
|
||||
} else {
|
||||
log.warn("[Pool change] The queue length cannot be modified. Queue type mismatch. Current queue type :: {}", executor.getQueue().getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
if (parameter.getKeepAliveTime() != null) {
|
||||
executor.setKeepAliveTime(parameter.getKeepAliveTime(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
if (parameter.getRejectedType() != null) {
|
||||
executor.setRejectedExecutionHandler(RejectedTypeEnum.createPolicy(parameter.getRejectedType()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.handler;
|
||||
|
||||
import com.github.dynamic.threadpool.common.model.PoolRunStateInfo;
|
||||
import com.github.dynamic.threadpool.starter.core.GlobalThreadPoolManage;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.CalculateUtil;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
import com.github.dynamic.threadpool.starter.wrap.DynamicThreadPoolWrap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* Thread pool run state service.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/7/12 21:25
|
||||
*/
|
||||
@Slf4j
|
||||
public class ThreadPoolRunStateHandler {
|
||||
|
||||
private static InetAddress INET_ADDRESS;
|
||||
|
||||
static {
|
||||
try {
|
||||
INET_ADDRESS = InetAddress.getLocalHost();
|
||||
} catch (UnknownHostException ex) {
|
||||
log.error("Local IP acquisition failed.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static PoolRunStateInfo getPoolRunState(String tpId) {
|
||||
DynamicThreadPoolWrap executorService = GlobalThreadPoolManage.getExecutorService(tpId);
|
||||
ThreadPoolExecutor pool = executorService.getPool();
|
||||
|
||||
// 核心线程数
|
||||
int corePoolSize = pool.getCorePoolSize();
|
||||
// 最大线程数
|
||||
int maximumPoolSize = pool.getMaximumPoolSize();
|
||||
// 线程池当前线程数
|
||||
int poolSize = pool.getPoolSize();
|
||||
// 活跃线程数
|
||||
int activeCount = pool.getActiveCount();
|
||||
// 同时进入池中的最大线程数
|
||||
int largestPoolSize = pool.getLargestPoolSize();
|
||||
// 线程池中执行任务总数量
|
||||
long completedTaskCount = pool.getCompletedTaskCount();
|
||||
// 当前负载
|
||||
String currentLoad = CalculateUtil.divide(activeCount, maximumPoolSize) + "%";
|
||||
// 峰值负载
|
||||
String peakLoad = CalculateUtil.divide(largestPoolSize, maximumPoolSize) + "%";
|
||||
|
||||
BlockingQueue<Runnable> queue = pool.getQueue();
|
||||
// 队列类型
|
||||
String queueType = queue.getClass().getSimpleName();
|
||||
// 队列元素个数
|
||||
int queueSize = queue.size();
|
||||
// 队列剩余容量
|
||||
int remainingCapacity = queue.remainingCapacity();
|
||||
// 队列容量
|
||||
int queueCapacity = queueSize + remainingCapacity;
|
||||
|
||||
PoolRunStateInfo stateInfo = new PoolRunStateInfo();
|
||||
stateInfo.setCoreSize(corePoolSize);
|
||||
stateInfo.setMaximumSize(maximumPoolSize);
|
||||
stateInfo.setPoolSize(poolSize);
|
||||
stateInfo.setActiveSize(activeCount);
|
||||
stateInfo.setCurrentLoad(currentLoad);
|
||||
stateInfo.setPeakLoad(peakLoad);
|
||||
stateInfo.setQueueType(queueType);
|
||||
stateInfo.setQueueSize(queueSize);
|
||||
stateInfo.setQueueRemainingCapacity(remainingCapacity);
|
||||
stateInfo.setQueueCapacity(queueCapacity);
|
||||
stateInfo.setLargestPoolSize(largestPoolSize);
|
||||
stateInfo.setCompletedTaskCount(completedTaskCount);
|
||||
stateInfo.setHost(INET_ADDRESS.getHostAddress());
|
||||
stateInfo.setTpId(tpId);
|
||||
|
||||
int rejectCount = pool instanceof CustomThreadPoolExecutor
|
||||
? ((CustomThreadPoolExecutor) pool).getRejectCount()
|
||||
: -1;
|
||||
stateInfo.setRejectCount(rejectCount);
|
||||
|
||||
return stateInfo;
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.remote;
|
||||
|
||||
import com.github.dynamic.threadpool.common.web.base.Result;
|
||||
import com.github.dynamic.threadpool.starter.config.BootstrapProperties;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.HttpClientUtil;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Server http agent.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/23 20:50
|
||||
*/
|
||||
public class ServerHttpAgent implements HttpAgent {
|
||||
|
||||
private final BootstrapProperties dynamicThreadPoolProperties;
|
||||
|
||||
private final ServerListManager serverListManager;
|
||||
|
||||
private final HttpClientUtil httpClientUtil;
|
||||
|
||||
public ServerHttpAgent(BootstrapProperties properties, HttpClientUtil httpClientUtil) {
|
||||
this.dynamicThreadPoolProperties = properties;
|
||||
this.httpClientUtil = httpClientUtil;
|
||||
this.serverListManager = new ServerListManager(dynamicThreadPoolProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenantId() {
|
||||
return dynamicThreadPoolProperties.getNamespace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result httpPostByDiscovery(String path, Object body) {
|
||||
return httpClientUtil.restApiPost(buildUrl(path), body, Result.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result httpGetByConfig(String path, Map<String, String> headers, Map<String, String> paramValues, long readTimeoutMs) {
|
||||
return httpClientUtil.restApiGetByThreadPool(buildUrl(path), headers, paramValues, readTimeoutMs, Result.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result httpPostByConfig(String path, Map<String, String> headers, Map<String, String> paramValues, long readTimeoutMs) {
|
||||
return httpClientUtil.restApiPostByThreadPool(buildUrl(path), headers, paramValues, readTimeoutMs, Result.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result httpDeleteByConfig(String path, Map<String, String> headers, Map<String, String> paramValues, long readTimeoutMs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String buildUrl(String path) {
|
||||
return serverListManager.getCurrentServerAddr() + path;
|
||||
}
|
||||
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.toolkit.thread;
|
||||
|
||||
import com.github.dynamic.threadpool.common.toolkit.Assert;
|
||||
import com.github.dynamic.threadpool.starter.alarm.ThreadPoolAlarm;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||
|
||||
/**
|
||||
* Abstract build threadPool template.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/7/5 21:45
|
||||
*/
|
||||
@Slf4j
|
||||
public class AbstractBuildThreadPoolTemplate {
|
||||
|
||||
/**
|
||||
* 线程池构建初始化参数
|
||||
* <p>
|
||||
* 此处本身是模版设计方法, 但是考虑创建简洁性, 移除 abstract
|
||||
* 异常参考 {@link AbstractQueuedSynchronizer#tryAcquire}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected static ThreadPoolInitParam initParam() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建线程池
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ThreadPoolExecutor buildPool() {
|
||||
ThreadPoolInitParam initParam = initParam();
|
||||
return buildPool(initParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建线程池
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ThreadPoolExecutor buildPool(ThreadPoolInitParam initParam) {
|
||||
Assert.notNull(initParam);
|
||||
ThreadPoolExecutor executorService =
|
||||
new ThreadPoolExecutorTemplate(initParam.getCorePoolNum(),
|
||||
initParam.getMaxPoolNum(),
|
||||
initParam.getKeepAliveTime(),
|
||||
initParam.getTimeUnit(),
|
||||
initParam.getWorkQueue(),
|
||||
initParam.getThreadFactory(),
|
||||
initParam.rejectedExecutionHandler);
|
||||
return executorService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建快速执行线程池
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ThreadPoolExecutor buildFastPool() {
|
||||
ThreadPoolInitParam initParam = initParam();
|
||||
return buildFastPool(initParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建快速执行线程池
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ThreadPoolExecutor buildFastPool(ThreadPoolInitParam initParam) {
|
||||
TaskQueue<Runnable> taskQueue = new TaskQueue(initParam.getCapacity());
|
||||
FastThreadPoolExecutor fastThreadPoolExecutor =
|
||||
new FastThreadPoolExecutor(initParam.getCorePoolNum(),
|
||||
initParam.getMaxPoolNum(),
|
||||
initParam.getKeepAliveTime(),
|
||||
initParam.getTimeUnit(),
|
||||
taskQueue,
|
||||
initParam.getThreadFactory(),
|
||||
initParam.rejectedExecutionHandler);
|
||||
taskQueue.setExecutor(fastThreadPoolExecutor);
|
||||
return fastThreadPoolExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建自定义线程池
|
||||
*
|
||||
* @param initParam
|
||||
* @return
|
||||
*/
|
||||
public static CustomThreadPoolExecutor buildCustomPool(ThreadPoolInitParam initParam) {
|
||||
Assert.notNull(initParam);
|
||||
CustomThreadPoolExecutor executorService =
|
||||
new CustomThreadPoolExecutor(initParam.getCorePoolNum(),
|
||||
initParam.getMaxPoolNum(),
|
||||
initParam.getKeepAliveTime(),
|
||||
initParam.getTimeUnit(),
|
||||
initParam.getWorkQueue(),
|
||||
initParam.getThreadPoolId(),
|
||||
initParam.getThreadFactory(),
|
||||
initParam.getThreadPoolAlarm(),
|
||||
initParam.getRejectedExecutionHandler());
|
||||
return executorService;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class ThreadPoolInitParam {
|
||||
|
||||
/**
|
||||
* 核心线程数量
|
||||
*/
|
||||
private Integer corePoolNum;
|
||||
|
||||
/**
|
||||
* 最大线程数量
|
||||
*/
|
||||
private Integer maxPoolNum;
|
||||
|
||||
/**
|
||||
* 线程存活时间
|
||||
*/
|
||||
private Long keepAliveTime;
|
||||
|
||||
/**
|
||||
* 线程存活时间单位
|
||||
*/
|
||||
private TimeUnit timeUnit;
|
||||
|
||||
/**
|
||||
* 队列最大容量
|
||||
*/
|
||||
private Integer capacity;
|
||||
|
||||
/**
|
||||
* 阻塞队列
|
||||
*/
|
||||
private BlockingQueue<Runnable> workQueue;
|
||||
|
||||
/**
|
||||
* 线程池任务满时拒绝任务策略
|
||||
*/
|
||||
private RejectedExecutionHandler rejectedExecutionHandler;
|
||||
|
||||
/**
|
||||
* 创建线程工厂
|
||||
*/
|
||||
private ThreadFactory threadFactory;
|
||||
|
||||
/**
|
||||
* 线程 ID
|
||||
*/
|
||||
private String threadPoolId;
|
||||
|
||||
/**
|
||||
* 报警策略
|
||||
*/
|
||||
private ThreadPoolAlarm threadPoolAlarm;
|
||||
|
||||
public ThreadPoolInitParam(String threadNamePrefix, boolean isDaemon) {
|
||||
this.threadPoolId = threadNamePrefix;
|
||||
this.threadFactory = ThreadFactoryBuilder.builder()
|
||||
.prefix(threadNamePrefix)
|
||||
.daemon(isDaemon)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,954 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.toolkit.thread;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.alarm.ThreadPoolAlarm;
|
||||
import com.github.dynamic.threadpool.starter.alarm.ThreadPoolAlarmManage;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static com.github.dynamic.threadpool.common.constant.Constants.MAP_INITIAL_CAPACITY;
|
||||
|
||||
/**
|
||||
* Custom threadPool wrap.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/7/8 21:47
|
||||
*/
|
||||
public final class CustomThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
|
||||
private final AtomicInteger rejectCount = new AtomicInteger();
|
||||
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
|
||||
|
||||
private static final int COUNT_BITS = Integer.SIZE - 3;
|
||||
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
|
||||
|
||||
private static final int RUNNING = -1 << COUNT_BITS;
|
||||
private static final int SHUTDOWN = 0 << COUNT_BITS;
|
||||
private static final int STOP = 1 << COUNT_BITS;
|
||||
private static final int TIDYING = 2 << COUNT_BITS;
|
||||
private static final int TERMINATED = 3 << COUNT_BITS;
|
||||
|
||||
private final BlockingQueue<Runnable> workQueue;
|
||||
private final ReentrantLock mainLock = new ReentrantLock();
|
||||
private final HashSet<Worker> workers = new HashSet();
|
||||
private final Condition termination = mainLock.newCondition();
|
||||
|
||||
private int largestPoolSize;
|
||||
private long completedTaskCount;
|
||||
private volatile long keepAliveTime;
|
||||
private volatile boolean allowCoreThreadTimeOut;
|
||||
private volatile int corePoolSize;
|
||||
private volatile int maximumPoolSize;
|
||||
private String threadPoolId;
|
||||
|
||||
private final AccessControlContext acc;
|
||||
private volatile ThreadPoolAlarm threadPoolAlarm;
|
||||
private volatile ThreadFactory threadFactory;
|
||||
private volatile RejectedExecutionHandler handler;
|
||||
|
||||
private static final RejectedExecutionHandler DEFAULT_HANDLER = new ThreadPoolExecutor.AbortPolicy();
|
||||
private static final RuntimePermission SHUTDOWN_PERM = new RuntimePermission("modifyThread");
|
||||
|
||||
public CustomThreadPoolExecutor(int corePoolSize,
|
||||
int maximumPoolSize,
|
||||
long keepAliveTime,
|
||||
TimeUnit unit,
|
||||
@NonNull BlockingQueue<Runnable> workQueue,
|
||||
@NonNull String threadPoolId,
|
||||
@NonNull ThreadFactory threadFactory,
|
||||
@NonNull ThreadPoolAlarm threadPoolAlarm,
|
||||
@NonNull RejectedExecutionHandler handler) {
|
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
|
||||
|
||||
if (corePoolSize < 0 ||
|
||||
maximumPoolSize <= 0 ||
|
||||
maximumPoolSize < corePoolSize ||
|
||||
keepAliveTime < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
this.corePoolSize = corePoolSize;
|
||||
this.maximumPoolSize = maximumPoolSize;
|
||||
this.workQueue = workQueue;
|
||||
this.threadPoolId = threadPoolId;
|
||||
this.keepAliveTime = unit.toNanos(keepAliveTime);
|
||||
this.threadFactory = threadFactory;
|
||||
this.handler = handler;
|
||||
this.threadPoolAlarm = threadPoolAlarm;
|
||||
this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
|
||||
}
|
||||
|
||||
private static int runStateOf(int c) {
|
||||
return c & ~CAPACITY;
|
||||
}
|
||||
|
||||
private static int workerCountOf(int c) {
|
||||
return c & CAPACITY;
|
||||
}
|
||||
|
||||
private static int ctlOf(int rs, int wc) {
|
||||
return rs | wc;
|
||||
}
|
||||
|
||||
private static boolean runStateLessThan(int c, int s) {
|
||||
return c < s;
|
||||
}
|
||||
|
||||
private static boolean runStateAtLeast(int c, int s) {
|
||||
return c >= s;
|
||||
}
|
||||
|
||||
private static boolean isRunning(int c) {
|
||||
return c < SHUTDOWN;
|
||||
}
|
||||
|
||||
private boolean compareAndIncrementWorkerCount(int expect) {
|
||||
return ctl.compareAndSet(expect, expect + 1);
|
||||
}
|
||||
|
||||
private boolean compareAndDecrementWorkerCount(int expect) {
|
||||
return ctl.compareAndSet(expect, expect - 1);
|
||||
}
|
||||
|
||||
private void decrementWorkerCount() {
|
||||
do {
|
||||
} while (!compareAndDecrementWorkerCount(ctl.get()));
|
||||
}
|
||||
|
||||
public Integer getRejectCount() {
|
||||
return rejectCount.get();
|
||||
}
|
||||
|
||||
public ThreadPoolAlarm getThreadPoolAlarm() {
|
||||
return this.threadPoolAlarm;
|
||||
}
|
||||
|
||||
public void setThreadPoolAlarm(ThreadPoolAlarm threadPoolAlarm) {
|
||||
this.threadPoolAlarm = threadPoolAlarm;
|
||||
}
|
||||
|
||||
public String getThreadPoolId() {
|
||||
return this.threadPoolId;
|
||||
}
|
||||
|
||||
private final class Worker
|
||||
extends AbstractQueuedSynchronizer
|
||||
implements Runnable {
|
||||
|
||||
private static final long serialVersionUID = 6138294804551838833L;
|
||||
|
||||
final Thread thread;
|
||||
Runnable firstTask;
|
||||
volatile long completedTasks;
|
||||
|
||||
Worker(Runnable firstTask) {
|
||||
setState(-1);
|
||||
this.firstTask = firstTask;
|
||||
this.thread = getThreadFactory().newThread(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
runWorker(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isHeldExclusively() {
|
||||
return getState() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean tryAcquire(int unused) {
|
||||
if (compareAndSetState(0, 1)) {
|
||||
setExclusiveOwnerThread(Thread.currentThread());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean tryRelease(int unused) {
|
||||
setExclusiveOwnerThread(null);
|
||||
setState(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
acquire(1);
|
||||
}
|
||||
|
||||
public boolean tryLock() {
|
||||
return tryAcquire(1);
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
release(1);
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return isHeldExclusively();
|
||||
}
|
||||
|
||||
void interruptIfStarted() {
|
||||
Thread t;
|
||||
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
|
||||
try {
|
||||
t.interrupt();
|
||||
} catch (SecurityException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceRunState(int targetState) {
|
||||
for (; ; ) {
|
||||
int c = ctl.get();
|
||||
if (runStateAtLeast(c, targetState) ||
|
||||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void tryTerminate() {
|
||||
for (; ; ) {
|
||||
int c = ctl.get();
|
||||
if (isRunning(c)
|
||||
|| runStateAtLeast(c, TIDYING)
|
||||
|| (runStateOf(c) == SHUTDOWN && !workQueue.isEmpty())) {
|
||||
return;
|
||||
}
|
||||
if (workerCountOf(c) != 0) {
|
||||
interruptIdleWorkers(ONLY_ONE);
|
||||
return;
|
||||
}
|
||||
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
|
||||
try {
|
||||
terminated();
|
||||
} finally {
|
||||
ctl.set(ctlOf(TERMINATED, 0));
|
||||
termination.signalAll();
|
||||
}
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkShutdownAccess() {
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security != null) {
|
||||
security.checkPermission(SHUTDOWN_PERM);
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
for (Worker w : workers) {
|
||||
security.checkAccess(w.thread);
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void interruptWorkers() {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
for (Worker w : workers) {
|
||||
w.interruptIfStarted();
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void interruptIdleWorkers(boolean onlyOne) {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
for (Worker w : workers) {
|
||||
Thread t = w.thread;
|
||||
if (!t.isInterrupted() && w.tryLock()) {
|
||||
try {
|
||||
t.interrupt();
|
||||
} catch (SecurityException ignore) {
|
||||
} finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
if (onlyOne) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void interruptIdleWorkers() {
|
||||
interruptIdleWorkers(false);
|
||||
}
|
||||
|
||||
private static final boolean ONLY_ONE = true;
|
||||
|
||||
final void reject(Runnable command) {
|
||||
rejectCount.incrementAndGet();
|
||||
ThreadPoolAlarmManage.checkPoolRejectAlarm(this);
|
||||
handler.rejectedExecution(command, this);
|
||||
}
|
||||
|
||||
void onShutdown() {
|
||||
}
|
||||
|
||||
final boolean isRunningOrShutdown(boolean shutdownOk) {
|
||||
int rs = runStateOf(ctl.get());
|
||||
return rs == RUNNING || (rs == SHUTDOWN && shutdownOk);
|
||||
}
|
||||
|
||||
private List<Runnable> drainQueue() {
|
||||
BlockingQueue<Runnable> q = workQueue;
|
||||
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
|
||||
q.drainTo(taskList);
|
||||
if (!q.isEmpty()) {
|
||||
for (Runnable r : q.toArray(new Runnable[0])) {
|
||||
if (q.remove(r)) {
|
||||
taskList.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
return taskList;
|
||||
}
|
||||
|
||||
private boolean addWorker(Runnable firstTask, boolean core) {
|
||||
retry:
|
||||
for (; ; ) {
|
||||
int c = ctl.get();
|
||||
int rs = runStateOf(c);
|
||||
|
||||
// Check if queue empty only if necessary.
|
||||
if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; ; ) {
|
||||
int wc = workerCountOf(c);
|
||||
if (wc >= CAPACITY ||
|
||||
wc >= (core ? corePoolSize : maximumPoolSize)) {
|
||||
return false;
|
||||
}
|
||||
if (compareAndIncrementWorkerCount(c)) {
|
||||
break retry;
|
||||
}
|
||||
c = ctl.get();
|
||||
if (runStateOf(c) != rs) {
|
||||
continue retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPoolAlarmManage.checkPoolLivenessAlarm(core, this);
|
||||
|
||||
boolean workerStarted = false;
|
||||
boolean workerAdded = false;
|
||||
Worker w = null;
|
||||
try {
|
||||
w = new Worker(firstTask);
|
||||
final Thread t = w.thread;
|
||||
if (t != null) {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
int rs = runStateOf(ctl.get());
|
||||
|
||||
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
|
||||
if (t.isAlive()) {
|
||||
throw new IllegalThreadStateException();
|
||||
}
|
||||
workers.add(w);
|
||||
int s = workers.size();
|
||||
if (s > largestPoolSize) {
|
||||
largestPoolSize = s;
|
||||
}
|
||||
workerAdded = true;
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
if (workerAdded) {
|
||||
t.start();
|
||||
workerStarted = true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!workerStarted) {
|
||||
addWorkerFailed(w);
|
||||
}
|
||||
}
|
||||
return workerStarted;
|
||||
}
|
||||
|
||||
private void addWorkerFailed(Worker w) {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
if (w != null) {
|
||||
workers.remove(w);
|
||||
}
|
||||
decrementWorkerCount();
|
||||
tryTerminate();
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void processWorkerExit(Worker w, boolean completedAbruptly) {
|
||||
if (completedAbruptly) {
|
||||
decrementWorkerCount();
|
||||
}
|
||||
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
completedTaskCount += w.completedTasks;
|
||||
workers.remove(w);
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
|
||||
tryTerminate();
|
||||
|
||||
int c = ctl.get();
|
||||
if (runStateLessThan(c, STOP)) {
|
||||
if (!completedAbruptly) {
|
||||
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
|
||||
if (min == 0 && !workQueue.isEmpty()) {
|
||||
min = 1;
|
||||
}
|
||||
if (workerCountOf(c) >= min) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
addWorker(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable getTask() {
|
||||
boolean timedOut = false;
|
||||
|
||||
for (; ; ) {
|
||||
int c = ctl.get();
|
||||
int rs = runStateOf(c);
|
||||
|
||||
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
|
||||
decrementWorkerCount();
|
||||
return null;
|
||||
}
|
||||
|
||||
int wc = workerCountOf(c);
|
||||
|
||||
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
|
||||
|
||||
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
|
||||
if (compareAndDecrementWorkerCount(c)) {
|
||||
return null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Runnable r = timed ?
|
||||
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
|
||||
workQueue.take();
|
||||
if (r != null) {
|
||||
return r;
|
||||
}
|
||||
timedOut = true;
|
||||
} catch (InterruptedException retry) {
|
||||
timedOut = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void runWorker(Worker w) {
|
||||
Thread wt = Thread.currentThread();
|
||||
Runnable task = w.firstTask;
|
||||
w.firstTask = null;
|
||||
w.unlock();
|
||||
boolean completedAbruptly = true;
|
||||
try {
|
||||
while (task != null || (task = getTask()) != null) {
|
||||
w.lock();
|
||||
if ((runStateAtLeast(ctl.get(), STOP)
|
||||
|| (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
|
||||
&& !wt.isInterrupted()) {
|
||||
wt.interrupt();
|
||||
}
|
||||
try {
|
||||
beforeExecute(wt, task);
|
||||
Throwable thrown = null;
|
||||
try {
|
||||
task.run();
|
||||
} catch (RuntimeException x) {
|
||||
thrown = x;
|
||||
throw x;
|
||||
} catch (Error x) {
|
||||
thrown = x;
|
||||
throw x;
|
||||
} catch (Throwable x) {
|
||||
thrown = x;
|
||||
throw new Error(x);
|
||||
} finally {
|
||||
afterExecute(task, thrown);
|
||||
}
|
||||
} finally {
|
||||
task = null;
|
||||
w.completedTasks++;
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
completedAbruptly = false;
|
||||
} finally {
|
||||
processWorkerExit(w, completedAbruptly);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull Runnable command) {
|
||||
int c = ctl.get();
|
||||
if (workerCountOf(c) < corePoolSize) {
|
||||
if (addWorker(command, true)) {
|
||||
return;
|
||||
}
|
||||
c = ctl.get();
|
||||
}
|
||||
if (isRunning(c) && workQueue.offer(command)) {
|
||||
ThreadPoolAlarmManage.checkPoolCapacityAlarm(this);
|
||||
int recheck = ctl.get();
|
||||
if (!isRunning(recheck) && remove(command)) {
|
||||
reject(command);
|
||||
} else if (workerCountOf(recheck) == 0) {
|
||||
addWorker(null, false);
|
||||
}
|
||||
} else if (!addWorker(command, false)) {
|
||||
reject(command);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
checkShutdownAccess();
|
||||
advanceRunState(SHUTDOWN);
|
||||
interruptIdleWorkers();
|
||||
onShutdown();
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
tryTerminate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
List<Runnable> tasks;
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
checkShutdownAccess();
|
||||
advanceRunState(STOP);
|
||||
interruptWorkers();
|
||||
tasks = drainQueue();
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
tryTerminate();
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return !isRunning(ctl.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminating() {
|
||||
int c = ctl.get();
|
||||
return !isRunning(c) && runStateLessThan(c, TERMINATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return runStateAtLeast(ctl.get(), TERMINATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
long nanos = unit.toNanos(timeout);
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
for (; ; ) {
|
||||
if (runStateAtLeast(ctl.get(), TERMINATED)) {
|
||||
return true;
|
||||
}
|
||||
if (nanos <= 0) {
|
||||
return false;
|
||||
}
|
||||
nanos = termination.awaitNanos(nanos);
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm == null || acc == null) {
|
||||
shutdown();
|
||||
} else {
|
||||
PrivilegedAction<Void> pa = () -> {
|
||||
shutdown();
|
||||
return null;
|
||||
};
|
||||
AccessController.doPrivileged(pa, acc);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThreadFactory(@NonNull ThreadFactory threadFactory) {
|
||||
this.threadFactory = threadFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadFactory getThreadFactory() {
|
||||
return threadFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRejectedExecutionHandler(@NonNull RejectedExecutionHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RejectedExecutionHandler getRejectedExecutionHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCorePoolSize(int corePoolSize) {
|
||||
if (corePoolSize < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int delta = corePoolSize - this.corePoolSize;
|
||||
this.corePoolSize = corePoolSize;
|
||||
if (workerCountOf(ctl.get()) > corePoolSize) {
|
||||
interruptIdleWorkers();
|
||||
} else if (delta > 0) {
|
||||
int k = Math.min(delta, workQueue.size());
|
||||
while (k-- > 0 && addWorker(null, true)) {
|
||||
if (workQueue.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCorePoolSize() {
|
||||
return corePoolSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prestartCoreThread() {
|
||||
return workerCountOf(ctl.get()) < corePoolSize &&
|
||||
addWorker(null, true);
|
||||
}
|
||||
|
||||
void ensurePrestart() {
|
||||
int wc = workerCountOf(ctl.get());
|
||||
if (wc < corePoolSize) {
|
||||
addWorker(null, true);
|
||||
} else if (wc == 0) {
|
||||
addWorker(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int prestartAllCoreThreads() {
|
||||
int n = 0;
|
||||
while (addWorker(null, true)) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowsCoreThreadTimeOut() {
|
||||
return allowCoreThreadTimeOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allowCoreThreadTimeOut(boolean value) {
|
||||
if (value && keepAliveTime <= 0) {
|
||||
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
|
||||
}
|
||||
if (value != allowCoreThreadTimeOut) {
|
||||
allowCoreThreadTimeOut = value;
|
||||
if (value) {
|
||||
interruptIdleWorkers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximumPoolSize(int maximumPoolSize) {
|
||||
if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.maximumPoolSize = maximumPoolSize;
|
||||
if (workerCountOf(ctl.get()) > maximumPoolSize) {
|
||||
interruptIdleWorkers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumPoolSize() {
|
||||
return maximumPoolSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepAliveTime(long time, TimeUnit unit) {
|
||||
if (time < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (time == 0 && allowsCoreThreadTimeOut()) {
|
||||
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
|
||||
}
|
||||
long keepAliveTime = unit.toNanos(time);
|
||||
long delta = keepAliveTime - this.keepAliveTime;
|
||||
this.keepAliveTime = keepAliveTime;
|
||||
if (delta < 0) {
|
||||
interruptIdleWorkers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getKeepAliveTime(TimeUnit unit) {
|
||||
return unit.convert(keepAliveTime, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<Runnable> getQueue() {
|
||||
return workQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Runnable task) {
|
||||
boolean removed = workQueue.remove(task);
|
||||
tryTerminate();
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purge() {
|
||||
final BlockingQueue<Runnable> q = workQueue;
|
||||
try {
|
||||
Iterator<Runnable> it = q.iterator();
|
||||
while (it.hasNext()) {
|
||||
Runnable r = it.next();
|
||||
if (r instanceof Future<?> && ((Future<?>) r).isCancelled()) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
} catch (ConcurrentModificationException fallThrough) {
|
||||
for (Object r : q.toArray()) {
|
||||
if (r instanceof Future<?> && ((Future<?>) r).isCancelled()) {
|
||||
q.remove(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tryTerminate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPoolSize() {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
return runStateAtLeast(ctl.get(), TIDYING) ? 0 : workers.size();
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveCount() {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
int n = 0;
|
||||
for (Worker w : workers) {
|
||||
if (w.isLocked()) {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLargestPoolSize() {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
return largestPoolSize;
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTaskCount() {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
long n = completedTaskCount;
|
||||
for (Worker w : workers) {
|
||||
n += w.completedTasks;
|
||||
if (w.isLocked()) {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
return n + workQueue.size();
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCompletedTaskCount() {
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
long n = completedTaskCount;
|
||||
for (Worker w : workers) {
|
||||
n += w.completedTasks;
|
||||
}
|
||||
return n;
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
long ncompleted;
|
||||
int nworkers, nactive;
|
||||
final ReentrantLock mainLock = this.mainLock;
|
||||
mainLock.lock();
|
||||
try {
|
||||
ncompleted = completedTaskCount;
|
||||
nactive = 0;
|
||||
nworkers = workers.size();
|
||||
for (Worker w : workers) {
|
||||
ncompleted += w.completedTasks;
|
||||
if (w.isLocked()) {
|
||||
++nactive;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
}
|
||||
int c = ctl.get();
|
||||
String rs = (runStateLessThan(c, SHUTDOWN) ? "Running" :
|
||||
(runStateAtLeast(c, TERMINATED) ? "Terminated" :
|
||||
"Shutting down"));
|
||||
return super.toString() +
|
||||
"[" + rs +
|
||||
", pool size = " + nworkers +
|
||||
", active threads = " + nactive +
|
||||
", queued tasks = " + workQueue.size() +
|
||||
", completed tasks = " + ncompleted +
|
||||
"]";
|
||||
}
|
||||
|
||||
private ConcurrentHashMap<String, Date> statisticsTime = new ConcurrentHashMap(MAP_INITIAL_CAPACITY);
|
||||
|
||||
@Override
|
||||
protected void beforeExecute(Thread t, Runnable r) {
|
||||
statisticsTime.put(String.valueOf(r.hashCode()), new Date());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable r, Throwable t) {
|
||||
Date startDate = statisticsTime.remove(String.valueOf(r.hashCode()));
|
||||
Date finishDate = new Date();
|
||||
long diff = finishDate.getTime() - startDate.getTime();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void terminated() {
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
public static class CallerRunsPolicy implements RejectedExecutionHandler {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
if (!e.isShutdown()) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
public static class AbortPolicy implements RejectedExecutionHandler {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
throw new RejectedExecutionException("Task " + r.toString() +
|
||||
" rejected from " +
|
||||
e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
public static class DiscardPolicy implements RejectedExecutionHandler {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
}
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
|
||||
if (!e.isShutdown()) {
|
||||
e.getQueue().poll();
|
||||
e.execute(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.toolkit.thread;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.spi.CustomRejectedExecutionHandler;
|
||||
import com.github.dynamic.threadpool.starter.spi.DynamicTpServiceLoader;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Reject policy type Enum.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/7/10 23:16
|
||||
*/
|
||||
public enum RejectedTypeEnum {
|
||||
|
||||
/**
|
||||
* 被拒绝任务的程序由主线程执行
|
||||
*/
|
||||
CALLER_RUNS_POLICY(1, new ThreadPoolExecutor.CallerRunsPolicy()),
|
||||
|
||||
/**
|
||||
* 被拒绝任务的处理程序, 抛出异常
|
||||
*/
|
||||
ABORT_POLICY(2, new ThreadPoolExecutor.AbortPolicy()),
|
||||
|
||||
/**
|
||||
* 被拒绝任务的处理程序, 默默地丢弃被拒绝的任务。
|
||||
*/
|
||||
DISCARD_POLICY(3, new ThreadPoolExecutor.DiscardPolicy()),
|
||||
|
||||
/**
|
||||
* 被拒绝任务的处理程序, 它丢弃最早的未处理请求, 然后重试
|
||||
*/
|
||||
DISCARD_OLDEST_POLICY(4, new ThreadPoolExecutor.DiscardOldestPolicy()),
|
||||
|
||||
/**
|
||||
* 发生拒绝事件时, 添加新任务并运行最早的任务
|
||||
*/
|
||||
RUNS_OLDEST_TASK_POLICY(5, RejectedPolicies.runsOldestTaskPolicy()),
|
||||
|
||||
/**
|
||||
* 使用阻塞方法将拒绝任务添加队列, 可保证任务不丢失
|
||||
*/
|
||||
SYNC_PUT_QUEUE_POLICY(6, RejectedPolicies.syncPutQueuePolicy());
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
public Integer type;
|
||||
|
||||
/**
|
||||
* 线程池拒绝策略
|
||||
*/
|
||||
public RejectedExecutionHandler rejectedHandler;
|
||||
|
||||
RejectedTypeEnum(Integer type, RejectedExecutionHandler rejectedHandler) {
|
||||
this.type = type;
|
||||
this.rejectedHandler = rejectedHandler;
|
||||
}
|
||||
|
||||
static {
|
||||
DynamicTpServiceLoader.register(CustomRejectedExecutionHandler.class);
|
||||
}
|
||||
|
||||
public static RejectedExecutionHandler createPolicy(int type) {
|
||||
Optional<RejectedExecutionHandler> rejectedTypeEnum = Stream.of(RejectedTypeEnum.values())
|
||||
.filter(each -> Objects.equals(type, each.type))
|
||||
.map(each -> each.rejectedHandler)
|
||||
.findFirst();
|
||||
|
||||
// 使用 SPI 匹配拒绝策略
|
||||
RejectedExecutionHandler resultRejected = rejectedTypeEnum.orElseGet(() -> {
|
||||
Collection<CustomRejectedExecutionHandler> customRejectedExecutionHandlers = DynamicTpServiceLoader
|
||||
.getSingletonServiceInstances(CustomRejectedExecutionHandler.class);
|
||||
Optional<RejectedExecutionHandler> customRejected = customRejectedExecutionHandlers.stream()
|
||||
.filter(each -> Objects.equals(type, each.getType()))
|
||||
.map(each -> each.generateRejected())
|
||||
.findFirst();
|
||||
|
||||
return customRejected.orElse(ABORT_POLICY.rejectedHandler);
|
||||
});
|
||||
|
||||
return resultRejected;
|
||||
}
|
||||
|
||||
public static String getRejectedNameByType(int type) {
|
||||
Optional<RejectedTypeEnum> rejectedTypeEnum = Arrays.stream(RejectedTypeEnum.values())
|
||||
.filter(each -> each.type == type).findFirst();
|
||||
|
||||
return rejectedTypeEnum.map(each -> each.rejectedHandler.getClass().getSimpleName()).orElse("");
|
||||
}
|
||||
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.wrap;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.common.CommonThreadPool;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Dynamic threadPool wrap.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/20 16:55
|
||||
*/
|
||||
@Data
|
||||
public class DynamicThreadPoolWrap {
|
||||
|
||||
private String tenantId;
|
||||
|
||||
private String itemId;
|
||||
|
||||
private String tpId;
|
||||
|
||||
private boolean subscribeFlag;
|
||||
|
||||
private CustomThreadPoolExecutor pool;
|
||||
|
||||
/**
|
||||
* 首选服务端线程池, 为空使用默认线程池 {@link CommonThreadPool#getInstance(String)}
|
||||
*
|
||||
* @param threadPoolId
|
||||
*/
|
||||
public DynamicThreadPoolWrap(String threadPoolId) {
|
||||
this(threadPoolId, CommonThreadPool.getInstance(threadPoolId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 首选服务端线程池, 为空使用 threadPoolExecutor
|
||||
*
|
||||
* @param threadPoolId
|
||||
* @param threadPoolExecutor
|
||||
*/
|
||||
public DynamicThreadPoolWrap(String threadPoolId, CustomThreadPoolExecutor threadPoolExecutor) {
|
||||
this.tpId = threadPoolId;
|
||||
this.pool = threadPoolExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交任务
|
||||
*
|
||||
* @param command
|
||||
*/
|
||||
public void execute(Runnable command) {
|
||||
pool.execute(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交任务
|
||||
*
|
||||
* @param task
|
||||
* @return
|
||||
*/
|
||||
public Future<?> submit(Runnable task) {
|
||||
return pool.submit(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交任务
|
||||
*
|
||||
* @param task
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
return pool.submit(task);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package com.github.dynamic.threadpool.starter.wrap;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.core.Listener;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Manager listener wrap.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/22 17:47
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ManagerListenerWrap {
|
||||
|
||||
private String lastCallMd5;
|
||||
|
||||
final Listener listener;
|
||||
|
||||
public ManagerListenerWrap(String md5, Listener listener) {
|
||||
this.lastCallMd5 = md5;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.github.dynamic.threadpool.starter.config.DynamicThreadPoolAutoConfiguration
|
@ -1,15 +0,0 @@
|
||||
package com.github.dynamic.threadpool.example;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.enable.EnableDynamicThreadPool;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDynamicThreadPool
|
||||
public class ExampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ExampleApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package com.github.dynamic.threadpool.example.config;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.core.DynamicThreadPool;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.CustomThreadPoolExecutor;
|
||||
import com.github.dynamic.threadpool.starter.toolkit.thread.ThreadPoolBuilder;
|
||||
import com.github.dynamic.threadpool.starter.wrap.DynamicThreadPoolWrap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import static com.github.dynamic.threadpool.example.constant.GlobalTestConstant.*;
|
||||
|
||||
/**
|
||||
* Thread pool config.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/6/20 17:16
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class ThreadPoolConfig {
|
||||
|
||||
/**
|
||||
* {@link DynamicThreadPoolWrap} 完成 Server 端订阅配置功能.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public DynamicThreadPoolWrap messageCenterConsumeThreadPool() {
|
||||
return new DynamicThreadPoolWrap(MESSAGE_CONSUME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 {@link DynamicThreadPool} 修饰 {@link CustomThreadPoolExecutor} 完成 Server 端订阅配置功能.
|
||||
* <p>
|
||||
* 由动态线程池注解修饰后, IOC 容器中保存的是 {@link CustomThreadPoolExecutor}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@DynamicThreadPool
|
||||
public ThreadPoolExecutor customThreadPoolExecutor() {
|
||||
return ThreadPoolBuilder.builder().threadFactory(MESSAGE_PRODUCE).isCustomPool(true).build();
|
||||
}
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package com.github.dynamic.threadpool.example.inittest;
|
||||
|
||||
import com.github.dynamic.threadpool.starter.core.GlobalThreadPoolManage;
|
||||
import com.github.dynamic.threadpool.starter.wrap.DynamicThreadPoolWrap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.github.dynamic.threadpool.example.constant.GlobalTestConstant.MESSAGE_PRODUCE;
|
||||
|
||||
/**
|
||||
* Test run time metrics.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/8/15 21:00
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RunStateHandlerTest {
|
||||
|
||||
// @PostConstruct
|
||||
@SuppressWarnings("all")
|
||||
public void runStateHandlerTest() {
|
||||
log.info("Test thread pool runtime state interface, The rejection policy will be triggered after 30s...");
|
||||
|
||||
ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduledThreadPool.scheduleAtFixedRate(() -> {
|
||||
DynamicThreadPoolWrap executorService = GlobalThreadPoolManage.getExecutorService(MESSAGE_PRODUCE);
|
||||
ThreadPoolExecutor pool = executorService.getPool();
|
||||
try {
|
||||
pool.execute(() -> {
|
||||
log.info("Thread pool name :: {}, Executing incoming blocking...", Thread.currentThread().getName());
|
||||
try {
|
||||
int maxRandom = 10;
|
||||
int temp = 2;
|
||||
Random random = new Random();
|
||||
// Assignment thread pool completedTaskCount
|
||||
if (random.nextInt(maxRandom) % temp == 0) {
|
||||
Thread.sleep(10241024);
|
||||
} else {
|
||||
Thread.sleep(3000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
}, 5, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
server:
|
||||
port: 8088
|
||||
servlet:
|
||||
context-path: /example
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
application:
|
||||
name: dynamic-threadpool-example
|
||||
dynamic:
|
||||
thread-pool:
|
||||
notifys:
|
||||
- type: DING
|
||||
url: https://oapi.dingtalk.com/robot/send?access_token=
|
||||
token: 4a582a588a161d6e3a1bd1de7eea9ee9f562cdfcbe56b6e72029e7fd512b2eae
|
||||
receives: '15601166691'
|
||||
alarm-interval: 5
|
||||
server-addr: http://localhost:6691
|
||||
namespace: prescription
|
||||
item-id: ${spring.application.name}
|
@ -0,0 +1,95 @@
|
||||
package cn.hippo4j.auth.config;
|
||||
|
||||
import cn.hippo4j.auth.constant.Constants;
|
||||
import cn.hippo4j.auth.filter.JWTAuthenticationFilter;
|
||||
import cn.hippo4j.auth.filter.JWTAuthorizationFilter;
|
||||
import cn.hippo4j.auth.security.JwtTokenManager;
|
||||
import cn.hippo4j.auth.service.impl.UserDetailsServiceImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 安全配置.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 21:10
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Resource
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Resource
|
||||
private JwtTokenManager tokenManager;
|
||||
|
||||
@Bean
|
||||
public UserDetailsService customUserService() {
|
||||
return new UserDetailsServiceImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.addAllowedMethod(Constants.SPLIT_STAR);
|
||||
config.applyPermitDefaultValues();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.cors().and().csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/static/**", "/index.html", "/favicon.ico", "/avatar.jpg").permitAll()
|
||||
.antMatchers("/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs").anonymous()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
|
||||
.addFilter(new JWTAuthorizationFilter(tokenManager, authenticationManager()))
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
String[] ignores = Stream.of("/hippo4j/v1/cs/auth/users/apply/token/**", "/hippo4j/v1/cs/configs/**").toArray(String[]::new);
|
||||
web.ignoring().antMatchers(ignores);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.hippo4j.auth.constant;
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 22:24
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
public static final String SPLIT_STAR = "*";
|
||||
|
||||
public static final String SPLIT_COMMA = ",";
|
||||
|
||||
public static final long TOKEN_VALIDITY_IN_SECONDS = 18000L;
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package cn.hippo4j.auth.filter;
|
||||
|
||||
import cn.hippo4j.auth.model.biz.user.JwtUser;
|
||||
import cn.hippo4j.auth.model.biz.user.LoginUser;
|
||||
import cn.hippo4j.auth.toolkit.JwtTokenUtil;
|
||||
import cn.hippo4j.auth.toolkit.ReturnT;
|
||||
import cn.hippo4j.common.web.base.Results;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.hippo4j.auth.constant.Constants.SPLIT_COMMA;
|
||||
import static cn.hippo4j.common.constant.Constants.BASE_PATH;
|
||||
import static cn.hippo4j.common.constant.Constants.MAP_INITIAL_CAPACITY;
|
||||
|
||||
/**
|
||||
* JWT authentication filter.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 22:21
|
||||
*/
|
||||
@Slf4j
|
||||
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
private final ThreadLocal<Integer> rememberMe = new ThreadLocal();
|
||||
|
||||
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
super.setFilterProcessesUrl(BASE_PATH + "/auth/login");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response) throws AuthenticationException {
|
||||
// 从输入流中获取到登录的信息
|
||||
try {
|
||||
LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
|
||||
rememberMe.set(loginUser.getRememberMe());
|
||||
return authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList())
|
||||
);
|
||||
} catch (IOException e) {
|
||||
logger.error("attemptAuthentication error :{}", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void successfulAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain chain,
|
||||
Authentication authResult) throws IOException {
|
||||
try {
|
||||
JwtUser jwtUser = (JwtUser) authResult.getPrincipal();
|
||||
boolean isRemember = rememberMe.get() == 1;
|
||||
|
||||
String role = "";
|
||||
Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities();
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
role = authority.getAuthority();
|
||||
}
|
||||
|
||||
String token = JwtTokenUtil.createToken(jwtUser.getId(), jwtUser.getUsername(), role, isRemember);
|
||||
response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + token);
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
Map<String, Object> maps = new HashMap(MAP_INITIAL_CAPACITY);
|
||||
maps.put("data", JwtTokenUtil.TOKEN_PREFIX + token);
|
||||
maps.put("roles", role.split(SPLIT_COMMA));
|
||||
response.getWriter().write(JSONUtil.toJsonStr(Results.success(maps)));
|
||||
} finally {
|
||||
rememberMe.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.getWriter().write(JSONUtil.toJsonStr(new ReturnT(-1, "Server Error")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package cn.hippo4j.auth.filter;
|
||||
|
||||
import cn.hippo4j.auth.security.JwtTokenManager;
|
||||
import cn.hippo4j.auth.toolkit.JwtTokenUtil;
|
||||
import cn.hippo4j.common.toolkit.JSONUtil;
|
||||
import cn.hippo4j.common.toolkit.UserContext;
|
||||
import cn.hippo4j.common.web.base.Results;
|
||||
import cn.hippo4j.common.web.exception.ServiceException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static cn.hippo4j.common.constant.Constants.ACCESS_TOKEN;
|
||||
import static cn.hippo4j.common.web.exception.ErrorCodeEnum.LOGIN_TIMEOUT;
|
||||
|
||||
/**
|
||||
* JWT authorization filter.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 22:21
|
||||
*/
|
||||
@Slf4j
|
||||
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
|
||||
|
||||
private final JwtTokenManager tokenManager;
|
||||
|
||||
public JWTAuthorizationFilter(JwtTokenManager tokenManager, AuthenticationManager authenticationManager) {
|
||||
super(authenticationManager);
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
// 验证客户端交互时 Token
|
||||
String accessToken = request.getParameter(ACCESS_TOKEN);
|
||||
if (StrUtil.isNotBlank(accessToken)) {
|
||||
tokenManager.validateToken(accessToken);
|
||||
|
||||
Authentication authentication = this.tokenManager.getAuthentication(accessToken);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果请求头中没有 Authorization 信息则直接放行
|
||||
String tokenHeader = request.getHeader(JwtTokenUtil.TOKEN_HEADER);
|
||||
if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果请求头中有 Token, 则进行解析, 并且设置认证信息
|
||||
try {
|
||||
SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
|
||||
} catch (Exception ex) {
|
||||
// 返回 Json 形式的错误信息
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("application/json; charset=utf-8");
|
||||
String resultStatus = "-1";
|
||||
if (ex instanceof ServiceException) {
|
||||
ServiceException serviceException = (ServiceException) ex;
|
||||
resultStatus = serviceException.errorCode.getCode();
|
||||
}
|
||||
response.getWriter().write(JSONUtil.toJSONString(Results.failure(resultStatus, ex.getMessage())));
|
||||
response.getWriter().flush();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
super.doFilterInternal(request, response, chain);
|
||||
} finally {
|
||||
UserContext.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Token 中获取用户信息并新建一个 Token.
|
||||
*
|
||||
* @param tokenHeader
|
||||
* @return
|
||||
*/
|
||||
private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) {
|
||||
String token = tokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");
|
||||
boolean expiration = JwtTokenUtil.isExpiration(token);
|
||||
if (expiration) {
|
||||
throw new ServiceException(LOGIN_TIMEOUT);
|
||||
}
|
||||
|
||||
String username = JwtTokenUtil.getUsername(token);
|
||||
String userRole = JwtTokenUtil.getUserRole(token);
|
||||
UserContext.setUserInfo(username, userRole);
|
||||
|
||||
String role = JwtTokenUtil.getUserRole(token);
|
||||
if (username != null) {
|
||||
return new UsernamePasswordAuthenticationToken(username, null,
|
||||
Collections.singleton(new SimpleGrantedAuthority(role))
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.github.dynamic.threadpool.auth.mapper;
|
||||
package cn.hippo4j.auth.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.github.dynamic.threadpool.auth.model.PermissionInfo;
|
||||
import cn.hippo4j.auth.model.PermissionInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
@ -1,7 +1,7 @@
|
||||
package com.github.dynamic.threadpool.auth.mapper;
|
||||
package cn.hippo4j.auth.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.github.dynamic.threadpool.auth.model.RoleInfo;
|
||||
import cn.hippo4j.auth.model.RoleInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
@ -1,7 +1,7 @@
|
||||
package com.github.dynamic.threadpool.auth.mapper;
|
||||
package cn.hippo4j.auth.mapper;
|
||||
|
||||
import cn.hippo4j.auth.model.UserInfo;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.github.dynamic.threadpool.auth.model.UserInfo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
@ -1,4 +1,4 @@
|
||||
package com.github.dynamic.threadpool.auth.model.biz.permission;
|
||||
package cn.hippo4j.auth.model.biz.permission;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.Data;
|
@ -1,4 +1,4 @@
|
||||
package com.github.dynamic.threadpool.auth.model.biz.permission;
|
||||
package cn.hippo4j.auth.model.biz.permission;
|
||||
|
||||
import lombok.Data;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.github.dynamic.threadpool.auth.model.biz.role;
|
||||
package cn.hippo4j.auth.model.biz.role;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.Data;
|
@ -1,4 +1,4 @@
|
||||
package com.github.dynamic.threadpool.auth.model.biz.role;
|
||||
package cn.hippo4j.auth.model.biz.role;
|
||||
|
||||
import lombok.Data;
|
||||
|
@ -0,0 +1,58 @@
|
||||
package cn.hippo4j.auth.model.biz.user;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Jwt user.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 22:34
|
||||
*/
|
||||
@Data
|
||||
public class JwtUser implements UserDetails {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* userName
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* password
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* authorities
|
||||
*/
|
||||
private Collection<? extends GrantedAuthority> authorities;
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.hippo4j.auth.model.biz.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Login user.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 22:41
|
||||
*/
|
||||
@Data
|
||||
public class LoginUser {
|
||||
|
||||
/**
|
||||
* username
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* password
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* rememberMe
|
||||
*/
|
||||
private Integer rememberMe;
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package cn.hippo4j.auth.model.biz.user;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* User req dto.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/11 20:30
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class UserReqDTO extends Page {
|
||||
|
||||
/**
|
||||
* userName
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* password
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* role
|
||||
*/
|
||||
private String role;
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cn.hippo4j.auth.security;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Auth manager
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/12/20 20:34
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class AuthManager {
|
||||
|
||||
private final JwtTokenManager jwtTokenManager;
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
/**
|
||||
* Resolve token from user.
|
||||
*
|
||||
* @param userName
|
||||
* @param rawPassword
|
||||
* @return
|
||||
* @throws AccessException
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String resolveTokenFromUser(String userName, String rawPassword) {
|
||||
try {
|
||||
UsernamePasswordAuthenticationToken authenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(userName, rawPassword);
|
||||
authenticationManager.authenticate(authenticationToken);
|
||||
} catch (AuthenticationException e) {
|
||||
throw new AccessException("Unknown user.");
|
||||
}
|
||||
|
||||
return jwtTokenManager.createToken(userName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package cn.hippo4j.auth.security;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.hippo4j.auth.constant.Constants.TOKEN_VALIDITY_IN_SECONDS;
|
||||
import static cn.hippo4j.auth.toolkit.JwtTokenUtil.SECRET;
|
||||
import static cn.hippo4j.common.constant.Constants.AUTHORITIES_KEY;
|
||||
|
||||
/**
|
||||
* Jwt token manager.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/12/20 20:36
|
||||
*/
|
||||
@Component
|
||||
public class JwtTokenManager {
|
||||
|
||||
public String createToken(String userName) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
Date validity;
|
||||
validity = new Date(now + TOKEN_VALIDITY_IN_SECONDS * 1000L);
|
||||
|
||||
Claims claims = Jwts.claims().setSubject(userName);
|
||||
return Jwts.builder().setClaims(claims).setExpiration(validity)
|
||||
.signWith(SignatureAlgorithm.HS512, SECRET).compact();
|
||||
}
|
||||
|
||||
public void validateToken(String token) {
|
||||
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get auth Info.
|
||||
*
|
||||
* @param token token
|
||||
* @return auth info
|
||||
*/
|
||||
public Authentication getAuthentication(String token) {
|
||||
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
|
||||
|
||||
List<GrantedAuthority> authorities = AuthorityUtils
|
||||
.commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY));
|
||||
|
||||
User principal = new User(claims.getSubject(), StrUtil.EMPTY, authorities);
|
||||
return new UsernamePasswordAuthenticationToken(principal, StrUtil.EMPTY, authorities);
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.github.dynamic.threadpool.auth.service;
|
||||
package cn.hippo4j.auth.service;
|
||||
|
||||
import cn.hippo4j.auth.model.biz.permission.PermissionRespDTO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.dynamic.threadpool.auth.model.biz.permission.PermissionRespDTO;
|
||||
|
||||
/**
|
||||
* Permission service.
|
@ -1,7 +1,7 @@
|
||||
package com.github.dynamic.threadpool.auth.service;
|
||||
package cn.hippo4j.auth.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.dynamic.threadpool.auth.model.biz.role.RoleRespDTO;
|
||||
import cn.hippo4j.auth.model.biz.role.RoleRespDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -0,0 +1,42 @@
|
||||
package cn.hippo4j.auth.service.impl;
|
||||
|
||||
import cn.hippo4j.auth.mapper.UserMapper;
|
||||
import cn.hippo4j.auth.model.UserInfo;
|
||||
import cn.hippo4j.auth.model.biz.user.JwtUser;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* User details service impl.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 22:26
|
||||
*/
|
||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
|
||||
UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, userName));
|
||||
|
||||
JwtUser jwtUser = new JwtUser();
|
||||
jwtUser.setId(userInfo.getId());
|
||||
jwtUser.setUsername(userName);
|
||||
jwtUser.setPassword(userInfo.getPassword());
|
||||
|
||||
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority(userInfo.getRole() + ""));
|
||||
jwtUser.setAuthorities(authorities);
|
||||
|
||||
return jwtUser;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package cn.hippo4j.auth.service.impl;
|
||||
|
||||
import cn.hippo4j.auth.mapper.UserMapper;
|
||||
import cn.hippo4j.auth.model.UserInfo;
|
||||
import cn.hippo4j.auth.model.biz.user.UserQueryPageReqDTO;
|
||||
import cn.hippo4j.auth.model.biz.user.UserReqDTO;
|
||||
import cn.hippo4j.auth.model.biz.user.UserRespDTO;
|
||||
import cn.hippo4j.auth.service.RoleService;
|
||||
import cn.hippo4j.auth.service.UserService;
|
||||
import cn.hippo4j.common.toolkit.StringUtil;
|
||||
import cn.hippo4j.common.web.exception.ServiceException;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* User service impl.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/10/30 21:40
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private final UserMapper userMapper;
|
||||
|
||||
private final RoleService roleService;
|
||||
|
||||
private final BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||
|
||||
@Override
|
||||
public IPage<UserRespDTO> listUser(UserQueryPageReqDTO reqDTO) {
|
||||
LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery(UserInfo.class)
|
||||
.eq(StringUtil.isNotBlank(reqDTO.getUserName()), UserInfo::getUserName, reqDTO.getUserName());
|
||||
IPage<UserInfo> selectPage = userMapper.selectPage(reqDTO, queryWrapper);
|
||||
|
||||
return selectPage.convert(each -> BeanUtil.toBean(each, UserRespDTO.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUser(UserReqDTO reqDTO) {
|
||||
LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery(UserInfo.class)
|
||||
.eq(UserInfo::getUserName, reqDTO.getUserName());
|
||||
UserInfo existUserInfo = userMapper.selectOne(queryWrapper);
|
||||
if (existUserInfo != null) {
|
||||
throw new RuntimeException("用户名重复");
|
||||
}
|
||||
|
||||
reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword()));
|
||||
UserInfo insertUser = BeanUtil.toBean(reqDTO, UserInfo.class);
|
||||
userMapper.insert(insertUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUser(UserReqDTO reqDTO) {
|
||||
if (StrUtil.isNotBlank(reqDTO.getPassword())) {
|
||||
reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword()));
|
||||
}
|
||||
UserInfo updateUser = BeanUtil.toBean(reqDTO, UserInfo.class);
|
||||
|
||||
LambdaUpdateWrapper<UserInfo> updateWrapper = Wrappers.lambdaUpdate(UserInfo.class)
|
||||
.eq(UserInfo::getUserName, reqDTO.getUserName());
|
||||
userMapper.update(updateUser, updateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUser(String userName) {
|
||||
LambdaUpdateWrapper<UserInfo> updateWrapper = Wrappers.lambdaUpdate(UserInfo.class)
|
||||
.eq(UserInfo::getUserName, userName);
|
||||
userMapper.delete(updateWrapper);
|
||||
// roleService.deleteRole("", userName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserLikeUsername(String userName) {
|
||||
LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery(UserInfo.class)
|
||||
.like(UserInfo::getUserName, userName)
|
||||
.select(UserInfo::getUserName);
|
||||
|
||||
List<UserInfo> userInfos = userMapper.selectList(queryWrapper);
|
||||
List<String> userNames = userInfos.stream().map(UserInfo::getUserName).collect(Collectors.toList());
|
||||
|
||||
return userNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserRespDTO getUser(UserReqDTO reqDTO) {
|
||||
Wrapper queryWrapper = Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, reqDTO.getUserName());
|
||||
UserInfo userInfo = userMapper.selectOne(queryWrapper);
|
||||
|
||||
UserRespDTO respUser = Optional.ofNullable(userInfo)
|
||||
.map(each -> BeanUtil.toBean(each, UserRespDTO.class))
|
||||
.orElseThrow(() -> new ServiceException("查询无此用户, 可以尝试清空缓存或退出登录."));
|
||||
return respUser;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package cn.hippo4j.auth.toolkit;
|
||||
|
||||
import cn.hippo4j.auth.constant.Constants;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.hippo4j.common.constant.Constants.MAP_INITIAL_CAPACITY;
|
||||
|
||||
/**
|
||||
* Jwt token util.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/9 22:43
|
||||
*/
|
||||
public class JwtTokenUtil {
|
||||
|
||||
public static final String TOKEN_HEADER = "Authorization";
|
||||
public static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
public static final String SECRET = "SecretKey039245678901232039487623456783092349288901402967890140939827";
|
||||
public static final String ISS = "admin";
|
||||
|
||||
/**
|
||||
* 角色的 Key
|
||||
*/
|
||||
private static final String ROLE_CLAIMS = "rol";
|
||||
|
||||
/**
|
||||
* 过期时间是 3600 秒, 既 24 小时
|
||||
*/
|
||||
private static final long EXPIRATION = 86400L;
|
||||
|
||||
/**
|
||||
* 选择了记住我之后的过期时间为 7 天
|
||||
*/
|
||||
private static final long EXPIRATION_REMEMBER = 7 * EXPIRATION;
|
||||
|
||||
/**
|
||||
* 创建 Token.
|
||||
*
|
||||
* @param id
|
||||
* @param username
|
||||
* @param role
|
||||
* @param isRememberMe
|
||||
* @return
|
||||
*/
|
||||
public static String createToken(Long id, String username, String role, boolean isRememberMe) {
|
||||
long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
|
||||
HashMap<String, Object> map = new HashMap(MAP_INITIAL_CAPACITY);
|
||||
map.put(ROLE_CLAIMS, role);
|
||||
return Jwts.builder()
|
||||
.signWith(SignatureAlgorithm.HS512, SECRET)
|
||||
.setClaims(map)
|
||||
.setIssuer(ISS)
|
||||
.setSubject(id + Constants.SPLIT_COMMA + username)
|
||||
.setIssuedAt(new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* Token 中获取用户名.
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static String getUsername(String token) {
|
||||
List<String> userInfo = Arrays.asList(getTokenBody(token).getSubject().split(Constants.SPLIT_COMMA));
|
||||
return userInfo.get(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Token 中获取用户名.
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static Integer getUserId(String token) {
|
||||
List<String> userInfo = Arrays.asList(getTokenBody(token).getSubject().split(Constants.SPLIT_COMMA));
|
||||
return Integer.parseInt(userInfo.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色.
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static String getUserRole(String token) {
|
||||
return (String) getTokenBody(token).get(ROLE_CLAIMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已过期.
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static boolean isExpiration(String token) {
|
||||
try {
|
||||
return getTokenBody(token).getExpiration().before(new Date());
|
||||
} catch (ExpiredJwtException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static Claims getTokenBody(String token) {
|
||||
return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cn.hippo4j.auth.toolkit;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* ReturnT.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/11/10 00:00
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ReturnT<T> implements Serializable {
|
||||
|
||||
public static final long serialVersionUID = 42L;
|
||||
|
||||
public static final int SUCCESS_CODE = 200;
|
||||
public static final int FAIL_CODE = 500;
|
||||
|
||||
public static final ReturnT<String> SUCCESS = new ReturnT<>(null);
|
||||
public static final ReturnT<String> FAIL = new ReturnT<>(FAIL_CODE, null);
|
||||
|
||||
private int code;
|
||||
private String msg;
|
||||
private T content;
|
||||
|
||||
public ReturnT(int code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public ReturnT(T content) {
|
||||
this.code = SUCCESS_CODE;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cn.hippo4j.common.api;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* Client close hook execute.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2022/1/6 22:14
|
||||
*/
|
||||
public interface ClientCloseHookExecute {
|
||||
|
||||
/**
|
||||
* Client close hook function execution.
|
||||
*
|
||||
* @param req
|
||||
*/
|
||||
void closeHook(ClientCloseHookReq req);
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
class ClientCloseHookReq {
|
||||
|
||||
/**
|
||||
* appName
|
||||
*/
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* instanceId
|
||||
*/
|
||||
private String instanceId;
|
||||
|
||||
/**
|
||||
* groupKey
|
||||
*/
|
||||
private String groupKey;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cn.hippo4j.common.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Json facade.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/12/13 20:01
|
||||
*/
|
||||
public interface JsonFacade {
|
||||
|
||||
/**
|
||||
* To JSON string.
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
String toJSONString(Object object);
|
||||
|
||||
/**
|
||||
* Parse object.
|
||||
*
|
||||
* @param text
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> T parseObject(String text, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Parse array.
|
||||
*
|
||||
* @param text
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> parseArray(String text, Class<T> clazz);
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.hippo4j.common.api;
|
||||
|
||||
import cn.hippo4j.common.notify.NotifyConfigDTO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Notify config builder.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2022/2/24 19:50
|
||||
*/
|
||||
public interface NotifyConfigBuilder {
|
||||
|
||||
/**
|
||||
* Build notify.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<String, List<NotifyConfigDTO>> buildNotify();
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package cn.hippo4j.common.api;
|
||||
|
||||
import cn.hippo4j.common.model.ThreadDetailStateInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Get thread status in thread pool.
|
||||
*
|
||||
* @param threadPoolExecutor
|
||||
* @return
|
||||
*/
|
||||
List<ThreadDetailStateInfo> getThreadDetailStateInfo(ThreadPoolExecutor threadPoolExecutor);
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.hippo4j.common.api;
|
||||
|
||||
/**
|
||||
* Thread pool dynamic refresh.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2022/2/26 12:26
|
||||
*/
|
||||
public interface ThreadPoolDynamicRefresh {
|
||||
|
||||
/**
|
||||
* Dynamic refresh.
|
||||
*
|
||||
* @param content
|
||||
*/
|
||||
void dynamicRefresh(String content);
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.github.dynamic.threadpool.common.config;
|
||||
package cn.hippo4j.common.config;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
@ -1,4 +1,4 @@
|
||||
package com.github.dynamic.threadpool.starter.toolkit.thread;
|
||||
package cn.hippo4j.common.design.builder;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -0,0 +1,134 @@
|
||||
package cn.hippo4j.common.design.observer;
|
||||
|
||||
import cn.hippo4j.common.toolkit.CollectionUtil;
|
||||
import cn.hippo4j.common.toolkit.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Send observer notification.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/12/25 19:47
|
||||
*/
|
||||
@Slf4j
|
||||
public class AbstractSubjectCenter {
|
||||
|
||||
private static final Map<String, List<Observer>> OBSERVERS_MAP = new ConcurrentHashMap();
|
||||
|
||||
/**
|
||||
* Register observer.
|
||||
*
|
||||
* @param observer
|
||||
*/
|
||||
public static void register(Observer observer) {
|
||||
register(SubjectType.SPRING_CONTENT_REFRESHED.name(), observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register observer.
|
||||
*
|
||||
* @param subjectType
|
||||
* @param observer
|
||||
*/
|
||||
public static void register(SubjectType subjectType, Observer observer) {
|
||||
register(subjectType.name(), observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register observer.
|
||||
*
|
||||
* @param subject
|
||||
* @param observer
|
||||
*/
|
||||
public static void register(String subject, Observer observer) {
|
||||
if (StringUtil.isBlank(subject) || observer == null) {
|
||||
log.warn("Register observer. A string whose subject or observer is empty or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Observer> observers = OBSERVERS_MAP.get(subject);
|
||||
if (CollectionUtil.isEmpty(observers)) {
|
||||
observers = new ArrayList();
|
||||
}
|
||||
|
||||
observers.add(observer);
|
||||
OBSERVERS_MAP.put(subject, observers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove observer.
|
||||
*
|
||||
* @param observer
|
||||
*/
|
||||
public static void remove(Observer observer) {
|
||||
remove(SubjectType.SPRING_CONTENT_REFRESHED.name(), observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove observer.
|
||||
*
|
||||
* @param subject
|
||||
* @param observer
|
||||
*/
|
||||
public static void remove(String subject, Observer observer) {
|
||||
List<Observer> observers;
|
||||
if (StringUtil.isBlank(subject) || CollectionUtil.isEmpty((observers = OBSERVERS_MAP.get(subject))) || observer == null) {
|
||||
log.warn("Remove observer. A string whose subject or observer is empty or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
observers.remove(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify.
|
||||
*
|
||||
* @param subjectType
|
||||
* @param observerMessage
|
||||
*/
|
||||
public static void notify(SubjectType subjectType, ObserverMessage observerMessage) {
|
||||
notify(subjectType.name(), observerMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify.
|
||||
*
|
||||
* @param subject
|
||||
* @param observerMessage
|
||||
*/
|
||||
public static void notify(String subject, ObserverMessage observerMessage) {
|
||||
List<Observer> observers = OBSERVERS_MAP.get(subject);
|
||||
if (CollectionUtil.isEmpty(observers)) {
|
||||
log.warn("Under the subject, there is no observer group.");
|
||||
return;
|
||||
}
|
||||
|
||||
observers.parallelStream().forEach(each -> {
|
||||
try {
|
||||
each.accept(observerMessage);
|
||||
} catch (Exception ex) {
|
||||
log.error("Notification subject :: {} observer exception", subject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public enum SubjectType {
|
||||
|
||||
/**
|
||||
* Spring content refreshed.
|
||||
*/
|
||||
SPRING_CONTENT_REFRESHED,
|
||||
|
||||
/**
|
||||
* Clear config cache.
|
||||
*/
|
||||
CLEAR_CONFIG_CACHE
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.hippo4j.common.design.observer;
|
||||
|
||||
/**
|
||||
* Observer.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/12/25 19:46
|
||||
*/
|
||||
public interface Observer<T> {
|
||||
|
||||
/**
|
||||
* Receive notification.
|
||||
*
|
||||
* @param observerMessage
|
||||
*/
|
||||
void accept(ObserverMessage<T> observerMessage);
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.hippo4j.common.design.observer;
|
||||
|
||||
/**
|
||||
* Message notifying observer.
|
||||
*
|
||||
* @author chen.ma
|
||||
* @date 2021/12/25 19:54
|
||||
*/
|
||||
public interface ObserverMessage<T> {
|
||||
|
||||
/**
|
||||
* Message.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
T message();
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue