mirror of https://github.com/longtai-cn/hippo4j
parent
3791c6e7de
commit
ded7622277
@ -0,0 +1,28 @@
|
|||||||
|
package io.dynamict.hreadpool.common.executor;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executor Factory.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 18:35
|
||||||
|
*/
|
||||||
|
public class ExecutorFactory {
|
||||||
|
|
||||||
|
|
||||||
|
public static final class Managed {
|
||||||
|
|
||||||
|
private static final String DEFAULT_NAMESPACE = "dynamic.thread-pool";
|
||||||
|
|
||||||
|
private static final ThreadPoolManager THREAD_POOL_MANAGER = ThreadPoolManager.getInstance();
|
||||||
|
|
||||||
|
public static ScheduledExecutorService newSingleScheduledExecutorService(String group, ThreadFactory threadFactory) {
|
||||||
|
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1, threadFactory);
|
||||||
|
THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executorService);
|
||||||
|
return executorService;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package io.dynamict.hreadpool.common.executor;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread Pool Manager.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 18:36
|
||||||
|
*/
|
||||||
|
public class ThreadPoolManager {
|
||||||
|
|
||||||
|
private Map<String, Map<String, Set<ExecutorService>>> resourcesManager;
|
||||||
|
|
||||||
|
private Map<String, Object> lockers = new ConcurrentHashMap(8);
|
||||||
|
|
||||||
|
private static final ThreadPoolManager INSTANCE = new ThreadPoolManager();
|
||||||
|
|
||||||
|
public static ThreadPoolManager getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(String namespace, String group, ExecutorService executor) {
|
||||||
|
if (!resourcesManager.containsKey(namespace)) {
|
||||||
|
synchronized (this) {
|
||||||
|
lockers.put(namespace, new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Object monitor = lockers.get(namespace);
|
||||||
|
synchronized (monitor) {
|
||||||
|
Map<String, Set<ExecutorService>> map = resourcesManager.get(namespace);
|
||||||
|
if (map == null) {
|
||||||
|
map = new HashMap(8);
|
||||||
|
map.put(group, new HashSet());
|
||||||
|
map.get(group).add(executor);
|
||||||
|
resourcesManager.put(namespace, map);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!map.containsKey(group)) {
|
||||||
|
map.put(group, new HashSet());
|
||||||
|
}
|
||||||
|
map.get(group).add(executor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package io.dynamict.hreadpool.common.model;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 远程调用通用参数
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 21:08
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class GlobalRemotePoolInfo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 5447003335557127308L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命名空间
|
||||||
|
*/
|
||||||
|
private String namespace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目 ID
|
||||||
|
*/
|
||||||
|
private String itemId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程池标识
|
||||||
|
*/
|
||||||
|
private String tpId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心线程数
|
||||||
|
*/
|
||||||
|
private Integer coreSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大线程数
|
||||||
|
*/
|
||||||
|
private Integer maxSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 队列类型
|
||||||
|
*/
|
||||||
|
private Integer queueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 队列长度
|
||||||
|
*/
|
||||||
|
private Integer capacity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程存活时长
|
||||||
|
*/
|
||||||
|
private Integer keepAliveTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否告警
|
||||||
|
*/
|
||||||
|
private Integer isAlarm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 容量告警
|
||||||
|
*/
|
||||||
|
private Integer capacityAlarm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活跃度告警
|
||||||
|
*/
|
||||||
|
private Integer livenessAlarm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MD5
|
||||||
|
*/
|
||||||
|
private String md5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package io.dynamict.hreadpool.common.web.base;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局返回对象
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/3/19 16:12
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class Result<T> implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -4408341719434417427L;
|
||||||
|
|
||||||
|
public static final String SUCCESS_CODE = "0";
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return SUCCESS_CODE.equals(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFail() {
|
||||||
|
return !isSuccess();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package io.dynamict.hreadpool.common.web.base;
|
||||||
|
|
||||||
|
import io.dynamict.hreadpool.common.web.exception.ErrorCode;
|
||||||
|
import io.dynamict.hreadpool.common.web.exception.ServiceException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result 工具类
|
||||||
|
*
|
||||||
|
* @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(ErrorCode.SERVICE_ERROR.getCode())
|
||||||
|
.setMessage(serviceException.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> failure(String code, String message) {
|
||||||
|
return new Result<T>()
|
||||||
|
.setCode(code)
|
||||||
|
.setMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package io.dynamict.hreadpool.common.web.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常码
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/3/19 16:07
|
||||||
|
*/
|
||||||
|
public enum ErrorCode {
|
||||||
|
|
||||||
|
UNKNOWN_ERROR {
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "未知错误";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
VALIDATION_ERROR {
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "2";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "参数错误";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
SERVICE_ERROR {
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return "3";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "服务异常";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract String getCode();
|
||||||
|
|
||||||
|
public abstract String getMessage();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package io.dynamict.hreadpool.common.web.exception;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务系统业务逻辑相关异常
|
||||||
|
*
|
||||||
|
* @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,50 +0,0 @@
|
|||||||
package io.dynamic.threadpool.starter.http;
|
|
||||||
|
|
||||||
import io.dynamic.threadpool.starter.config.DynamicThreadPoolProperties;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Server Http Agent.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/6/23 20:50
|
|
||||||
*/
|
|
||||||
public class ServerHttpAgent implements HttpAgent {
|
|
||||||
|
|
||||||
private final DynamicThreadPoolProperties dynamicThreadPoolProperties;
|
|
||||||
|
|
||||||
public ServerHttpAgent(DynamicThreadPoolProperties properties) {
|
|
||||||
this.dynamicThreadPoolProperties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String httpGet(String path, Map<String, String> headers, Map<String, String> paramValues, String encoding, long readTimeoutMs) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String httpPost(String path, Map<String, String> headers, Map<String, String> paramValues, String encoding, long readTimeoutMs) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String httpDelete(String path, Map<String, String> headers, Map<String, String> paramValues, String encoding, long readTimeoutMs) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNameSpace() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEncode() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package io.dynamic.threadpool.starter.http;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Server List Manager.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/6/23 20:42
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class ServerListManager {
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package io.dynamic.threadpool.starter.listener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 长轮询执行
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/6/20 18:37
|
|
||||||
*/
|
|
||||||
public class LongPollingRunnable implements Runnable {
|
|
||||||
|
|
||||||
private final int taskId;
|
|
||||||
|
|
||||||
public LongPollingRunnable(Integer taskId) {
|
|
||||||
this.taskId = taskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,62 @@
|
|||||||
|
package io.dynamic.threadpool.starter.remote;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.starter.config.ApplicationContextHolder;
|
||||||
|
import io.dynamic.threadpool.starter.config.DynamicThreadPoolProperties;
|
||||||
|
import io.dynamic.threadpool.starter.toolkit.HttpClientUtil;
|
||||||
|
import io.dynamict.hreadpool.common.web.base.Result;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server Http Agent.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 20:50
|
||||||
|
*/
|
||||||
|
public class ServerHttpAgent implements HttpAgent {
|
||||||
|
|
||||||
|
private final DynamicThreadPoolProperties dynamicThreadPoolProperties;
|
||||||
|
|
||||||
|
private final ServerListManager serverListManager;
|
||||||
|
|
||||||
|
private HttpClientUtil httpClientUtil = ApplicationContextHolder.getBean(HttpClientUtil.class);
|
||||||
|
|
||||||
|
public ServerHttpAgent(DynamicThreadPoolProperties properties) {
|
||||||
|
this.dynamicThreadPoolProperties = properties;
|
||||||
|
this.serverListManager = new ServerListManager(dynamicThreadPoolProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result httpGet(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 httpPost(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 httpDelete(String path, Map<String, String> headers, Map<String, String> paramValues, long readTimeoutMs) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNameSpace() {
|
||||||
|
return dynamicThreadPoolProperties.getNamespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEncode() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildUrl(String path) {
|
||||||
|
return serverListManager.getCurrentServerAddr() + path;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
package io.dynamic.threadpool.starter.remote;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.starter.config.DynamicThreadPoolProperties;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server List Manager.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 20:42
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ServerListManager {
|
||||||
|
|
||||||
|
private static final String HTTPS = "https://";
|
||||||
|
|
||||||
|
private static final String HTTP = "http://";
|
||||||
|
|
||||||
|
private String serverAddrsStr;
|
||||||
|
|
||||||
|
volatile List<String> serverUrls = new ArrayList();
|
||||||
|
|
||||||
|
private volatile String currentServerAddr;
|
||||||
|
|
||||||
|
private Iterator<String> iterator;
|
||||||
|
|
||||||
|
private final DynamicThreadPoolProperties properties;
|
||||||
|
|
||||||
|
public ServerListManager(DynamicThreadPoolProperties dynamicThreadPoolProperties) {
|
||||||
|
this.properties = dynamicThreadPoolProperties;
|
||||||
|
serverAddrsStr = properties.getServerAddr();
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(serverAddrsStr)) {
|
||||||
|
List<String> serverAddrs = new ArrayList();
|
||||||
|
String[] serverAddrsArr = this.serverAddrsStr.split(",");
|
||||||
|
|
||||||
|
for (String serverAddr : serverAddrsArr) {
|
||||||
|
if (serverAddr.startsWith(HTTPS) || serverAddr.startsWith(HTTP)) {
|
||||||
|
// TODO 固定写,后面优化
|
||||||
|
currentServerAddr = serverAddr;
|
||||||
|
serverAddrs.add(serverAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.serverUrls = serverAddrs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentServerAddr() {
|
||||||
|
if (StringUtils.isEmpty(currentServerAddr)) {
|
||||||
|
iterator = iterator();
|
||||||
|
currentServerAddr = iterator.next();
|
||||||
|
}
|
||||||
|
return currentServerAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<String> iterator() {
|
||||||
|
if (serverUrls.isEmpty()) {
|
||||||
|
log.error("[iterator-serverlist] No server address defined!");
|
||||||
|
}
|
||||||
|
return new ServerAddressIterator(serverUrls);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ServerAddressIterator implements Iterator<String> {
|
||||||
|
|
||||||
|
final List<RandomizedServerAddress> sorted;
|
||||||
|
|
||||||
|
final Iterator<RandomizedServerAddress> iter;
|
||||||
|
|
||||||
|
public ServerAddressIterator(List<String> source) {
|
||||||
|
sorted = new ArrayList<RandomizedServerAddress>();
|
||||||
|
for (String address : source) {
|
||||||
|
sorted.add(new RandomizedServerAddress(address));
|
||||||
|
}
|
||||||
|
Collections.sort(sorted);
|
||||||
|
iter = sorted.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String next() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RandomizedServerAddress implements Comparable<RandomizedServerAddress> {
|
||||||
|
|
||||||
|
static Random random = new Random();
|
||||||
|
|
||||||
|
String serverIp;
|
||||||
|
|
||||||
|
int priority = 0;
|
||||||
|
|
||||||
|
int seed;
|
||||||
|
|
||||||
|
public RandomizedServerAddress(String ip) {
|
||||||
|
try {
|
||||||
|
this.serverIp = ip;
|
||||||
|
/*
|
||||||
|
change random scope from 32 to Integer.MAX_VALUE to fix load balance issue
|
||||||
|
*/
|
||||||
|
this.seed = random.nextInt(Integer.MAX_VALUE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(RandomizedServerAddress other) {
|
||||||
|
if (this.priority != other.priority) {
|
||||||
|
return other.priority - this.priority;
|
||||||
|
} else {
|
||||||
|
return other.seed - this.seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.dynamic.threadpool.starter.config.CommonConfiguration, \
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.dynamic.threadpool.starter.config.CommonConfiguration, \
|
||||||
io.dynamic.threadpool.starter.config.OkHttpClientConfig
|
io.dynamic.threadpool.starter.config.OkHttpClientConfig,\
|
||||||
|
io.dynamic.threadpool.starter.config.DynamicThreadPoolAutoConfiguration
|
@ -0,0 +1,12 @@
|
|||||||
|
package io.dynamic.threadpool.server.event;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.server.notify.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Data Change Event.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 19:13
|
||||||
|
*/
|
||||||
|
public class LocalDataChangeEvent extends Event {
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.ConcurrentHashSet;
|
||||||
|
import io.dynamic.threadpool.server.notify.listener.Subscriber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default event publisher implementation.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 19:06
|
||||||
|
*/
|
||||||
|
public class DefaultPublisher extends Thread implements EventPublisher {
|
||||||
|
|
||||||
|
protected final ConcurrentHashSet<Subscriber> subscribers = new ConcurrentHashSet();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addSubscriber(Subscriber subscriber) {
|
||||||
|
subscribers.add(subscriber);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.ConcurrentHashSet;
|
||||||
|
import io.dynamic.threadpool.server.notify.listener.Subscriber;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
||||||
|
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<Subscriber>();
|
||||||
|
newSet.add(subscriber);
|
||||||
|
subMappings.put(subSlowEventType, newSet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sets.add(subscriber);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract class for event.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 18:59
|
||||||
|
*/
|
||||||
|
public abstract class Event implements Serializable {
|
||||||
|
|
||||||
|
private static final AtomicLong SEQUENCE = new AtomicLong(0);
|
||||||
|
|
||||||
|
private final long sequence = SEQUENCE.getAndIncrement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event sequence number, which can be used to handle the sequence of events.
|
||||||
|
*
|
||||||
|
* @return sequence num, It's best to make sure it's monotone.
|
||||||
|
*/
|
||||||
|
public long sequence() {
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.server.notify.listener.Subscriber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event publisher.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 18:58
|
||||||
|
*/
|
||||||
|
public interface EventPublisher {
|
||||||
|
|
||||||
|
void addSubscriber(Subscriber subscriber);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.server.notify.listener.SmartSubscriber;
|
||||||
|
import io.dynamic.threadpool.server.notify.listener.Subscriber;
|
||||||
|
import io.dynamic.threadpool.server.toolkit.ClassUtil;
|
||||||
|
import io.dynamic.threadpool.server.toolkit.MapUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unified Event Notify Center.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 18:58
|
||||||
|
*/
|
||||||
|
public class NotifyCenter {
|
||||||
|
|
||||||
|
private static final NotifyCenter INSTANCE = new NotifyCenter();
|
||||||
|
|
||||||
|
public static int ringBufferSize = 16384;
|
||||||
|
|
||||||
|
private DefaultSharePublisher sharePublisher;
|
||||||
|
|
||||||
|
private static BiFunction<Class<? extends Event>, Integer, EventPublisher> publisherFactory = null;
|
||||||
|
|
||||||
|
private final Map<String, EventPublisher> publisherMap = new ConcurrentHashMap(16);
|
||||||
|
|
||||||
|
public static <T> void registerSubscriber(final Subscriber consumer) {
|
||||||
|
if (consumer instanceof SmartSubscriber) {
|
||||||
|
for (Class<? extends Event> subscribeType : ((SmartSubscriber) consumer).subscribeTypes()) {
|
||||||
|
if (ClassUtil.isAssignableFrom(SlowEvent.class, subscribeType)) {
|
||||||
|
INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType);
|
||||||
|
} else {
|
||||||
|
addSubscriber(consumer, subscribeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Class<? extends Event> subscribeType = consumer.subscribeType();
|
||||||
|
if (ClassUtil.isAssignableFrom(SlowEvent.class, subscribeType)) {
|
||||||
|
INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSubscriber(consumer, subscribeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSubscriber(final Subscriber consumer, Class<? extends Event> subscribeType) {
|
||||||
|
|
||||||
|
final String topic = ClassUtil.getCanonicalName(subscribeType);
|
||||||
|
synchronized (NotifyCenter.class) {
|
||||||
|
// MapUtils.computeIfAbsent is a unsafe method.
|
||||||
|
MapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, publisherFactory, subscribeType, ringBufferSize);
|
||||||
|
}
|
||||||
|
EventPublisher publisher = INSTANCE.publisherMap.get(topic);
|
||||||
|
publisher.addSubscriber(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slow Event.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 19:05
|
||||||
|
*/
|
||||||
|
public abstract class SlowEvent extends Event {
|
||||||
|
@Override
|
||||||
|
public long sequence() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify.listener;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.server.notify.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 {
|
||||||
|
|
||||||
|
public abstract List<Class<? extends Event>> subscribeTypes();
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package io.dynamic.threadpool.server.notify.listener;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.server.notify.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract subscriber class for subscriber interface.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 19:02
|
||||||
|
*/
|
||||||
|
public abstract class Subscriber<T extends Event> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event callback.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
public abstract void onEvent(T event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of this subscriber's subscription.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract Class<? extends Event> subscribeType();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package io.dynamic.threadpool.server.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 + "";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,184 @@
|
|||||||
|
package io.dynamic.threadpool.server.service;
|
||||||
|
|
||||||
|
import io.dynamic.threadpool.server.event.LocalDataChangeEvent;
|
||||||
|
import io.dynamic.threadpool.server.notify.Event;
|
||||||
|
import io.dynamic.threadpool.server.notify.NotifyCenter;
|
||||||
|
import io.dynamic.threadpool.server.notify.listener.Subscriber;
|
||||||
|
import io.dynamic.threadpool.server.toolkit.ConfigExecutor;
|
||||||
|
import io.dynamic.threadpool.server.toolkit.RequestUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.AsyncContext;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 长轮询服务
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/22 23:14
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class LongPollingService {
|
||||||
|
|
||||||
|
private static final int FIXED_POLLING_INTERVAL_MS = 10000;
|
||||||
|
|
||||||
|
public static final String LONG_POLLING_HEADER = "Long-Pulling-Timeout";
|
||||||
|
|
||||||
|
public static final String LONG_POLLING_NO_HANG_UP_HEADER = "Long-Pulling-Timeout-No-Hangup";
|
||||||
|
|
||||||
|
public static final String CLIENT_APPNAME_HEADER = "Client-AppName";
|
||||||
|
|
||||||
|
private Map<String, Long> retainIps = new ConcurrentHashMap();
|
||||||
|
|
||||||
|
public LongPollingService() {
|
||||||
|
allSubs = new ConcurrentLinkedQueue();
|
||||||
|
|
||||||
|
ConfigExecutor.scheduleLongPolling(new StatTask(), 0L, 10L, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
NotifyCenter.registerSubscriber(new Subscriber() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(Event event) {
|
||||||
|
if (isFixedPolling()) {
|
||||||
|
// Ignore.
|
||||||
|
} else {
|
||||||
|
if (event instanceof LocalDataChangeEvent) {
|
||||||
|
LocalDataChangeEvent evt = (LocalDataChangeEvent) event;
|
||||||
|
// ConfigExecutor.executeLongPolling(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Event> subscribeType() {
|
||||||
|
return LocalDataChangeEvent.class;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatTask implements Runnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
log.info("[long-pulling] client count " + allSubs.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataChangeTask implements Runnable {
|
||||||
|
|
||||||
|
final String groupKey;
|
||||||
|
|
||||||
|
final long changeTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
final boolean isBeta;
|
||||||
|
|
||||||
|
final List<String> betaIps;
|
||||||
|
|
||||||
|
DataChangeTask(String groupKey, boolean isBeta, List<String> betaIps) {
|
||||||
|
this.groupKey = groupKey;
|
||||||
|
this.isBeta = isBeta;
|
||||||
|
this.betaIps = betaIps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isSupportLongPolling(HttpServletRequest req) {
|
||||||
|
return null != req.getHeader(LONG_POLLING_HEADER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isFixedPolling() {
|
||||||
|
return SwitchService.getSwitchBoolean(SwitchService.FIXED_POLLING, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getFixedPollingInterval() {
|
||||||
|
return SwitchService.getSwitchInteger(SwitchService.FIXED_POLLING_INTERVAL, FIXED_POLLING_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, Map<String, String> clientMd5Map,
|
||||||
|
int probeRequestSize) {
|
||||||
|
String str = req.getHeader(LONG_POLLING_HEADER);
|
||||||
|
String noHangUpFlag = req.getHeader(LONG_POLLING_NO_HANG_UP_HEADER);
|
||||||
|
String appName = req.getHeader(CLIENT_APPNAME_HEADER);
|
||||||
|
int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500);
|
||||||
|
|
||||||
|
long timeout = Math.max(10000, Long.parseLong(str) - delayTime);
|
||||||
|
if (isFixedPolling()) {
|
||||||
|
timeout = Math.max(10000, getFixedPollingInterval());
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
String ip = RequestUtil.getRemoteIp(req);
|
||||||
|
|
||||||
|
final AsyncContext asyncContext = req.startAsync();
|
||||||
|
asyncContext.setTimeout(0L);
|
||||||
|
|
||||||
|
ConfigExecutor.executeLongPolling(new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Queue<ClientLongPolling> allSubs;
|
||||||
|
|
||||||
|
class ClientLongPolling implements Runnable {
|
||||||
|
|
||||||
|
final AsyncContext asyncContext;
|
||||||
|
|
||||||
|
final Map<String, String> clientMd5Map;
|
||||||
|
|
||||||
|
final long createTime;
|
||||||
|
|
||||||
|
final String ip;
|
||||||
|
|
||||||
|
final String appName;
|
||||||
|
|
||||||
|
final int probeRequestSize;
|
||||||
|
|
||||||
|
final long timeoutTime;
|
||||||
|
|
||||||
|
Future<?> asyncTimeoutFuture;
|
||||||
|
|
||||||
|
public ClientLongPolling(AsyncContext asyncContext, Map<String, String> clientMd5Map, String ip, int probeRequestSize, long timeout, String appName) {
|
||||||
|
this.asyncContext = asyncContext;
|
||||||
|
this.clientMd5Map = clientMd5Map;
|
||||||
|
this.ip = ip;
|
||||||
|
this.probeRequestSize = probeRequestSize;
|
||||||
|
this.timeoutTime = timeout;
|
||||||
|
this.appName = appName;
|
||||||
|
this.createTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
asyncTimeoutFuture = ConfigExecutor.scheduleLongPolling(() -> {
|
||||||
|
getRetainIps().put(ClientLongPolling.this.ip, System.currentTimeMillis());
|
||||||
|
allSubs.remove(ClientLongPolling.this);
|
||||||
|
|
||||||
|
if (isFixedPolling()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}, timeoutTime, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
allSubs.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Long> getRetainIps() {
|
||||||
|
return retainIps;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package io.dynamic.threadpool.server.service;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SwitchService.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 18:23
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class SwitchService {
|
||||||
|
|
||||||
|
public static final String FIXED_DELAY_TIME = "fixedDelayTime";
|
||||||
|
|
||||||
|
public static final String FIXED_POLLING = "isFixedPolling";
|
||||||
|
|
||||||
|
public static final String FIXED_POLLING_INTERVAL = "fixedPollingInertval";
|
||||||
|
|
||||||
|
private static volatile Map<String, String> switches = new HashMap(16);
|
||||||
|
|
||||||
|
public static int getSwitchInteger(String key, int defaultValue) {
|
||||||
|
int rtn = defaultValue;
|
||||||
|
try {
|
||||||
|
String status = switches.get(key);
|
||||||
|
rtn = status != null ? Integer.parseInt(status) : defaultValue;
|
||||||
|
} catch (Exception e) {
|
||||||
|
rtn = defaultValue;
|
||||||
|
log.error("corrupt switch value {}, {}", key, switches.get(key));
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getSwitchBoolean(String key, boolean defaultValue) {
|
||||||
|
boolean rtn = defaultValue;
|
||||||
|
try {
|
||||||
|
String value = switches.get(key);
|
||||||
|
rtn = value != null ? Boolean.parseBoolean(value) : defaultValue;
|
||||||
|
} catch (Exception e) {
|
||||||
|
rtn = defaultValue;
|
||||||
|
log.error("corrupt switch value {}, {}", key, switches.get(key));
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package io.dynamic.threadpool.server.toolkit;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Util.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 19:03
|
||||||
|
*/
|
||||||
|
public class ClassUtil {
|
||||||
|
|
||||||
|
public static boolean isAssignableFrom(Class clazz, Class cls) {
|
||||||
|
Objects.requireNonNull(cls, "cls");
|
||||||
|
return clazz.isAssignableFrom(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCanonicalName(Class cls) {
|
||||||
|
Objects.requireNonNull(cls, "cls");
|
||||||
|
return cls.getCanonicalName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package io.dynamic.threadpool.server.toolkit;
|
||||||
|
|
||||||
|
import cn.hutool.core.thread.ThreadFactoryBuilder;
|
||||||
|
import io.dynamict.hreadpool.common.executor.ExecutorFactory;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config Executor.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/6/23 18:33
|
||||||
|
*/
|
||||||
|
public class ConfigExecutor {
|
||||||
|
|
||||||
|
private static final ScheduledExecutorService LONG_POLLING_EXECUTOR = ExecutorFactory.Managed
|
||||||
|
.newSingleScheduledExecutorService("default group",
|
||||||
|
ThreadFactoryBuilder.create()
|
||||||
|
.setNamePrefix("io.dynamic.threadPool.config.LongPolling")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
public static void executeLongPolling(Runnable runnable) {
|
||||||
|
LONG_POLLING_EXECUTOR.execute(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ScheduledFuture<?> scheduleLongPolling(Runnable runnable, long period, TimeUnit unit) {
|
||||||
|
return LONG_POLLING_EXECUTOR.schedule(runnable, period, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void scheduleLongPolling(Runnable runnable, long initialDelay, long period, TimeUnit unit) {
|
||||||
|
LONG_POLLING_EXECUTOR.scheduleWithFixedDelay(runnable, initialDelay, period, unit);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package io.dynamic.threadpool.server.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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package io.dynamic.threadpool.server.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 final String CLIENT_APPNAME_HEADER = "Client-AppName";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue