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

          擁抱Kubernetes,再見了SpringBoot cronjob

          共 5820字,需瀏覽 12分鐘

           ·

          2020-11-06 07:00

          ?寫給自己看,說給別人聽。你好,這是think123的第77篇原創(chuàng)文章



          這張圖是我媳婦兒用手機(jī)拍的,我覺得很棒。放出來讓大家欣賞下

          項目開發(fā)中總是需要執(zhí)行一些定時任務(wù),比如定時處理數(shù)據(jù)之后發(fā)送郵件,定時更新緩存等等。

          Java定時任務(wù)

          1. 基于 java.util.Timer 定時器,實現(xiàn)類似鬧鐘的定時任務(wù)
          2. 使用 Quartz、elastic-job、xxl-job 等開源第三方定時任務(wù)框架,適合分布式項目應(yīng)用
          3. 使用 Spring 提供的一個注解:@Schedule

          項目框架使用的是SpringBoot,所以之前定時任務(wù)使用的是SpringBoot中的@Scheduled。可是這種方式并不適合我們現(xiàn)在的cloud環(huán)境,為了更加cloud native一點,我刪除了使用SpringBoot寫的37個定時任務(wù),改為使用Kubernetes cronjob的方式。

          定時任務(wù)代碼編寫

          public?interface?Command?{
          ????/**
          ?????*?遵循Unix約定,如果命令執(zhí)行正常,則返回0;否則為非0。
          ?????*/

          ????int?execute(String...?args);
          }

          首先定義一個接口,所有具體的定時任務(wù)都必須實現(xiàn)該接口。接下來是具體的某一個定時任務(wù)

          @Component
          @Slf4j
          public?class?ProjectCommandLineRunner?implements?CommandLineRunner?{

          ??Map?commandMap?=?new?HashMap<>();

          ??@Autowired
          ??private?SendEmailCommand?sendEmailCommand;

          ??@PostConstruct
          ??private?void?init()?{
          ????commandMap.put("sendEmail",?sendEmailCommand);
          ??}

          ??@Override
          ??public?void?run(String...?args)?throws?Exception?{

          ????if?(args.length?==?0)?{
          ????????return;
          ????}

          ????if?(!commandMap.containsKey(args[0]))?{
          ????????log.error("'{}'?command?not?found",?args[0]);
          ????????System.exit(-1);
          ????}

          ????Command?command?=?commandMap.get(args[0]);

          ????String[]?arguments?=?Arrays.copyOfRange(args,?1,?args.length);

          ????System.exit(command.execute(arguments));
          ??}
          }

          @Component
          @Slf4j
          public?class?SendEmailCommand?implements?Command?{

          ??@Override
          ??public?int?execute(String...?args)?{

          ????try?{
          ??????//?省略業(yè)務(wù)邏輯代碼

          ??????log.info("send?email?success");

          ??????return?0;

          ????}?catch?(Exception?e)?{
          ??????log.error("send?email?error",?e);
          ??????return?-1;
          ????}
          ??}
          }

          上面的代碼我們采用了策略模式,后面即使新增其他定時任務(wù)也只是會改動很少的代碼。

          本地調(diào)試

          cronjob不用打包成單獨的鏡像,它直接和我們的web應(yīng)用公用同一個鏡像,本地調(diào)試的時候也是極為方便的,只需要我們啟動SpringBoot Application時指定參數(shù)即可

          調(diào)試定時任務(wù)

          對應(yīng)的定時任務(wù)執(zhí)行完成之后就會,application就會退出。

          cronjob yaml

          一個基本的cronjob yaml如下所示

          apiVersion:?batch/v1beta1
          kind:?CronJob
          metadata:
          ??name:?send-email-job
          spec:
          ??failedJobsHistoryLimit:?3
          ??successfulJobsHistoryLimit:?1
          ??startingDeadlineSeconds:?180
          ??concurrencyPolicy:?Forbid
          ??schedule:?"0?4?*?*?1-5"
          ??jobTemplate:
          ????spec:
          ??????template:
          ????????spec:
          ??????????containers:
          ????????????-?name:?send-email-job
          ??????????????image:?harbor.xxx.com/think123/project
          ??????????????imagePullPolicy:?Always
          ??????????????command:?["java"]
          ??????????????args:?["-jar","/app/target/think123-task.jar","sendEmail"]
          ??????????????envFrom:
          ????????????????-?configMapRef:
          ????????????????????name:?smcp-config
          ????????????????-?secretRef:
          ????????????????????name:?smcp-service-secret
          ??????????????resources:
          ????????????????requests:
          ??????????????????cpu:?"250m"
          ??????????????????memory:?1024Mi
          ????????????????limits:
          ??????????????????cpu:?"500m"
          ??????????????????memory:?1024Mi
          ??????????restartPolicy:?Never

          在定時任務(wù)中,可能某個job還沒有執(zhí)行完,另外一個job就產(chǎn)生了。這個時候我們可以通過spec.concurrencyPolicy字段來定義具體的處理策略。

          1. concurrencyPolicy=Allow,這也是默認(rèn)情況,這意味著這些Job可以同時存在;
          2. concurrencyPolicy=Forbid,這意味著不會創(chuàng)建新的Pod,該創(chuàng)建周期被跳過;
          3. concurrencyPolicy=Replace,這意味著新產(chǎn)生的Job會替換舊的、沒有執(zhí)行完的Job

          幾個關(guān)鍵參數(shù)解釋如下:

          1. schedule : Unix Cron格式的表達(dá)式,cron表達(dá)式中的五個部分分別代表:分鐘、小時、日、月、星期。
          2. startingDeadlineSeconds :表示在過去的多少秒(這里設(shè)置的180)里,如果job創(chuàng)建失敗的數(shù)據(jù)達(dá)到了100次,那么這個job就不會被創(chuàng)建執(zhí)行了。
          3. restartPolicy: 重啟策略(有Never和OnFailure兩個選項)。當(dāng)job正常結(jié)束之后是否需要重啟

          restartPolicy在Job對象里只允許被設(shè)置為Never和OnFailure;而在Deployment對象里,restartPolicy則只允許被設(shè)置為Always。

          實際上在jobTemplate.spec.template中可以像pod中那樣,指定volume,指定nodeSelector,都是可以的。這個template實際上就是指的pod的template。比如上面示例我們就指定了環(huán)境變量,我們的一些參數(shù)就可以通過環(huán)境變量進(jìn)行注入,比如redis地址,mongodb用戶名密碼等。

          實際使用

          上面的yaml雖然可以直接使用,但是我們用不著每個job都去寫一份同樣的模板,實際中我們會使用Kustomize控制模板來生成job。比如我們有一個新的任務(wù),是計算熱點文章并更新redis

          對于這個任務(wù)而言,變化的主要有兩個地方,第一個是定時任務(wù)的時間不同,第二個是指定的參數(shù)不同。所以我們的每個任務(wù)只需要更新這兩個參數(shù)就行了

          關(guān)于kustomize的使用可以參考我之前的kustomize的介紹,打包的話可以看看springboot build的文章

          對于模板設(shè)定,我們形成了下面的目錄結(jié)構(gòu)

          $?tree
          .
          |--?base
          |???|--?cronjob.yaml
          |???`--?kustomization.yaml
          `--?overlay
          ????`--?beta
          ????????|--?kustomization.yaml
          ????????`--?send-email-patch-args.yaml

          cronjob.yaml作為所有job的模板,而send-emial-patch-args.yaml則是針對具體的job的一個替換。涉及到的yaml文件內(nèi)容如下:


          #?base/kustomization.yaml

          apiVersion:?kustomize.config.k8s.io/v1beta1
          kind:?Kustomization
          resources:
          -?cronjob.yaml


          #?base/cronjob.yaml
          apiVersion:?batch/v1beta1
          kind:?CronJob
          metadata:
          ??name:?think123-
          spec:
          ??failedJobsHistoryLimit:?3
          ??successfulJobsHistoryLimit:?1
          ??startingDeadlineSeconds:?180
          ??concurrencyPolicy:?Forbid
          ??schedule:?"0?0?1?*?*"
          ??jobTemplate:
          ????spec:
          ??????template:
          ????????spec:
          ??????????containers:
          ????????????-?name:?cron-job
          ??????????????image:?harbor.xxx.com/think123/my-task
          ??????????????imagePullPolicy:?Always
          ??????????????args:
          ????????????????-?"help"
          ??????????????envFrom:
          ????????????????-?configMapRef:
          ????????????????????name:?smcp-config
          ????????????????-?secretRef:
          ????????????????????name:?smcp-service-secret
          ??????????????resources:
          ????????????????requests:
          ??????????????????cpu:?"250m"
          ??????????????????memory:?1024Mi
          ????????????????limits:
          ??????????????????cpu:?"500m"
          ??????????????????memory:?1024Mi
          ??????????restartPolicy:?Never


          #?overlay/beta/kustomization.yaml
          apiVersion:?kustomize.config.k8s.io/v1beta1
          kind:?Kustomization

          nameSuffix:?send-email-job

          resources:
          -?../../base/

          patchesStrategicMerge:
          ??-?send-email-patch-args.yaml


          #?overlay/beta/send-email-patch-args.yaml
          apiVersion:?batch/v1beta1
          kind:?CronJob
          metadata:
          ??name:?think123-
          spec:
          ??schedule:?"0?4?*?*?1-5"
          ??jobTemplate:
          ????spec:
          ??????template:
          ????????spec:
          ??????????containers:
          ????????????-?name:?send-email-job
          ??????????????args:?["sendEmail"]

          你可以使用kustomize build beta > send-email-cron-job.yaml命令,然后查看send-email-cron-job.yaml文件,就可以看到生成的具體的cronjob的詳細(xì)。

          kustomize的文檔可以參考: https://kubernetes-sigs.github.io/kustomize/api-reference/

          為什么要用Kubernetes Cron Job

          使用SpringBoot的定時任務(wù)不香嗎?為什么要還要引入新的東西。再想這個問題的時候,想想為什么你在SpringBoot中不寫Servlet,不是一樣可以嗎?

          其實想想還是有原因的,首先我們的服務(wù)是分布式的,我們的定時任務(wù)應(yīng)該只需要運行一次,而不是每個實例都運行一次,如果用SpringBoot的task那么我們需要用代碼來保證這個行為。

          如果引入分布式任務(wù)框架,又是引入了一堆其他新的東西,比如注冊中心等等,而且還要去學(xué)習(xí)一項新的技術(shù)。

          而我們的服務(wù)由于是通過Kubernetes部署的,我們的job再使用Kubernetes來,更是相得益彰。


          作者:think123, 一個試圖把問題想簡單的程序員。

          "三思而后行 , think23"



          瀏覽 84
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  影音先锋男人网站 | 国产偷拍自拍在线观看 | 日一区人2区 | 色香蕉视频在线 | 激情综合网站 |