最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊

Java如何實(shí)現(xiàn)定時(shí)任務(wù)?

2023-03-16 16:07 作者:吳小敏63  | 我要投稿

我是3y,一年CRUD經(jīng)驗(yàn)用十年的markdown程序員???????常年被譽(yù)為優(yōu)質(zhì)八股文選手

挺早就規(guī)劃了要引入分布式定時(shí)任務(wù)框架了,在年前austin就已經(jīng)接入了,但代碼過年一直都沒寫,文章也就一直拖到今天了。今天主要就跟大家在聊聊定時(shí)任務(wù)這個(gè)話題。

看完這篇文章你會(huì)了解到什么是定時(shí)任務(wù),以及為什么austin項(xiàng)目要引入分布式定時(shí)任務(wù)框架,可以把代碼下載下來看到我是怎么使用xxl-job的。

01、如何簡單實(shí)現(xiàn)定時(shí)功能?

我是看視頻入門Java的,那時(shí)候?qū)WJava基礎(chǔ)API的時(shí)候,看的視頻也帶有講定時(shí)功能(JDK原生就支持),我記得視頻講師寫了Timer來講解定時(shí)任務(wù)。

當(dāng)時(shí)并不知道定時(shí)任務(wù)有什么實(shí)際作用,所以在初學(xué)階段的我,從來沒使用過Timer來實(shí)現(xiàn)定時(shí)的功能。

再后來,我學(xué)到并發(fā)了。那時(shí)候的講師提到了ScheduledExecutorService這個(gè)接口,它比Timer更加強(qiáng)大,一般我們在JDK里可以用它來實(shí)現(xiàn)定時(shí)的功能

強(qiáng)就強(qiáng)在于ScheduledExecutorService內(nèi)部是線程池,Timer是單線程,它能更合理的利用資源。

我學(xué)并發(fā)的時(shí)候,我也并不太關(guān)注它(它并不是并發(fā)的重點(diǎn)),所以我也沒用過ScheduledExecutorService來實(shí)現(xiàn)定時(shí)的功能。

后來吧,要到學(xué)習(xí)做項(xiàng)目了,那時(shí)候視頻有個(gè)Quartz課程。我記得理解了很久,最后我才反應(yīng)過來了,原來寫了這么多的代碼就是用它來實(shí)現(xiàn)定時(shí)的功能。

至于比ScheduledExecutorServiceTimer好在哪里呢,最直觀的是:它支持cron表達(dá)式。

為啥我會(huì)理解很久呢,因?yàn)?code>Quartz的api太復(fù)雜了(它也有著自己的專業(yè)術(shù)語和概念性的東西)。不過這種跟著做項(xiàng)目的,我是一步一步跟著敲代碼的。

Quartz相關(guān)的API我是記不住了,但那時(shí)候我理解了:原來我們寫代碼可以靠「組件包」來完成想要的功能,原來這就是cron表達(dá)式。

等到我大三的時(shí)候,我想用自己學(xué)過的知識點(diǎn)來寫個(gè)小項(xiàng)目,也算是梳理一遍自己到底學(xué)了什么東西。于是,我想起了Quartz。

那時(shí)候我已經(jīng)學(xué)到了Spring/SpringBoot了。所以當(dāng)我在網(wǎng)上搜SpringQuartz整合的時(shí)候,了解到了SpringTask,再后來發(fā)現(xiàn)了@Schedule注解。

只需要一個(gè)簡單的注解,就能實(shí)現(xiàn)定時(shí)任務(wù)的功能,并且支持cron表達(dá)式。

那那那那,還要個(gè)錘子的Quartz啊!

02、實(shí)習(xí)&&工作 定時(shí)任務(wù)

等我工作了之后,我學(xué)到了一個(gè)新的名詞「分布式定時(shí)任務(wù)框架」。等我踏入職場了以后,我才發(fā)現(xiàn)原來定時(shí)任務(wù)這么好使!

列舉下我真實(shí)工作時(shí)使用定時(shí)任務(wù)的常見姿勢:

1、動(dòng)態(tài)創(chuàng)建定時(shí)任務(wù)推送運(yùn)營類的消息(定時(shí)推送消息)

2、廣告結(jié)算定時(shí)任務(wù)掃表找到對應(yīng)的可結(jié)算記錄(定時(shí)掃表更新狀態(tài))

3、每天定時(shí)更新數(shù)據(jù)記錄(定時(shí)更新數(shù)據(jù))

還很多人問我有沒有用過分布式事務(wù),我往往會(huì)回答:沒有啊,我們都是掃表一把梭保證數(shù)據(jù)最終一致性的當(dāng)然了,如果是面試的時(shí)候被問到,可以吹吹分布式事務(wù)。實(shí)際上是怎么掃表的呢?就是定時(shí)掃的咯。

另外,我當(dāng)時(shí)簡單看了下公司自研的分布式定時(shí)任務(wù)框架是怎么做的,我記得是基于Quartz進(jìn)行擴(kuò)展的,擴(kuò)展有failover、分片等等機(jī)制。

一般來說,使用定時(shí)任務(wù)就是在應(yīng)用啟動(dòng)或者提前在Web頁面配置好定時(shí)任務(wù)(定時(shí)任務(wù)框架都是支持cron表達(dá)式的,所以是周期或者定時(shí)的任務(wù)),這種場景是最最最多的。

03、為什么分布式定時(shí)任務(wù)

在前面提到Timer/ScheduledExecutorService/SpringTask(@Schedule)都是單機(jī)的,但我們一旦上了生產(chǎn)環(huán)境,應(yīng)用部署往往都是集群模式的。

在集群下,我們一般是希望某個(gè)定時(shí)任務(wù)只在某臺(tái)機(jī)器上執(zhí)行,那這時(shí)候,單機(jī)實(shí)現(xiàn)的定時(shí)任務(wù)就不太好處理了。

Quartz是有集群部署方案的,所以有的人會(huì)利用數(shù)據(jù)庫行鎖或者使用Redis分布式鎖來自己實(shí)現(xiàn)定時(shí)任務(wù)跑在某一臺(tái)應(yīng)用機(jī)器上;做肯定是能做的,包括有些挺出名的分布式定時(shí)任務(wù)框架也是這樣做的,能解決問題。

但我們遇到的問題不單單只有這些,比如我想要支持容錯(cuò)功能(失敗重試)、分片功能、手動(dòng)觸發(fā)一次任務(wù)、有一個(gè)比較好的管理定時(shí)任務(wù)的后臺(tái)界面路由負(fù)載均衡等等。這些功能,就是作為「分布式定時(shí)任務(wù)框架」所具備的。

既然現(xiàn)在已經(jīng)有這么多的輪子了,那我們作為使用方/需求方就沒必要自己重新實(shí)現(xiàn)一套了,用現(xiàn)有的就好了,我們可以學(xué)習(xí)現(xiàn)有輪子的實(shí)現(xiàn)設(shè)計(jì)思想。

04、分布式定時(shí)任務(wù)基礎(chǔ)

Quartz是優(yōu)秀的開源組件,它將定時(shí)任務(wù)抽象了三個(gè)角色:調(diào)度器、執(zhí)行器任務(wù),以至于市面上的分布式定時(shí)任務(wù)框架都有類似角色劃分。

對于我們使用方而言,一般是引入一個(gè)client包,然后根據(jù)它的規(guī)則(可能是使用注解標(biāo)識,又或是實(shí)現(xiàn)某個(gè)接口),隨后自定義我們自己的定時(shí)任務(wù)邏輯。

看著上面的執(zhí)行圖對應(yīng)的角色抽象以及一般使用姿勢,應(yīng)該還是比較容易理解這個(gè)過程的。我們又可以再稍微思考兩個(gè)問題:

1、 任務(wù)信息以及調(diào)度的信息是需要存儲(chǔ)的,存儲(chǔ)在哪?調(diào)度器是需要「通知」執(zhí)行器去執(zhí)行的,那「通知」是以什么方式去做?

2、調(diào)度器是怎么找到即將需要執(zhí)行的任務(wù)的呢?

針對第一個(gè)問題,分布式定時(shí)任務(wù)框架又可以分成了兩個(gè)流派:中心化和去中心化

  • 所謂的「中心化」指的是:調(diào)度器和執(zhí)行器分離,調(diào)度器統(tǒng)一進(jìn)行調(diào)度,通知執(zhí)行器去執(zhí)行定時(shí)任務(wù)

  • 所謂的「去中心化」指的是:調(diào)度器和執(zhí)行器耦合,自己調(diào)度自己執(zhí)行

對于「中心化」流派來說,存儲(chǔ)相關(guān)的信息很可能是在數(shù)據(jù)庫(DataBase),而我們引入的client包實(shí)際上就是執(zhí)行器相關(guān)的代碼。調(diào)度器實(shí)現(xiàn)了任務(wù)調(diào)度的邏輯,遠(yuǎn)程調(diào)用執(zhí)行器觸發(fā)對應(yīng)的邏輯。

調(diào)度器「通知」執(zhí)行器去執(zhí)行任務(wù)時(shí),可以是通過「RPC」調(diào)用,也可以是把任務(wù)信息寫入消息隊(duì)列給執(zhí)行器消費(fèi)來達(dá)到目的。

對于「去中心化」流派來說存儲(chǔ)相關(guān)的信息很可能是在注冊中心(Zookeeper),而我們引入的client包實(shí)際上就是執(zhí)行器+調(diào)度器相關(guān)的代碼。

依賴注冊中心來完成任務(wù)的分配,「中心化」流派在調(diào)度的時(shí)候是需要保證一個(gè)任務(wù)只被一臺(tái)機(jī)器消費(fèi),這就需要在代碼里寫分布式鎖相關(guān)邏輯進(jìn)行保證,而「去中心化」依賴注冊中心就免去了這個(gè)環(huán)節(jié)。

針對第二個(gè)問題,調(diào)度器是怎么找到即將需要執(zhí)行的任務(wù)的呢?現(xiàn)在一般較新的分布式定時(shí)任務(wù)框架都用了「時(shí)間輪」。

1、如果我們?nèi)粘R业綔?zhǔn)備要執(zhí)行的任務(wù),可能會(huì)把這些任務(wù)放在一個(gè)List里然后進(jìn)行判斷,那此時(shí)查詢的時(shí)間復(fù)雜度為O(n)

2、稍微改進(jìn)下,我們可能把這些任務(wù)放在一個(gè)最小堆里(對時(shí)間進(jìn)行排序),那此時(shí)的增刪改時(shí)間復(fù)雜度為O(logn),而查詢是O(1)

3、再改進(jìn)下,我們把這些任務(wù)放在一個(gè)環(huán)形數(shù)組里,那這時(shí)候的增刪改查時(shí)間復(fù)雜度都是O(1)。但此時(shí)的環(huán)形數(shù)組大小決定著我們能存放任務(wù)的大小,超出環(huán)形數(shù)組的任務(wù)就需要用另外的數(shù)組結(jié)構(gòu)存放。

4、最后再改進(jìn)下,我們可以有多層環(huán)形數(shù)組,不同層次的環(huán)形數(shù)組的精度是不一樣的,使用多層環(huán)形數(shù)組能大大提高我們的精度。

05、分布式定時(shí)任務(wù)框架選型

分布式定時(shí)任務(wù)框架現(xiàn)在可選擇的還是挺多的,比較出名的有:XXL-JOB/Elastic-Job/LTS/SchedulerX/Saturn/PowerJob等等等。有條件的公司可能會(huì)基于Quartz進(jìn)行拓展,自研一套符合自己的公司內(nèi)的分布式定時(shí)任務(wù)框架。

我并不是做這塊出身的,對于我而言,我的austin項(xiàng)目技術(shù)選型主要會(huì)關(guān)注兩塊(其實(shí)跟選擇apollo作為分布式配置中心的理由是一樣的):成熟、穩(wěn)定、社區(qū)是否活躍。

這一次我選擇了xxl-job作為austin的分布式任務(wù)調(diào)度框架。xxl-job已經(jīng)有很多公司都已經(jīng)接入了(說明他的開箱即用還是很到位的)。不過最新的一個(gè)版本在2021-02,近一年沒有比較大的更新了。

06、為什么austin需要分布式定時(shí)任務(wù)框架

回到austin的系統(tǒng)架構(gòu)上,austin-admin后臺(tái)管理頁面已經(jīng)被我造出來了,這個(gè)后臺(tái)管理系統(tǒng)會(huì)提供「消息模板」的管理功能。

那發(fā)送一條消息不單單是「技術(shù)側(cè)」調(diào)用接口進(jìn)行發(fā)送的,還有很多是「運(yùn)營側(cè)」通過設(shè)置定時(shí)進(jìn)而推送。

而這個(gè)功能,就需要用到分布式定時(shí)任務(wù)框架作為中間件支撐我的業(yè)務(wù),并且很重要的一點(diǎn):分布式定時(shí)任務(wù)框架需要支持動(dòng)態(tài)創(chuàng)建定時(shí)任務(wù)的功能。

當(dāng)在頁面點(diǎn)擊「啟動(dòng)」的時(shí)候,就需要?jiǎng)?chuàng)建一個(gè)定時(shí)任務(wù),當(dāng)在頁面點(diǎn)擊「暫停」的時(shí)候,就需要停止定時(shí)任務(wù),當(dāng)在頁面點(diǎn)擊「刪除」模板的時(shí)候,如果曾經(jīng)有過定時(shí)任務(wù),就需要把它給一起刪掉。當(dāng)在頁面點(diǎn)擊「編輯」并保存的時(shí)候,也需要把停止定時(shí)任務(wù)。

嗯,所需要的流程就這些了

07、austin接入xxl-job

接入xxl-job分布式定時(shí)任務(wù)框架的步驟還是蠻簡單的(看下文檔基本就會(huì)了),我簡單說下吧。接入具體的代碼大家可以拉ausitn的下來看看,我會(huì)重點(diǎn)講講我接入時(shí)的感受。

官網(wǎng)文檔:https://www.xuxueli.com/xxl-job/#%E4%BA%8C%E3%80%81%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8

1、自己項(xiàng)目上引入xxl-job-core的maven依賴

2、在MySQL中執(zhí)行/xxl-job/doc/db/tables_xxl_job.sql的SQL腳本

3、從GiteeGitHub下載xxl-job的源碼,修改xxl-job-admin調(diào)度中心的數(shù)據(jù)庫配置,啟動(dòng)xxl-job-admin項(xiàng)目。

4、在自己項(xiàng)目上添加xxl-job相關(guān)的配置信息

5、使用@XxlJob注解修飾方法編寫定時(shí)任務(wù)的相關(guān)邏輯

從接入或者已經(jīng)看過文檔的小伙伴應(yīng)該就很容易發(fā)現(xiàn),xxl-job它是屬于「中心化」流派的分布式定時(shí)任務(wù)框架,調(diào)度器和執(zhí)行器是分離的。

在前面我提到了austin需要動(dòng)態(tài)增刪改定時(shí)任務(wù),而xxl-job是支持的,但我覺得沒封裝得足夠好,只在調(diào)度器上給出了http接口。而調(diào)用http接口是相對麻煩的,很多相關(guān)的JavaBean都沒有在core包定義,只能我自己再寫一次。

所以,我花了挺長的時(shí)間和挺多的代碼去完成動(dòng)態(tài)增刪改定時(shí)任務(wù)這個(gè)工作。

調(diào)度器和執(zhí)行器是分開部署的,意味著,調(diào)度器和執(zhí)行器的網(wǎng)絡(luò)是必須可通的:原本我在本地是沒有裝任何的環(huán)境的,包括MySQL我都是連接云服務(wù)器的,但是現(xiàn)在我要調(diào)試就必須在網(wǎng)絡(luò)可通的環(huán)境內(nèi),所以我不得不在本地啟動(dòng)xxl-job-admin調(diào)度中心來調(diào)試。

在啟動(dòng)執(zhí)行器的時(shí)候,會(huì)開一個(gè)新的端口給xxl-job-admin調(diào)度中心調(diào)用而不是復(fù)用SpringBoot默認(rèn)端口也是挺奇怪的?

08、總結(jié)

這篇文章主要講了什么是定時(shí)任務(wù)、為什么要用定時(shí)任務(wù)、在Java領(lǐng)域中如果有定時(shí)任務(wù)相關(guān)的需求可以用什么來實(shí)現(xiàn)、分布式定時(shí)任務(wù)的基礎(chǔ)知識以及如何接入XXL-JOB

相信大家對分布式定時(shí)任務(wù)框架有了個(gè)基本的了解,如果感興趣可以挑個(gè)開源框架去學(xué)學(xué),想了解接入的代碼可以把我的austin項(xiàng)目拉下來看看。

主要的代碼就在austin-cronxxl包下,而分布式應(yīng)用的代碼主要在austin-webMessageTemplateController跟模板的增刪改查耦合在一起了。

下一篇想來講講當(dāng)定時(shí)任務(wù)被觸發(fā),得到了一個(gè)人群文件,我是怎么設(shè)計(jì)去調(diào)用消息進(jìn)行推送下發(fā)的。

都看到這里了,點(diǎn)個(gè)贊一點(diǎn)都不過分吧?我是3y,下期見。


Java如何實(shí)現(xiàn)定時(shí)任務(wù)?的評論 (共 條)

分享到微博請遵守國家法律
门头沟区| 瑞丽市| 雷州市| 海原县| 龙山县| 元朗区| 昌江| 苍南县| 旬邑县| 陕西省| 平乡县| 绩溪县| 双峰县| 罗平县| 浏阳市| 平邑县| 阿克| 郯城县| 临西县| 昌图县| 临清市| 江油市| 鹤山市| 化州市| 栾川县| 新田县| 万源市| 正安县| 衡南县| 万全县| 沁阳市| 区。| 木兰县| 孟津县| 毕节市| 康马县| 彩票| 衡水市| 大庆市| 广丰县| 中牟县|