昨天,有羣友反應根據之前這篇《使用Elastic Job實現定時任務》(https://blog.didispace.com/spring-boot-learning-2-7-2/)文章編寫測試定時任務的時候,報了類似下面的這個錯誤:
Caused by: org.apache.shardingsphere.elasticjob.infra.exception.JobConfigurationException: Job conflict with register center. The job 'my-simple-job' in register center's class is 'com.didispace.chapter72.MySimpleJob', your job class is 'com.didispace.chapter74.MySimpleJob'
at org.apache.shardingsphere.elasticjob.lite.internal.config.ConfigurationService.checkConflictJob(ConfigurationService.java:86) ~[elasticjob-lite-core-3.0.0.jar:3.0.0]
at org.apache.shardingsphere.elasticjob.lite.internal.config.ConfigurationService.setUpJobConfiguration(ConfigurationService.java:70) ~[elasticjob-lite-core-3.0.0.jar:3.0.0]
at org.apache.shardingsphere.elasticjob.lite.internal.setup.SetUpFacade.setUpJobConfiguration(SetUpFacade.java:66) ~[elasticjob-lite-core-3.0.0.jar:3.0.0]
at org.apache.shardingsphere.elasticjob.lite.internal.schedule.JobScheduler.<init>(JobScheduler.java:84) ~[elasticjob-lite-core-3.0.0.jar:3.0.0]
at org.apache.shardingsphere.elasticjob.lite.api.bootstrap.impl.ScheduleJobBootstrap.<init>(ScheduleJobBootstrap.java:36) ~[elasticjob-lite-core-3.0.0.jar:3.0.0]
at org.apache.shardingsphere.elasticjob.lite.spring.boot.job.ElasticJobBootstrapConfiguration.registerClassedJob(ElasticJobBootstrapConfiguration.java:101) ~[elasticjob-lite-spring-boot-starter-3.0.0.jar:3.0.0]
at org.apache.shardingsphere.elasticjob.lite.spring.boot.job.ElasticJobBootstrapConfiguration.constructJobBootstraps(ElasticJobBootstrapConfiguration.java:84) ~[elasticjob-lite-spring-boot-starter-3.0.0.jar:3.0.0]
at org.apache.shardingsphere.elasticjob.lite.spring.boot.job.ElasticJobBootstrapConfiguration.createJobBootstrapBeans(ElasticJobBootstrapConfiguration.java:57) ~[elasticjob-lite-spring-boot-starter-3.0.0.jar:3.0.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_151]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_151]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_151]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_151]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333) ~[spring-beans-5.3.8.jar:5.3.8]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157) ~[spring-beans-5.3.8.jar:5.3.8]
... 17 common frames omitted
根據錯誤消息Job conflict with register center. The job 'my-simple-job' in register center's
,初步判斷是ZooKeeper中存儲的任務配置出現衝突:任務名一樣,但實現類不同。
經過一番交流,原來他是使用公司測試環境的ZooKeeper來寫的例子做測試,同時之前有同事也寫過類似的任務,因爲配置的任務名稱是拷貝的,所以出現了任務名稱相對,但實現類不同的情況。
實際上,如果我們在一個大一些的團隊做開發的時候,只要存在多系統的話,那麼定時任務的重名其實是很有可能發生。比如:很多應用都可能存在一些定時清理某些資源的任務,就很可能起一樣的名字,然後註冊到同一個ZooKeeper,最後出現衝突。那麼有什麼好辦法來解決這個問題嗎?
方法一:任務創建的統一管理
最原始的處理方法,就是集中的管理任務創建流程,比如:可以開一個Wiki頁面,所有任務在這個頁面上登記,每個人登記的時候,可以查一下想起的名字是否已經存在。如果存在了就再想一個名字,並做好登記。
這種方法很簡單,也很好理解。但存在的問題是,當任務非常非常多的時候,這個頁面內容就很大,維護起來也是非常麻煩的。
方法二:巧用Elastic Job的namespace屬性來隔離任務名稱
回憶一下之前第一篇寫定時任務的時候,關於註冊中心的配置是不是有下面兩項:
elasticjob.reg-center.server-lists=localhost:2181
elasticjob.reg-center.namespace=didispace
第一個elasticjob.reg-center.server-lists
不多說,就是ZooKeeper的訪問地址。這裏要重點講的就是第二個參數elasticjob.reg-center.namespace
。
其實在ZooKeeper中註冊任務的時候,真正衝突的並不純粹是因爲任務名稱,而是namespace + 任務名稱,全部一樣,纔會出現問題。所以,是不是引入把每個應用創建的任務都設定一個獨立的namespace,那麼是不是就隔離了呢?
繼續思考一下,每個應用是不是肯定有一些屬性是肯定不一樣的呢?最後,我給出了下面這樣的建議:
spring.application.name=chapter74
elasticjob.reg-center.server-lists=localhost:2181
elasticjob.reg-center.namespace=${spring.application.name}
即:將定時任務服務的elasticjob.reg-center.namespace
設置爲當前Spring Boot應用的名稱一致spring.application.name
。
通常,我們在規劃各個Spring Boot應用的時候,都會做好唯一性的規劃,這樣未來註冊到Eureka、Nacos等註冊中心的時候,也可以保證唯一。利用好這個唯一參數,也可以方便的幫我們把各個應用的定時任務也都隔離出來,也就解決了文章開頭,我們所說的場景了。
記得自己動手寫一寫,這樣體會更深哦!如果碰到問題,可以拉取文末的代碼示例對比一下是否有地方配置不一樣。下一篇,我們還將繼續介紹關於定時任務的一些高級內容。關注我,收藏本系列教程《Spring Boot 2.x基礎教程》:http://blog.didispace.com/spring-boot-learning-2x/
學習過程中如遇困難,加入Spring技術交流羣,參與討論
關注我回復「加羣」,加入Spring技術交流羣
代碼示例
本文的完整工程可以查看下面倉庫中的chapter7-4
目錄:
-
Github:https://github.com/dyc87112/SpringBoot-Learning/tree/master/2.x -
Gitee:https://gitee.com/didispace/SpringBoot-Learning/tree/master/2.x
如果您覺得本文不錯,歡迎Star
支持,您的關注是我堅持的動力!
往期推薦
本文分享自微信公衆號 - 程序猿DD(didispace)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。