业务上为了支撑动态刷新线程池核心参数,所以集成Nacos配置中心实现自动刷新。
前提是系统已经集成Nacos相关依赖,比如
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
做了个注解启动动态线程池开关@EnableDiversityThreadPool,使用时加在启动类即可
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({DtpFactory.class})
public @interface EnableDiversityThreadPool {
/**
* 初始化IO密集型线程池
*/
boolean initIOPool() default true;
/**
* 初始化CPU密集型线程池
*/
boolean initCPUPool() default true;
/**
* 初始化优先级调度线程池
*/
boolean initPriorityPool() default true;
/**
* 初始化future任务线程池,若关闭,则默认使用ForkJoinPool
*/
boolean initFuturePool() default true;
/**
* 自动扩容核心线程
*/
boolean autoExpansion() default true;
/**
* 是否回收核心线程
*/
boolean allowCoreThreadTimeOut() default false;
/**
* 线程空闲存活时间,超过则回收 单位:TimeUnit.SECONDS
*/
long keepAliveTime() default 5;
}
注册Nacos配置监听回调方法即可,基于注解@ConditionalOnClass(value = NacosConfigManager.class)实现自动装配。
基于Spring容器启动事件ApplicationStartedEvent @EventListener实现监听注册
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ByteArrayResource;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
/**
* @author rui.wang
* @ Version: 1.0
* @ Time: 2023/5/16 17:28
* @ Description:
*/
@Slf4j
@Configuration
@ConditionalOnClass(value = NacosConfigManager.class)
public class NacosEventListenerConfiguration {
@Autowired
private NacosConfigManager nacosConfigManager;
@Autowired
private Environment environment;
@Resource
private DtpProperties dtpProperties;
@EventListener
public void listener(ApplicationStartedEvent event) throws NacosException {
EnableDiversityThreadPool annotation = AnnotationUtils.getAnnotation(event.getSpringApplication().getMainApplicationClass(),
EnableDiversityThreadPool.class);
if (annotation != null) {
init();
}
}
public void init() throws NacosException {
Listener listener = new AbstractListener() {
@Override
public void receiveConfigInfo(String configInfo) {
val properties = doParse(configInfo);
bindDtpProperties(properties, dtpProperties);
log.info("Nacos config refresh dtpProperties:{}", dtpProperties.toString());
doRefresh(dtpProperties);
}
};
String[] profiles = environment.getActiveProfiles();
if (profiles.length < 1) {
profiles = environment.getDefaultProfiles();
}
String appName = environment.getProperty("spring.application.name");
appName = StringUtils.isNoneBlank(appName) ? appName : "application";
String fileType = environment.getProperty("spring.cloud.nacos.config.file-extension");
fileType = StringUtils.isNoneBlank(fileType) ? fileType : "yml";
String group = environment.getProperty("spring.cloud.nacos.config.group");
group = StringUtils.isNoneBlank(group) ? group : DEFAULT_GROUP;
String dataId = appName + "-" + profiles[0] + "." + fileType;
nacosConfigManager.getConfigService().addListener(dataId, group, listener);
log.info("Nacos dtp config eventListener resistry success");
}
private void doRefresh(DtpProperties dtpProperties) {
DtpContext.IO_POOL.fixPoolSize(dtpProperties.getCorePoolSizeIO(), dtpProperties.getMaximumPoolSizeIO());
DtpContext.CPU_POOL.fixPoolSize(dtpProperties.getCorePoolSizeCPU(), dtpProperties.getMaximumPoolSizeCPU());
DtpContext.PRIORITY_POOL.fixPoolSize(dtpProperties.getCorePoolSizePriority(), dtpProperties.getMaximumPoolSizePriority());
DtpContext.FUTURE_POOL.fixPoolSize(dtpProperties.getCorePoolSizeFuture(), dtpProperties.getMaximumPoolSizePriority());
DtpContext.IO_POOL.autoExpansion.set(dtpProperties.isAutoExpansion());
DtpContext.CPU_POOL.autoExpansion.set(dtpProperties.isAutoExpansion());
DtpContext.PRIORITY_POOL.autoExpansion.set(dtpProperties.isAutoExpansion());
}
private Map<Object, Object> doParse(String configInfo) {
if (StringUtils.isEmpty(configInfo)) {
return Collections.emptyMap();
}
YamlPropertiesFactoryBean bean = new YamlPropertiesFactoryBean();
bean.setResources(new ByteArrayResource(configInfo.getBytes()));
return bean.getObject();
}
public static void bindDtpProperties(Map<?, Object> properties, DtpProperties dtpProperties) {
ConfigurationPropertySource sources = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(sources);
ResolvableType type = ResolvableType.forClass(DtpProperties.class);
Bindable<?> target = Bindable.of(type).withExistingValue(dtpProperties);
binder.bind("spring.dynamic.tp", target);
}
}
Properties
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "spring.dynamic.tp")
public class DtpProperties {
/**
* IO密集型线程池核心线程数
*/
private int corePoolSizeIO = 1;
/**
* IO密集型线程池最大线程数
*/
private int maximumPoolSizeIO = 1;
/**
* CPU密集型线程池核心线程数
*/
private int corePoolSizeCPU = 1;
/**
* CPU密集型线程池最大线程数
*/
private int maximumPoolSizeCPU = 1;
/**
* 优先级线程池核心线程数
*/
private int corePoolSizePriority = 1;
/**
* 优先级线程池最大线程数
*/
private int maximumPoolSizePriority = 1;
/**
* future线程池核心线程数
*/
private int corePoolSizeFuture = 1;
/**
* future线程池最大线程数
*/
private int maximumPoolSizeFuture = 1;
/**
* 阻塞队列默认容量
*/
private int poolQueueSize = 1000;
/**
* 是否开启自动扩容核心线程
*/
private boolean autoExpansion = true;
}