mirror of https://github.com/longtai-cn/hippo4j
commit
05bb17f94c
@ -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.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;
|
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.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;
|
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.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.github.dynamic.threadpool.auth.model.UserInfo;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
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 com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import lombok.Data;
|
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;
|
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 com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import lombok.Data;
|
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;
|
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.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.github.dynamic.threadpool.auth.model.biz.permission.PermissionRespDTO;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permission service.
|
* 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.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;
|
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.beans.BeansException;
|
||||||
import org.springframework.context.ApplicationContext;
|
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;
|
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