Xxl-Job執(zhí)行器自動注冊是怎么做的?
一、xxl-job 執(zhí)行器自動注冊 ip 錯誤
先說一下問題發(fā)生的場景,這個問題來自我們測試 xxl-job 后管新增一個執(zhí)行器時,新增執(zhí)行器時選擇的是自動注冊機制,也就是由 xxl-job 自動填寫我們的執(zhí)行器 ip 地址。
相信使用過 xxl-job 的眾多開發(fā)同學(xué)里,有很多人新增執(zhí)行器時都是跟我們一樣選擇自動注冊。這個操作在容器化部署來臨之前沒什么問題,但是隨著后來 docker 容器的流行,線上服務(wù)大多以容器方式放行。
容器的 ip 都是虛擬 ip,不代表物理機真實通信地址的 ip,這里就有一個問題,如果 xxl-job 后管跟執(zhí)行器不在同一個服務(wù)器部署,并且執(zhí)行器自動注冊提交的是容器 ip,那么就會產(chǎn)生定時任務(wù)執(zhí)行失敗的問題,因為 xxl-job 后管調(diào)用不通執(zhí)行器。
這里給大家畫一個 xxl-job 調(diào)用執(zhí)行器的示意圖,方便大家理解,
調(diào)用流程
給大家講一下示意圖:
1.xxl-job 執(zhí)行器在啟動時會向 xxl-job 后管提交當(dāng)前服務(wù)器 ip,獲取邏輯在 com.xxl.job.core.util.IpUtil#getIp 方法中,這個方法在最新版本里默認優(yōu)先使用 JDK 提供的 InetAddress.getLocalHost() 方法獲取本機 ip。獲取不到的話再使用 JDK 提供的 NetworkInterface.getNetworkInterfaces() 方法遍歷本機網(wǎng)卡獲取 ip。
InetAddress.getLocalHost() 的一些坑:
- 在 Linux 環(huán)境通過獲取 /etc/hosts 和 /etc/resolv.conf 文件內(nèi)容,如果在 /etc/hosts 文件內(nèi)容中沒有匹配到對應(yīng)的 ip 地址,則通過 /etc/resolv.conf 中配置的 DNS 地址,向 DNS 服務(wù)器發(fā)出域名解析請求;
- 如果訪問 DNS 服務(wù)存在性能問題;
- InetAddress.getLocalHost() 實現(xiàn)中還加了 synchronized 鎖,并發(fā)環(huán)境中會影響性能。
2.調(diào)用 xxl-job 后管提供的 api/registry 接口,傳遞 xxl-job 執(zhí)行器名稱以及通信地址。
3.xxl-job 后管觸發(fā)定時任務(wù)時,會調(diào)用 xxl-job 執(zhí)行器提供的 /run 接口來執(zhí)行對應(yīng)任務(wù)。
在上面流程中,因為第二步提交的 xxl-job 執(zhí)行器通信地址錯誤,因而導(dǎo)致第三步執(zhí)行失敗。那么怎么解決這個問題嘞,其實很簡單,xxl-job 后管使用手動注冊填寫可以聯(lián)通的執(zhí)行器地址即可。
二、xxl-job執(zhí)行器自動注冊流程
xxl-job 執(zhí)行器的自動注冊流程其實很簡單,這里給大家梳理一下,
自動注冊、移除流程
三、執(zhí)行器注冊
客戶端在引入 xxl-job 執(zhí)行器啟動時,xxl-job 執(zhí)行器會啟動 ExecutorRegistryThread 線程,
圖片
這個線程是專門來做執(zhí)行器注冊的,它會每隔 30 秒調(diào)用 xxl-job 后管的 /api/register 接口提交執(zhí)行器地址和名稱。
xxl-job 后管收到執(zhí)行器的注冊信息后,會通過 registryOrRemoveThreadPool 線程池異步保存執(zhí)行器信息并發(fā)送成功響應(yīng)。
圖片
registryOrRemoveThreadPool 線程池會判斷是否已經(jīng)存在當(dāng)前執(zhí)行器注冊記錄,不存在就新建,存在就更新執(zhí)行器最新注冊時間。
所以當(dāng)我們在 xxl-job 后管新增執(zhí)行器,選擇自動注冊的時候,執(zhí)行器的地址就是這么來的。
四、執(zhí)行器移除
移除有兩個方式,一種是正常結(jié)束 xxl-job 執(zhí)行器,比如停止應(yīng)用程序,xxl-job 執(zhí)行器就會調(diào)用 /api/registryRemove 接口用來移除當(dāng)前執(zhí)行器注冊信息。還有一種 xxl-job 后管啟動的 registryMonitorThread 線程自動檢測注冊時間已經(jīng)超期的執(zhí)行器注冊信息。
圖片
registryMonitorThread 線程會查詢所有自動注冊類型的執(zhí)行器,然后查詢它們對應(yīng)的注冊記錄最新時間是否超期,在xxl-job中,這個超期時間是90秒。超過 90 秒后就會移除執(zhí)行器。
移除時會同時刪除 xxl_job_group、xxl_job_registry 表的執(zhí)行器信息。
四、最后關(guān)于IpUtil.getIp() 的一點思考
其實查看 xxl-job 執(zhí)行器獲取執(zhí)行器 ip 的 com.xxl.job.core.util.IpUtil 文件的提交歷史,我們可以發(fā)現(xiàn),早在 18 年就有人發(fā)現(xiàn)了這個問題,并向官方提交了 issue 和 pr,并且這個 pr 也被官方認可合并了。
圖片
然而官方在 2020 的提交記錄中又修改了 com.xxl.job.core.util.IpUtil.getIp 方法,
圖片
把本來放到方法末尾的 InetAddress.getLocalHost() 方法又提到了方法開頭。。。
看了提交歷史和上面關(guān)于InetAddress.getLocalHost() 一些坑的朋友,此處應(yīng)該一臉疑問。。。
這里就暫且當(dāng)作者基于某種考慮這么做,至于為什么,我已經(jīng)去 github 提交了 issue,幫大家問了。。。
圖片