<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>

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

          共 5149字,需瀏覽 11分鐘

           ·

          2022-05-19 17:45

          Hollis的新書(shū)限時(shí)折扣中,一本深入講解Java基礎(chǔ)的干貨筆記!

          來(lái)源:juejin.cn/post/

          6930912870058328071


          單點(diǎn)定時(shí)任務(wù)

          JDK原生

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

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

          Spring Task

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

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

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

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

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

          基于 Redis 實(shí)現(xiàn)

          相較于之前兩種方式,這種基于Redis的實(shí)現(xiàn)可以通過(guò)多點(diǎn)來(lái)增加定時(shí)任務(wù),多點(diǎn)消費(fèi)。但是要做好防范重復(fù)消費(fèi)的準(zhǔn)備。

          通過(guò)ZSet的方式

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

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

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

          ?*
          ?*?@author?mxy
          ?*/

          @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());
          ????}

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

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

          適用場(chǎng)景如下:

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

          優(yōu)勢(shì)是:

          • 省去了MySQL的查詢操作,而使用性能更高的Redis做為代替;
          • 不會(huì)因?yàn)橥C(jī)等原因,遺漏要執(zhí)行的任務(wù);

          鍵空間通知的方式

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

          默認(rèn)情況下Redis是不開(kāi)啟鍵空間通知的,需要我們通過(guò)config set notify-keyspace-events Ex的命令手動(dòng)開(kāi)啟。開(kāi)啟之后定時(shí)任務(wù)的代碼如下:

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

          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);
          ????????//?過(guò)期的key
          ????????String?key?=?new?String(message.getBody(),?StandardCharsets.UTF_8);
          ????????//?todo?你的處理
          ????}
          }
          設(shè)置該監(jiān)聽(tīng)器
          /**
          ?*?Description:?通過(guò)訂閱Redis的過(guò)期通知來(lái)實(shí)現(xiàn)定時(shí)任務(wù)?.

          ?*
          ?*?@author?mxy
          ?*/

          @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會(huì)監(jiān)聽(tīng)符合以下格式的Redis消息

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

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

          場(chǎng)景舉例:

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

          優(yōu)劣勢(shì)是:

          • 被動(dòng)觸發(fā),對(duì)于服務(wù)的資源消耗更小;
          • Redis的Pub/Sub不可靠,沒(méi)有ACK機(jī)制等,但是一般情況可以容忍;
          • 鍵空間通知功能會(huì)耗費(fèi)一些CPU

          基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能。

          項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro

          分布式定時(shí)任務(wù)

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

          將定時(shí)任務(wù)作為單獨(dú)的服務(wù),遏制了重復(fù)消費(fèi),獨(dú)立的服務(wù)也有利于擴(kuò)展和維護(hù)。

          quartz

          依賴于MySQL,使用相對(duì)簡(jiǎn)單,可多節(jié)點(diǎn)部署,通過(guò)競(jìng)爭(zhēng)數(shù)據(jù)庫(kù)鎖來(lái)保證只有一個(gè)節(jié)點(diǎn)執(zhí)行任務(wù)。沒(méi)有圖形化管理頁(yè)面,使用相對(duì)麻煩。

          elastic-job-lite

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

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

          LTS

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

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

          xxl-job

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

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

          基于微服務(wù)的思想,構(gòu)建在 B2C 電商場(chǎng)景下的項(xiàng)目實(shí)戰(zhàn)。核心技術(shù)棧,是 Spring Boot + Dubbo 。未來(lái),會(huì)重構(gòu)成 Spring Cloud Alibaba 。

          項(xiàng)目地址:https://github.com/YunaiV/onemall

          總結(jié)

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

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

          • 任務(wù)不會(huì)因?yàn)榧翰渴鸲欢啻螆?zhí)行。
          • 任務(wù)發(fā)生異常得到有效的處理
          • 任務(wù)的處理過(guò)慢導(dǎo)致大量積壓
          • 任務(wù)應(yīng)該在預(yù)期的時(shí)間點(diǎn)執(zhí)行

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


          往期推薦

          再有人問(wèn)你數(shù)據(jù)庫(kù)緩存一致性的問(wèn)題,直接把這篇文章發(fā)給他!


          給你 2 萬(wàn)條數(shù)據(jù),怎么快速導(dǎo)入到 MySQL?寫(xiě)得太好了!


          下載 145 部盜版視頻,被法院判賠償 70 萬(wàn)元!




          有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號(hào)


          好文章,我在看??

          瀏覽 42
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  十八岁禁黄网站 | 欧美日韩国产免费电影 | 天天弄| 免费三级怡红院 | 人人操人人人人操人人爱 |