<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          幾種主流的分布式定時任務(wù),你知道哪些?

          共 4986字,需瀏覽 10分鐘

           ·

          2022-04-19 08:54

          單點定時任務(wù)

          JDK原生

          自從JDK1.5之后,提供了ScheduledExecutorService代替TimerTask來執(zhí)行定時任務(wù),提供了不錯的可靠性。

          public?class?SomeScheduledExecutorService?{
          ????public?static?void?main(String[]?args)?{
          ????????//?創(chuàng)建任務(wù)隊列,共?10?個線程
          ????????ScheduledExecutorService?scheduledExecutorService?=
          ????????????????Executors.newScheduledThreadPool(10);
          ????????//?執(zhí)行任務(wù):?1秒?后開始執(zhí)行,每?30秒?執(zhí)行一次
          ????????scheduledExecutorService.scheduleAtFixedRate(()?->?{
          ????????????System.out.println("執(zhí)行任務(wù):"?+?new?Date());
          ????????},?10,?30,?TimeUnit.SECONDS);
          ????}
          }
          復制代碼

          Spring Task

          Spring Framework自帶定時任務(wù),提供了cron表達式來實現(xiàn)豐富定時任務(wù)配置。新手推薦使用https://cron.qqe2.com/這個網(wǎng)站來匹配你的cron表達式

          @Configuration
          @EnableScheduling
          public?class?SomeJob?{
          ????private?static?final?Logger?LOGGER?=?LoggerFactory.getLogger(SomeJob.class);

          ????/**
          ?????*?每分鐘執(zhí)行一次(例:18:01:00,18:02:00)
          ?????*?秒?分鐘?小時?日?月?星期?年
          ?????*/

          ????@Scheduled(cron?=?"0?0/1?*?*?*???*")
          ????public?void?someTask()?{
          ???????//...
          ????}
          }
          復制代碼

          單點的定時服務(wù)在目前微服務(wù)的大環(huán)境下,應(yīng)用場景越來越局限,所以嘗鮮一下分布式定時任務(wù)吧。

          關(guān)于該功能的使用,DD的Spring Boot教程中也有介紹,感興趣的小伙伴可以通過這個鏈接查看:https://blog.didispace.com/spring-boot-learning-2-7-1/

          基于 Redis 實現(xiàn)

          相較于之前兩種方式,這種基于Redis的實現(xiàn)可以通過多點來增加定時任務(wù),多點消費。但是要做好防范重復消費的準備。

          通過ZSet的方式

          將定時任務(wù)存放到ZSet集合中,并且將過期時間存儲到ZSetScore字段中,然后通過一個循環(huán)來判斷當前時間內(nèi)是否有需要執(zhí)行的定時任務(wù),如果有則進行執(zhí)行。

          具體實現(xiàn)代碼如下:

          /**
          ?*?Description:?基于Redis的ZSet的定時任務(wù)?.

          ?*
          ?*?@author?mxy
          ?*?@Date?2020/8/25?11:54
          ?*/

          @Configuration
          @EnableScheduling
          public?class?RedisJob?{
          ????public?static?final?String?JOB_KEY?=?"redis.job.task";
          ????private?static?final?Logger?LOGGER?=?LoggerFactory.getLogger(RedisJob.class);
          ????@Autowired?private?StringRedisTemplate?stringRedisTemplate;

          ????/**
          ?????*?添加任務(wù).
          ?????*
          ?????*?@param?task
          ?????*/

          ????public?void?addTask(String?task,?Instant?instant)?{
          ????????stringRedisTemplate.opsForZSet().add(JOB_KEY,?task,?instant.getEpochSecond());
          ????}

          ????/**
          ?????*?定時任務(wù)隊列消費
          ?????*?每分鐘消費一次(可以縮短間隔到1s)
          ?????*/

          ????@Scheduled(cron?=?"0?0/1?*?*?*???*")
          ????public?void?doDelayQueue()?{
          ????????long?nowSecond?=?Instant.now().getEpochSecond();
          ????????//?查詢當前時間的所有任務(wù)
          ????????Set?strings?=?stringRedisTemplate.opsForZSet().range(JOB_KEY,?0,?nowSecond);
          ????????for?(String?task?:?strings)?{
          ????????????//?開始消費?task
          ????????????LOGGER.info("執(zhí)行任務(wù):{}",?task);
          ????????}
          ????????//?刪除已經(jīng)執(zhí)行的任務(wù)
          ????????stringRedisTemplate.opsForZSet().remove(JOB_KEY,?0,?nowSecond);
          ????}
          }
          復制代碼

          適用場景如下:

          • 訂單下單之后15分鐘后,用戶如果沒有付錢,系統(tǒng)需要自動取消訂單
          • 紅包24小時未被查收,需要延遲執(zhí)退還業(yè)務(wù);
          • 某個活動指定在某個時間內(nèi)生效&失效

          優(yōu)勢是:

          1. 省去了MySQL的查詢操作,而使用性能更高的Redis做為代替;
          2. 不會因為停機等原因,遺漏要執(zhí)行的任務(wù);

          鍵空間通知的方式

          我們可以通過Redis的鍵空間通知來實現(xiàn)定時任務(wù),它的實現(xiàn)思路是給所有的定時任務(wù)設(shè)置一個過期時間,等到了過期之后,我們通過訂閱過期消息就能感知到定時任務(wù)需要被執(zhí)行了,此時我們執(zhí)行定時任務(wù)即可。

          默認情況下Redis是不開啟鍵空間通知的,需要我們通過config set notify-keyspace-events Ex的命令手動開啟。

          開啟之后定時任務(wù)的代碼如下:

          自定義監(jiān)聽器
          ?/**
          ??*?自定義監(jiān)聽器.
          ??*/

          public?class?KeyExpiredListener?extends?KeyExpirationEventMessageListener?{
          ????public?KeyExpiredListener(RedisMessageListenerContainer?listenerContainer)?{
          ????????super(listenerContainer);
          ????}

          ????@Override
          ????public?void?onMessage(Message?message,?byte[]?pattern)?{
          ????????//?channel
          ????????String?channel?=?new?String(message.getChannel(),?StandardCharsets.UTF_8);
          ????????//?過期的key
          ????????String?key?=?new?String(message.getBody(),?StandardCharsets.UTF_8);
          ????????//?todo?你的處理
          ????}
          }
          復制代碼
          設(shè)置該監(jiān)聽器
          /**
          ?*?Description:?通過訂閱Redis的過期通知來實現(xiàn)定時任務(wù)?.

          ?*
          ?*?@author?mxy
          ?*?@Date?2020/8/25?12:07
          ?*/

          @Configuration
          public?class?RedisExJob?{
          ????@Autowired?private?RedisConnectionFactory?redisConnectionFactory;
          ????@Bean
          ????public?RedisMessageListenerContainer?redisMessageListenerContainer()?{
          ????????RedisMessageListenerContainer?redisMessageListenerContainer?=?new?RedisMessageListenerContainer();
          ????????redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
          ????????return?redisMessageListenerContainer;
          ????}

          ????@Bean
          ????public?KeyExpiredListener?keyExpiredListener()?{
          ????????return?new?KeyExpiredListener(this.redisMessageListenerContainer());
          ????}
          }
          復制代碼

          Spring會監(jiān)聽符合以下格式的Redis消息

          private?static?final?Topic?TOPIC_ALL_KEYEVENTS?=?new?PatternTopic("__keyevent@*");
          復制代碼

          基于Redis的定時任務(wù)能夠適用的場景也比較有限,但實現(xiàn)上相對簡單,但對于功能冪等有很大要求。從使用場景上來說,更應(yīng)該叫做延時任務(wù)

          場景舉例:

          • 訂單下單之后15分鐘后,用戶如果沒有付錢,系統(tǒng)需要自動取消訂單
          • 紅包24小時未被查收,需要延遲執(zhí)退還業(yè)務(wù);

          優(yōu)劣勢是:

          1. 被動觸發(fā),對于服務(wù)的資源消耗更小;
          2. Redis的Pub/Sub不可靠,沒有ACK機制等,但是一般情況可以容忍;
          3. 鍵空間通知功能會耗費一些CPU

          分布式定時任務(wù)

          引入分布式定時任務(wù)組件or中間件

          將定時任務(wù)作為單獨的服務(wù),遏制了重復消費,獨立的服務(wù)也有利于擴展和維護。

          quartz

          依賴于MySQL,使用相對簡單,可多節(jié)點部署,通過競爭數(shù)據(jù)庫鎖來保證只有一個節(jié)點執(zhí)行任務(wù)。沒有圖形化管理頁面,使用相對麻煩。

          elastic-job-lite

          依賴于Zookeeper,通過zookeeper的注冊與發(fā)現(xiàn),可以動態(tài)的添加服務(wù)器。

          • 多種作業(yè)模式
          • 失效轉(zhuǎn)移
          • 運行狀態(tài)收集
          • 多線程處理數(shù)據(jù)
          • 冪等性
          • 容錯處理
          • 支持spring命名空間
          • 有圖形化管理頁面

          關(guān)于該框架的實用,DD在博客也連載過,通過這個鏈接可以直接看詳細教程:https://blog.didispace.com/tags/Elastic-Job/


          LTS

          依賴于Zookeeper,集群部署,可以動態(tài)的添加服務(wù)器。可以手動增加定時任務(wù),啟動和暫停任務(wù)。

          • 業(yè)務(wù)日志記錄器
          • SPI擴展支持
          • 故障轉(zhuǎn)移
          • 節(jié)點監(jiān)控
          • 多樣化任務(wù)執(zhí)行結(jié)果支持
          • FailStore容錯
          • 動態(tài)擴容
          • 對spring相對友好
          • 有監(jiān)控和管理圖形化界面

          xxl-job

          國產(chǎn),依賴于MySQL,基于競爭數(shù)據(jù)庫鎖保證只有一個節(jié)點執(zhí)行任務(wù),支持水平擴容。可以手動增加定時任務(wù),啟動和暫停任務(wù)。

          • 彈性擴容
          • 分片廣播
          • 故障轉(zhuǎn)移
          • Rolling實時日志
          • GLUE(支持在線編輯代碼,免發(fā)布)
          • 任務(wù)進度監(jiān)控
          • 任務(wù)依賴
          • 數(shù)據(jù)加密
          • 郵件報警
          • 運行報表
          • 優(yōu)雅停機
          • 國際化(中文友好)

          總結(jié)

          微服務(wù)下,推薦使用xxl-job這一類組件服務(wù)將定時任務(wù)合理有效的管理起來。而單點的定時任務(wù)有其局限性,適用于規(guī)模較小、對未來擴展要求不高的服務(wù)。

          相對而言,基于spring task的定時任務(wù)最簡單快捷,而xxl-job的難度主要體現(xiàn)在集成和調(diào)試上。無論是什么樣的定時任務(wù),你都需要確保:

          • 任務(wù)不會因為集群部署而被多次執(zhí)行。
          • 任務(wù)發(fā)生異常得到有效的處理
          • 任務(wù)的處理過慢導致大量積壓
          • 任務(wù)應(yīng)該在預期的時間點執(zhí)行

          中間件可以將服務(wù)解耦,但增加了復雜度

          作者:襄垣

          來源:https://juejin.cn/post/6930912870058328071


          ——————END——————

          歡迎關(guān)注“Java引導者”,我們分享最有價值的Java的干貨文章,助力您成為有思想的Java開發(fā)工程師!

          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青青大香蕉 | 大鸡巴精品视频在线看 | 操逼视频网站网址 | 日韩三级影音先锋 | 亚洲欧美视频一区 |