欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Schedule定時(shí)任務(wù)在分布式產(chǎn)生的問(wèn)題詳解

 更新時(shí)間:2022年10月19日 08:46:31   作者:寧在春  
這篇文章主要介紹了Schedule定時(shí)任務(wù)在分布式產(chǎn)生的問(wèn)題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

定時(shí)任務(wù)的實(shí)現(xiàn)方式多種多樣,框架也是層出不窮。

本文所談及的是 SpringBoot 本身所帶有的@EnableScheduling 、 @Scheduled實(shí)現(xiàn)定時(shí)任務(wù)的方式。

以及采用這種方式,在分布式調(diào)度中可能會(huì)出現(xiàn)的問(wèn)題,又針對(duì)為什么會(huì)發(fā)生這種問(wèn)題?又該如何解決,做出了一些敘述。

為了適合每個(gè)階段的讀者,我把前面測(cè)試的代碼都貼出來(lái)啦~

確保每一步都是有跡可循的,希望大家不要嫌啰嗦,感謝

一、搭建基本環(huán)境

基本依賴

 <parent>
     <artifactId>spring-boot-parent</artifactId>
     <groupId>org.springframework.boot</groupId>
     <version>2.7.2</version>
 </parent>
 <dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
         </dependency>
  </dependencies>

創(chuàng)建個(gè)啟動(dòng)類及定時(shí)任務(wù)

 @SpringBootApplication
 public class ApplicationScheduling {
     public static void main(String[] args) {
         SpringApplication.run(ApplicationScheduling.class, args);
     }
 }
 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年09月06日 0:02
  */
 @Slf4j
 @Component
 @EnableScheduling
 public class ScheduleService {
     // 每五秒執(zhí)行一次,cron的表達(dá)式就不再多說(shuō)明了
     @Scheduled(cron = "0/5 * * * * ? ")
     public void testSchedule() {
             log.info("當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>{}", Thread.currentThread().getId());
     }
 }

二、問(wèn)題::執(zhí)行時(shí)間延遲和單線程執(zhí)行

按照上面代碼中給定的cron表達(dá)式@Scheduled(cron = "0/5 * * * * ? ")每五秒執(zhí)行一次,那么最近五次的執(zhí)行結(jié)果應(yīng)當(dāng)為:

 2022-09-06 00:21:10
 2022-09-06 00:21:15
 2022-09-06 00:21:20
 2022-09-06 00:21:25
 2022-09-06 00:21:30

如果定時(shí)任務(wù)中是執(zhí)行非??斓娜蝿?wù)的,時(shí)間非常非常短,確實(shí)不會(huì)有什么的延遲性。

上面代碼執(zhí)行結(jié)果:

 2022-09-06 19:42:10.018  INFO 24496 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:42:15.015  INFO 24496 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:42:20.001  INFO 24496 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:42:25.005  INFO 24496 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:42:30.007  INFO 24496 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64

如果說(shuō)從時(shí)間上來(lái)看,說(shuō)不上什么延遲性,但真實(shí)的業(yè)務(wù)場(chǎng)景中,業(yè)務(wù)的執(zhí)行時(shí)間可能遠(yuǎn)比這里時(shí)間長(zhǎng)。

我主動(dòng)讓線程睡上10秒,讓我們?cè)賮?lái)看看輸出結(jié)果是如何的吧

     @Scheduled(cron = "0/5 * * * * ? ")
     public void testSchedule() {
         try {
             Thread.sleep(10000);
             log.info("當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>{}", Thread.currentThread().getId());
         } catch (Exception e) {
             e.printStackTrace();
         } 
     }

輸出結(jié)果

 2022-09-06 19:46:50.019  INFO 27236 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:47:05.024  INFO 27236 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:47:20.016  INFO 27236 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:47:35.005  INFO 27236 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 19:47:50.006  INFO 27236 --- [   scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64

請(qǐng)注意兩個(gè)問(wèn)題:

  • 執(zhí)行時(shí)間延遲:從時(shí)間上可以明顯看出,不再是每五秒執(zhí)行一次,執(zhí)行時(shí)間延遲很多,造成任務(wù)的
  • 單線程執(zhí)行:從始至終都只有一個(gè)線程在執(zhí)行任務(wù),造成任務(wù)的堵塞.

三、為什么會(huì)出現(xiàn)上述問(wèn)題?

問(wèn)題的根本:線程阻塞式執(zhí)行,執(zhí)行任務(wù)線程數(shù)量過(guò)少。

那到底是為什么呢?

回到啟動(dòng)類上,我們?cè)趩?dòng)上標(biāo)明了一個(gè)@EnableScheduling注解。

大家在看到諸如@Enablexxxx這樣的注解的時(shí)候,就要知道它一定有一個(gè)xxxxxAutoConfiguration的自動(dòng)裝配的類。

@EnableScheduling也不例外,它的自動(dòng)裝配的類是TaskSchedulingAutoConfiguration。

我們來(lái)看看它到底做了一些什么設(shè)置?我們?nèi)绾涡薷模?/p>

 @ConditionalOnClass(ThreadPoolTaskScheduler.class)
 @Configuration(proxyBeanMethods = false)
 @EnableConfigurationProperties(TaskSchedulingProperties.class)
 @AutoConfigureAfter(TaskExecutionAutoConfiguration.class)
 public class TaskSchedulingAutoConfiguration {
     @Bean
     @ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
     @ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class })
     public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
         return builder.build();
     }
    // ......
 }

可以看到它也是構(gòu)造了一個(gè) 線程池注入到Spring 中

build()調(diào)用繼續(xù)看下去,

 public ThreadPoolTaskScheduler build() {
     return configure(new ThreadPoolTaskScheduler());
 }

ThreadPoolTaskScheduler中,給定的線程池的核心參數(shù)就為1,這也表明了之前為什么只有一條線程在執(zhí)行任務(wù)。private volatile int poolSize = 1;

這一段是分開的用代碼不好展示,我用圖片標(biāo)明出來(lái)。

主要邏輯在這里,創(chuàng)建線程池的時(shí)候,只使用了三個(gè)參數(shù),剩下的都是使用ScheduledExecutorService的默認(rèn)的參數(shù)

     protected ScheduledExecutorService createExecutor(
             int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) 

而這默認(rèn)參數(shù)是不行的,生產(chǎn)環(huán)境的大坑,阿里的 Java 開發(fā)手冊(cè)中也明確規(guī)定,要手動(dòng)創(chuàng)建線程池,并給定合適的參數(shù)值~是為什么呢?

因?yàn)槟J(rèn)的線程池中, 池中允許的最大線程數(shù)和最大任務(wù)等待隊(duì)列都是Integer.MAX_VALUE.

大家都懂的,如果使用這玩意,只要出了問(wèn)題,必定掛~

configure(new ThreadPoolTaskScheduler())這里就是構(gòu)造,略過(guò)~

如果已經(jīng)較為熟悉SpringBoot的朋友,現(xiàn)在已然明白解決當(dāng)前問(wèn)題的方式~

四、解決方式

1、@EnableConfigurationProperties(TaskSchedulingProperties.class) ,自動(dòng)裝配類通常也都會(huì)對(duì)應(yīng)有個(gè)xxxxProperties文件滴,TaskSchedulingProperties也確實(shí)可以配置核心線程數(shù)等基本參數(shù),但是無(wú)法配置線程池中最大的線程數(shù)量和等待隊(duì)列數(shù)量,這種方式還是不合適的。

2、可以手動(dòng)異步編排,交給某個(gè)線程池來(lái)執(zhí)行。

3、將定時(shí)任務(wù)加上異步注解@Async,將其改為異步的定時(shí)任務(wù),另外自定義一個(gè)系統(tǒng)通用的線程池,讓異步任務(wù)使用該線程執(zhí)行任務(wù)~

我們分別針對(duì)上述三種方式來(lái)實(shí)現(xiàn)一遍

4.1、修改配置文件

可以配置的就下面幾項(xiàng)~

 spring:
   task:
     scheduling:
       thread-name-prefix: nzc-schedule- #線程名前綴
       pool:
         size: 10 #核心線程數(shù)
      # shutdown:
      #  await-termination: true #執(zhí)行程序是否應(yīng)等待計(jì)劃任務(wù)在關(guān)機(jī)時(shí)完成。
      #   await-termination-period:  #執(zhí)行程序應(yīng)等待剩余任務(wù)完成的最長(zhǎng)時(shí)間。

測(cè)試結(jié)果:

 2022-09-06 20:49:15.015  INFO 7852 --- [ nzc-schedule-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 20:49:30.004  INFO 7852 --- [ nzc-schedule-2] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>66
 2022-09-06 20:49:45.024  INFO 7852 --- [ nzc-schedule-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>64
 2022-09-06 20:50:00.025  INFO 7852 --- [ nzc-schedule-3] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>67
 2022-09-06 20:50:15.023  INFO 7852 --- [ nzc-schedule-2] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>66
 2022-09-06 20:50:30.008  INFO 7852 --- [ nzc-schedule-4] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>68

請(qǐng)注意:這里的配置并非是一定生效的,修改后有可能成功,有可能失敗,具體原因未知,但這一點(diǎn)是真實(shí)存在的。

不過(guò)從執(zhí)行結(jié)果中可以看出,這里的執(zhí)行的線程不再是孤單單的一個(gè)。

4.2、執(zhí)行邏輯改為異步執(zhí)行

首先我們先向Spring中注入一個(gè)我們自己編寫的線程池,參數(shù)自己設(shè)置即可,我這里比較隨意。

 @Configuration
 public class MyTheadPoolConfig {
     @Bean
     public TaskExecutor taskExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
         //設(shè)置核心線程數(shù)
         executor.setCorePoolSize(10);
         //設(shè)置最大線程數(shù)
         executor.setMaxPoolSize(20);
         //緩沖隊(duì)列200:用來(lái)緩沖執(zhí)行任務(wù)的隊(duì)列
         executor.setQueueCapacity(200);
         //線程活路時(shí)間 60 秒
         executor.setKeepAliveSeconds(60);
         //線程池名的前綴:設(shè)置好了之后可以方便我們定位處理任務(wù)所在的線程池
         // 這里我繼續(xù)沿用 scheduling 默認(rèn)的線程名前綴
         executor.setThreadNamePrefix("nzc-create-scheduling-");
         //設(shè)置拒絕策略
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
         executor.setWaitForTasksToCompleteOnShutdown(true);
         return executor;
     }
 }

然后在定時(shí)任務(wù)這里注入進(jìn)去:

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年09月06日 0:02
  */
 @Slf4j
 @Component
 @EnableScheduling
 public class ScheduleService {
     @Autowired
     TaskExecutor taskExecutor;
     @Scheduled(cron = "0/5 * * * * ? ")
     public void testSchedule() {
         CompletableFuture.runAsync(()->{
             try {
                 Thread.sleep(10000);
                 log.info("當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>{}", Thread.currentThread().getId());
             } catch (Exception e) {
                 e.printStackTrace();
             } 
         },taskExecutor);
     }
 }

測(cè)試結(jié)果:

 2022-09-06 21:00:00.019  INFO 18356 --- [te-scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>66
 2022-09-06 21:00:05.022  INFO 18356 --- [te-scheduling-2] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>67
 2022-09-06 21:00:10.013  INFO 18356 --- [te-scheduling-3] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>68
 2022-09-06 21:00:15.020  INFO 18356 --- [te-scheduling-4] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>69
 2022-09-06 21:00:20.026  INFO 18356 --- [te-scheduling-5] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>70

可以看到雖然業(yè)務(wù)執(zhí)行時(shí)間比較長(zhǎng),但是木有再出現(xiàn),延遲執(zhí)行定時(shí)任務(wù)的情況。

4.3、異步定時(shí)任務(wù)

異步定時(shí)任務(wù)其實(shí)和上面的方式原理是一樣的,不過(guò)實(shí)現(xiàn)稍稍不同罷了。

在定時(shí)任務(wù)的類上再加一個(gè)@EnableAsync注解,給方法添加一個(gè)@Async即可。

不過(guò)一般@Async都會(huì)指定線程池,比如寫成這樣@Async(value = "taskExecutor"),

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年09月06日 0:02
  */
 @Slf4j
 @Component
 @EnableAsync
 @EnableScheduling
 public class ScheduleService {
     @Autowired
     TaskExecutor taskExecutor;
     @Async(value = "taskExecutor")
     @Scheduled(cron = "0/5 * * * * ? ")
     public void testSchedule() {
             try {
                 Thread.sleep(10000);
                 log.info("當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>{}", Thread.currentThread().getId());
             } catch (Exception e) {
                 e.printStackTrace();
             } 
     }
 }

執(zhí)行結(jié)果:

 2022-09-06 21:10:15.022  INFO 22760 --- [zc-scheduling-1] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>66
 2022-09-06 21:10:20.021  INFO 22760 --- [zc-scheduling-2] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>67
 2022-09-06 21:10:25.007  INFO 22760 --- [zc-scheduling-3] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>68
 2022-09-06 21:10:30.020  INFO 22760 --- [zc-scheduling-4] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>69
 2022-09-06 21:10:35.007  INFO 22760 --- [zc-scheduling-5] com.nzc.service.ScheduleService          : 當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>70

結(jié)果顯而易見(jiàn)是可行的啦~

分析

@EnableAsync注解相應(yīng)的也有一個(gè)自動(dòng)裝配類為TaskExecutionAutoConfiguration

也有一個(gè)TaskExecutionProperties配置類,可以在yml文件中對(duì)參數(shù)進(jìn)行設(shè)置,這里的話是可以配置線程池最大存活數(shù)量的。

它的默認(rèn)核心線程數(shù)為8,這里我不再進(jìn)行演示了,同時(shí)它的線程池中最大存活數(shù)量以及任務(wù)等待數(shù)量也都為Integer.MAX_VALUE,這也是不建議大家使用默認(rèn)線程池的原因。

4.4、小結(jié)

 /**
  * 定時(shí)任務(wù)
  *      1、@EnableScheduling 開啟定時(shí)任務(wù)
  *      2、@Scheduled開啟一個(gè)定時(shí)任務(wù)
  *      3、自動(dòng)裝配類 TaskSchedulingAutoConfiguration
  *
  * 異步任務(wù)
  *      1、@EnableAsync:開啟異步任務(wù)
  *      2、@Async:給希望異步執(zhí)行的方法標(biāo)注
  *      3、自動(dòng)裝配類 TaskExecutionAutoConfiguration
  */

實(shí)現(xiàn)方式雖不同,但從效率而言,并無(wú)太大區(qū)別,覺(jué)得那種合適使用那種便可。

不過(guò)總結(jié)起來(lái),考查的都是對(duì)線程池的理解,對(duì)于線程池的了解是真的非常重要的,也很有用處

五、分布式下的思考

針對(duì)上述情況而言,這些解決方法在不引入第三包的情況下是足以應(yīng)付大部分情況了。

定時(shí)框架的實(shí)現(xiàn)有許多方式,在此并非打算討論這個(gè)。

在單體項(xiàng)目中,也許上面的問(wèn)題是解決了,但是站在分布式的情況下考慮,就并非是安全的了。

當(dāng)多個(gè)項(xiàng)目在同時(shí)運(yùn)行,那么必然會(huì)有多個(gè)項(xiàng)目同時(shí)這段代碼。

思考:并發(fā)執(zhí)行

如果一個(gè)定時(shí)任務(wù)同時(shí)在多個(gè)機(jī)器中運(yùn)行,會(huì)產(chǎn)生怎么樣的問(wèn)題?

假如這個(gè)定時(shí)任務(wù)是收集某個(gè)信息,發(fā)送給消息隊(duì)列,如果多臺(tái)機(jī)器同時(shí)執(zhí)行,同時(shí)給消息隊(duì)列發(fā)送信息,那么必然導(dǎo)致之后產(chǎn)生一系列的臟數(shù)據(jù)。這是非常不可靠的

解決方式:分布式鎖

很簡(jiǎn)單也不簡(jiǎn)單,加分布式鎖~ 或者是用一些分布式調(diào)度的框架

如使用XXL-JOB實(shí)現(xiàn),或者是其他的定時(shí)任務(wù)框架。

大家在執(zhí)行這個(gè)定時(shí)任務(wù)之前,先去獲取一把分布式鎖,獲取到了就執(zhí)行,獲取不到就直接結(jié)束。

我這里使用的是 redission,因?yàn)榉奖?,打算寫分布式鎖的文章,還在準(zhǔn)備當(dāng)中。

redission官方文檔,我覺(jué)得應(yīng)當(dāng)算是比較友好的文檔了哈哈

加入依賴:

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson-spring-boot-starter</artifactId>
     <version>3.17.6</version>
 </dependency>

按照文檔說(shuō)的,編寫配置類,注入 RedissonClient,redisson的全部操作都是基于此。

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年09月06日 9:31
  */
 @Configuration
 public class MyRedissonConfig {
     /**
      * 所有對(duì)Redisson的使用都是通過(guò)RedissonClient
      * @return
      * @throws IOException
      */
     @Bean(destroyMethod="shutdown")
     public RedissonClient redissonClient() throws IOException {
         //1、創(chuàng)建配置
         Config config = new Config();
        // 這里規(guī)定要用 redis://+IP地址
           config.useSingleServer().setAddress("redis://xxxxx:6379").setPassword("000415");   // 有密碼就寫密碼~ 木有不用寫~
         //2、根據(jù)Config創(chuàng)建出RedissonClient實(shí)例
         //Redis url should start with redis:// or rediss://
         RedissonClient redissonClient = Redisson.create(config);
         return redissonClient;
     }
 }

修改定時(shí)任務(wù):

 
 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年09月06日 0:02
  */
 @Slf4j
 @Component
 @EnableAsync
 @EnableScheduling
 public class ScheduleService {
     @Autowired
     TaskExecutor taskExecutor;
     @Autowired
     RedissonClient redissonClient;
     private final String SCHEDULE_LOCK = "schedule:lock";
     @Async(value = "taskExecutor")
     @Scheduled(cron = "0/5 * * * * ? ")
     public void testSchedule() {
         //分布式鎖
         RLock lock = redissonClient.getLock(SCHEDULE_LOCK);
         try {
             //加鎖 10 為時(shí)間,加上時(shí)間 默認(rèn)會(huì)去掉 redisson 的看門狗機(jī)制(即自動(dòng)續(xù)鎖機(jī)制)
             lock.lock(10, TimeUnit.SECONDS);
             Thread.sleep(10000);
             log.info("當(dāng)前執(zhí)行任務(wù)的線程號(hào)ID===>{}", Thread.currentThread().getId());
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             // 一定要記得解鎖~
             lock.unlock();
         }
     }
 }

這里只是給出個(gè)大概的實(shí)現(xiàn),實(shí)際上還是可以優(yōu)化的,比如在給定一個(gè)flag,在獲取鎖之前判斷。如果有人搶到鎖,就修改這個(gè)值,之后的請(qǐng)求,判斷這個(gè)flag,如果不是默認(rèn)的值,則直接結(jié)束任務(wù)等等。

思考:繼續(xù)往深處思考,在分布式情況下如果一個(gè)定時(shí)任務(wù)搶到鎖,但是它在執(zhí)行業(yè)務(wù)過(guò)程中失敗或者是宕機(jī)了,這又該如何處理呢?如何補(bǔ)償呢?

個(gè)人思考:

失敗還比較好說(shuō),我們可以直接try{}catch(){}中進(jìn)行通知告警,及時(shí)檢查出問(wèn)題。

如果是掛了,我還沒(méi)想好怎么做。

后記

但實(shí)際上,我所闡述的這種方式,只能說(shuō)適用于簡(jiǎn)單的單體項(xiàng)目,一旦牽扯到動(dòng)態(tài)定時(shí)任務(wù),使用這種方式就不再那么方便了。

大部分都是使用定時(shí)任務(wù)框架集成了,尤其是分布式調(diào)度遠(yuǎn)比單體項(xiàng)目需要考慮多的多。

以上就是Schedule定時(shí)任務(wù)在分布式產(chǎn)生的問(wèn)題詳解的詳細(xì)內(nèi)容,更多關(guān)于Schedule定時(shí)任務(wù)分布式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JAVACORE與HEAPDUMP生成方法

    JAVACORE與HEAPDUMP生成方法

    JavaCore文件主要保存的是Java應(yīng)用各線程在某一時(shí)刻的運(yùn)行的位置,即JVM執(zhí)行到哪一個(gè)類、哪一個(gè)方法、哪一個(gè)行上,它是一個(gè)文本文件,打開后可以看到每一個(gè)線程的執(zhí)行棧,以stack?trace的顯示,本文介紹JAVACORE與HEAPDUMP生成大法,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • Mybatis動(dòng)態(tài)SQL?foreach批量操作方法

    Mybatis動(dòng)態(tài)SQL?foreach批量操作方法

    這篇文章主要介紹了Mybatis動(dòng)態(tài)SQL?foreach批量操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Rabbitmq延遲隊(duì)列實(shí)現(xiàn)定時(shí)任務(wù)的方法

    Rabbitmq延遲隊(duì)列實(shí)現(xiàn)定時(shí)任務(wù)的方法

    這篇文章主要介紹了Rabbitmq延遲隊(duì)列實(shí)現(xiàn)定時(shí)任務(wù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Java實(shí)現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn)

    Java實(shí)現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn)

    本文主要介紹了Java實(shí)現(xiàn)bmp和jpeg圖片格式互轉(zhuǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 圖解Java經(jīng)典算法快速排序的原理與實(shí)現(xiàn)

    圖解Java經(jīng)典算法快速排序的原理與實(shí)現(xiàn)

    快速排序是基于二分的思想,對(duì)冒泡排序的一種改進(jìn)。主要思想是確立一個(gè)基數(shù),將小于基數(shù)的數(shù)放到基數(shù)左邊,大于基數(shù)的數(shù)字放到基數(shù)的右邊,然后在對(duì)這兩部分進(jìn)一步排序,從而實(shí)現(xiàn)對(duì)數(shù)組的排序
    2022-09-09
  • Java的內(nèi)存區(qū)域與內(nèi)存溢出異常你了解嗎

    Java的內(nèi)存區(qū)域與內(nèi)存溢出異常你了解嗎

    這篇文章主要為大家詳細(xì)介紹了Java的內(nèi)存區(qū)域與內(nèi)存溢出異常,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • 簡(jiǎn)單了解JAVA SimpleDateFormat yyyy和YYYY的區(qū)別

    簡(jiǎn)單了解JAVA SimpleDateFormat yyyy和YYYY的區(qū)別

    這篇文章主要介紹了簡(jiǎn)單了解JAVA SimpleDateFormat yyyy和YYYY的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java中遍歷集合的并發(fā)修改異常解決方案實(shí)例代碼

    Java中遍歷集合的并發(fā)修改異常解決方案實(shí)例代碼

    當(dāng)你遍歷集合的同時(shí),又往集合中添加或者刪除元素,就可能報(bào)并發(fā)修改異常,下面這篇文章主要給大家介紹了關(guān)于Java中遍歷集合的并發(fā)修改異常解決方案的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • Java泛型與數(shù)據(jù)庫(kù)應(yīng)用實(shí)例詳解

    Java泛型與數(shù)據(jù)庫(kù)應(yīng)用實(shí)例詳解

    這篇文章主要介紹了Java泛型與數(shù)據(jù)庫(kù)應(yīng)用,結(jié)合實(shí)例形式詳細(xì)分析了java繼承泛型類實(shí)現(xiàn)增刪改查操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-08-08
  • 使用Java將字符串在ISO-8859-1和UTF-8之間相互轉(zhuǎn)換

    使用Java將字符串在ISO-8859-1和UTF-8之間相互轉(zhuǎn)換

    大家都知道在一些情況下,我們需要特殊的編碼格式,如:UTF-8,但是系統(tǒng)默認(rèn)的編碼為ISO-8859-1,遇到這個(gè)問(wèn)題,該如何對(duì)字符串進(jìn)行兩個(gè)編碼的轉(zhuǎn)換呢,下面小編給大家分享下java中如何在ISO-8859-1和UTF-8之間相互轉(zhuǎn)換,感興趣的朋友一起看看吧
    2021-12-12

最新評(píng)論