teprunner測試平臺定時任務這次終於穩了

自動化程式碼美學 發表於 2021-05-29

teprunner測試平臺已經有一個多月沒有更新了,主要原因是定時任務不夠穩定,經過反覆試錯,找到了解決辦法,這次終於穩定了。

本文開發內容

作為測試平臺而言,定時任務算是必備要素了,只有跑起來的自動化,才能算是真正的自動化。本文將給測試計劃新增定時任務功能,具體如下:

  • 前端新增測試計劃的定時任務開關
  • 採用crontab表示式設定計劃時間
  • 後端整合django-apschedule,在資料庫中記錄任務明細和執行詳情。
  • 定時清理執行記錄。

前端效果圖:

image-20210528222640069

前端開發內容

編輯src/views/teprunner/plan/PlanEditor.vue檔案:

image-20210528222823663

執行環境用el-select實現了下拉框,用el-switch實現了開關按鈕。

image-20210528223000259

el-pophover實現了幫助描述,可以參考編寫crontab表示式。

image-20210528223141339

在data中新增了表單項taskRunEnv、taskStatus、taskCrontab,必填規則,以及其他變數。

image-20210528223326133

頁面建立時讀取localStorage中的計劃資訊。

image-20210528223407162

並獲取執行環境下拉框選項。

image-20210528223437261

開關按鈕的文字是根taskStatus進行設定的。

image-20210528223522132

在儲存時,給請求新增上新的這3個引數。

後端開發內容

第一步是安裝django-apscheduler,要麼直接安裝:

pip install django-apscheduler

要麼更新專案程式碼後通過requirements.txt安裝:

pip install -r requirements.txt

然後編輯teprunnerbackend/settings.py檔案:

image-20210528223904507

在INSTALLED_APPS中新增django_apscheduler。

接著遷移資料庫,建立兩張任務表,一張任務明細表,一張任務執行情況表

python manage.py migrate

image-20210528224044201

編輯teprunner/models.py檔案:

image-20210528224148248

給Plan模型新增3個欄位。

編輯teprunner/serializers.py檔案:

image-20210528224243250

同樣的,給PlanSerializer新增3個欄位。

新建teprunner/views/task.py檔案:

image-20210528224352704

建立BackgroundScheduler的物件例項,Background指的是在後臺執行。並新增DjangoJobStore,把任務通過Django儲存到資料庫中。

image-20210528224622874

新增一個定時刪除執行記錄的任務,max_age是最大儲存時間,這裡設定為7天。scheduler.add_job()用來新增定時任務,trigger是觸發器,也就是計劃時間,這裡設定為每週一0點。id是任務的識別符號。max_instances指同時最多隻有一個例項。replace_existing設定為True,每次都更新已存在的任務,防止重啟服務導致scheduler.add_job()報錯。

image-20210528225020524

啟動任務。

編輯teprunner/views/run.py檔案:

image-20210528225152787

為了手動執行測試計劃和定時任務執行測試計劃共用,這裡把執行程式碼抽取了部分作為run_plan_engine()函式。

編輯teprunner/views/plan.py檔案:

image-20210528225455141

重寫create方法,先根據測試計劃的名字判斷是否已存在,如果存在就直接返回500。接著判斷開關如果開啟,那麼就通過scheduler.add_job()新增任務。跟剛才新增任務的有點區別是,通過args引數指定了func函式的引數。最後把任務新增日誌寫到響應中返回。

image-20210528225724696

重寫update方法,先判斷測試計劃是否已經存在,判斷規則是根據名字去查詢已存在記錄,如果找到同名計劃,且id不是自己,那麼就認為已存在同名計劃,直接返回500。

image-20210528225911350

然後判斷如果開關開啟,就新增任務;如果開關關閉,就刪除任務,刪除任務使用scheduler.remove_job()。

image-20210528230002678

最後重寫destroy方法,在刪除測試計劃時,一併刪除定時任務。

猴子補丁解決pymysql連線問題

為什麼定時任務會不穩定?因為我用的pymysql庫,它不會進行資料庫連線斷開後重試。Django和MySQL建立建立後,何時斷開連線通過CONNECT_MAX_AGE來設定,預設是0,表示使用完馬上斷開連線。Django只會對Web請求採取這個策略,使用signals.request_started.connect(close_old_connections)和signals.request_finished.connect(close_old_connections)來關閉舊連線。但定時任務不是Web請求,而是直接連線資料庫,Django並不會去主動斷開這個連線。而MySQL預設8小時會把連線斷掉,於是當Django拿著已經被MySQL斷開的連線物件去請求MySQL,就報錯了。

當我在本地安裝了MySQL後,重啟MySQL就能復現這個問題。

解決辦法一是把舊連線復活,進行斷線重連,但是會導致連線佔用可能越來越多,耗費資源。解決辦法二是像Django處理Web請求一樣,每次用完就斷開,下次使用再重新連線,佔用資源少。

猴子補丁是指不修改第三方庫的基礎上,對庫的功能進行擴充套件。我給django-apscheduler寫了個猴子補丁,實現第二個解決辦法,用完就斷開連線:

image-20210528231438135

並且通過issue方式,告訴了它的作者:

image-20210528231757896

這開啟了我在GitHub上英文交流技術的大門。

比如我又給loguru提了個bug,此時已經和loguru的作者英文交流了5個回合。

小結

本文給測試計劃新增了定時任務功能,為teprunner測試平臺補上了一塊重要拼圖。從此它不但能批量執行用例了,還能按照計劃時間,定時執行,實現了真正的自動化。