From 601c4f142e5b4af6011cf935236141709d757edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Miller=20=28=E9=94=BA=E4=BF=8A=29?= Date: Wed, 22 Sep 2021 14:41:42 +0800 Subject: [PATCH] perf($Quartz): customize Quartz thread pool by CPU core count --- .../src/main/resources/application.yml | 4 -- maf-mis/src/main/resources/application.yml | 4 -- .../quartz/QuartzConfiguration.java | 24 ++++++++ .../quartz/QuartzSchedulerPostProcessor.java | 61 +++++++++++++++++++ 4 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzSchedulerPostProcessor.java diff --git a/auth-center/src/main/resources/application.yml b/auth-center/src/main/resources/application.yml index df793cfe..6ae94403 100644 --- a/auth-center/src/main/resources/application.yml +++ b/auth-center/src/main/resources/application.yml @@ -110,10 +110,6 @@ spring: isClustered: true clusterCheckinInterval: 3000 useProperties: false - threadPool: - threadCount: 15 - threadPriority: 5 - class: org.quartz.simpl.SimpleThreadPool feign: httpclient: diff --git a/maf-mis/src/main/resources/application.yml b/maf-mis/src/main/resources/application.yml index 2805b2f3..ac253141 100644 --- a/maf-mis/src/main/resources/application.yml +++ b/maf-mis/src/main/resources/application.yml @@ -112,10 +112,6 @@ spring: isClustered: true clusterCheckinInterval: 3000 useProperties: false - threadPool: - threadCount: 15 - threadPriority: 5 - class: org.quartz.simpl.SimpleThreadPool feign: httpclient: diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzConfiguration.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzConfiguration.java index f9805ed8..d27a0d7b 100644 --- a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzConfiguration.java +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzConfiguration.java @@ -6,13 +6,17 @@ import org.quartz.Scheduler; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.quartz.QuartzProperties; +import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; +import java.util.Map; import java.util.Optional; +import java.util.Properties; /** * Description: QuartzConfiguration, change description here. @@ -22,6 +26,26 @@ @Slf4j @ConditionalOnClass({Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class}) public class QuartzConfiguration { + private static final String THREAD_COUNT = "org.quartz.threadPool.threadCount"; + + @Bean + public SchedulerFactoryBeanCustomizer threadPoolCustomizer(QuartzProperties quartzProperties) { + return schedulerFactoryBean -> { + val cpuCoreCount = Runtime.getRuntime().availableProcessors(); + val threadCount = String.valueOf(cpuCoreCount * 2); + quartzProperties.getProperties().put(THREAD_COUNT, threadCount); + log.warn("Quartz thread pool enhanced by current cpuCoreCount: {}, threadCount: {}", cpuCoreCount, + threadCount); + schedulerFactoryBean.setQuartzProperties(this.asProperties(quartzProperties.getProperties())); + }; + } + + private Properties asProperties(Map source) { + val properties = new Properties(); + properties.putAll(source); + return properties; + } + @Bean public GreetingQuartzJobBean greetingQuartzJobBean(MafProjectProperty mafProjectProperty) { log.warn("Initial bean: '{}'", GreetingQuartzJobBean.class.getSimpleName()); diff --git a/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzSchedulerPostProcessor.java b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzSchedulerPostProcessor.java new file mode 100644 index 00000000..da270426 --- /dev/null +++ b/spring-cloud-starter/src/main/java/com/jmsoftware/maf/springcloudstarter/quartz/QuartzSchedulerPostProcessor.java @@ -0,0 +1,61 @@ +package com.jmsoftware.maf.springcloudstarter.quartz; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.autoconfigure.quartz.QuartzProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +/** + *

QuartzSchedulerPostProcessor is to enhance Quartz thread pool by CPU core count.

+ *

Hyper-threading + * works by duplicating certain sections of the processor—those that store the + * architectural state + * —but not duplicating the main execution resources + * . This allows a hyper-threading processor to appear as the usual "physical" processor and an extra + * "logical + * " processor to the host operating system (HTT-unaware operating systems see two "physical" + * processors), allowing the operating system to schedule two threads or processes simultaneously and appropriately.

+ * + * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 8/25/2021 11:27 AM + **/ +@Slf4j +@RequiredArgsConstructor +public class QuartzSchedulerPostProcessor implements BeanPostProcessor, DisposableBean { + private static final String THREAD_COUNT = "org.quartz.threadPool.threadCount"; + private final ApplicationContext applicationContext; + private final QuartzProperties quartzProperties; + private int initializedBeanCounter = 0; + + @Nullable + @Override + public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { + this.initializedBeanCounter++; + if (bean instanceof SchedulerFactoryBean) { + this.enhanceThreadPool(); + } + return bean; + } + + private void enhanceThreadPool() { + log.info("Spring initialized {} beans previously, beanDefinitionCount: {}", this.initializedBeanCounter, + this.applicationContext.getBeanDefinitionCount()); + val cpuCoreCount = Runtime.getRuntime().availableProcessors(); + val threadCount = String.valueOf(cpuCoreCount * 2 + 1); + this.quartzProperties.getProperties().put(THREAD_COUNT, threadCount); + log.warn("Quartz thread pool enhanced by current cpuCoreCount: {}, threadCount: {}", cpuCoreCount, + threadCount); + this.applicationContext.getAutowireCapableBeanFactory().destroyBean(this); + } + + @Override + public void destroy() { + log.warn("Destroyed bean {}", this.getClass().getSimpleName()); + } +}