前言
老黃前段時間遇到了一個數據清洗的需求,其實就是每天凌晨把昨天的數據清洗一遍,歸歸類。
這是一個比較典型的定時任務的處理場景。
定時任務可以說就一把利器,幾乎每個公司都離不開,它的應用場景也不在少數,比如:
- 生成前一天的統計數據
- 每隔幾天清理一次日志
- 定期處理失效的單據
- ...
對於定時任務,常見的解決方案有下面幾種
- quartz.net
- hangfire
- xxl-job
- saturn
- ...
對於1和2,無疑是要投入學習成本的,要習慣它們的用法,不好的地方就是不能讓開發人員集中精力去處理業務上面的內容。
對於3和4,這兩個算是分布式任務調度的平台,很好的與業務解耦了,可以通過HTTP的接口來觸發任務的執行。
3和4想在生產環境高可用,離不開集群部署,在資源緊張的時候其實想部署這么一套東西其實還是挺不容易的。
對於上面的幾種方案,老黃都沒有采用,卻而代之的是k8s的cronjob。
為什么選擇cronjob
上面提到的幾種方案,也已經表達了不選用的原因了,無非就是成本和復雜度,下面來講講選擇k8s的cronjob的原因吧。
首先k8s的cronjob本身就可以當作是一個任務調度的平台了,調度的時候會創建一個POD來執行我們的任務。
其次的話,沒有復雜的依賴關系,只要編寫一個簡單的控制台程序就好了。
還有一個是成本問題,老黃公司用的k8s是serverless的,沒有實實在在的服務器資源,交付的只是鏡像,執行這些定時任務,都是按時間計費的。
1C2G的配置只要0.00006126塊錢一秒,假設你的任務執行要3分鍾,那這一次任務只要1毛錢就可以了。
選擇什么配置,最后要看的還是你業務的需要。
說了這么多,來個偽例子吧。
簡單例子
要先准備一下我們的任務內容,其實就是寫個簡單的控制台程序。
using System;
internal class Program
{
private static void Main(string[] args)
{
// 寫這個定時任務要處理的內容
Console.WriteLine($"Hello World! {a}");
}
}
這里有一個要注意的是,不要出現 Console.ReadLine
, Console.ReadKey
之類的東西,不然是run不起來的。
Dockerfile就不寫了,只要能把這個控制台程序打包成一個鏡像,可以run起來就可以了。
后面就是寫cronjob的配置了。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
labels:
etl: diagnosis
name: xyzxyz
namespace: prod
spec:
# 禁止並發運行
concurrencyPolicy: Forbid
failedJobsHistoryLimit: 1
jobTemplate:
metadata: {}
spec:
# 指定存活時長
activeDeadlineSeconds: 1200
# 指定失敗時可以重試2次
backoffLimit: 2
completions: 1
parallelism: 1
template:
spec:
containers:
- env:
- name: DOTNET_RUNNING_IN_CONTAINER
value: 'true'
- name: TZ
value: Asia/Shanghai
image: >-
xxxxx:5000/xxxxx:version
imagePullPolicy: IfNotPresent
name: xyzxyz
ports:
- containerPort: 80
protocol: TCP
resources:
# 這里只用了0.25C 0.5G
requests:
cpu: 250m
memory: 512Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Never
schedulerName: default-scheduler
# cron表達式,
schedule: 22 4 1/1 * ?
# 成功job歷史顯示個數
successfulJobsHistoryLimit: 1
里面的配置其實還是挺多的,對老黃的場景來說,上面的配置足夠了,對更多的配置,可以參考k8s的官網。
執行kubectl apply -f xxx.yml
就可以創建定時任務了,后面就會自動調度執行對應的任務了。
這里就不執行了,直接拿線上正在跑的三個定時任務給大家參考一下。
點詳情可以看到具體的執行情況。
寫在最后
定時任務這個問題的答案有很多種解法,可以選擇適合公司的最優解。
因為每種解法都有它好或者不好的地方,k8s的cronjob也是有它不足的地方的,最為明顯的就是cron表達式第1位是分鍾而不是秒,也就是說最小粒度只到分鍾,如果你的應用需要到秒的,可能就沒辦法支持到了。