使用quartz或者spring-task實現任務調度在集羣環境下有什麼問題?
答:html
一、不敢輕易跟着應用服務多節點部署,可能會重複屢次執行而引起系統邏輯的錯誤web
二、quartz的集羣基於數據庫的狀態標記,涉及的表多達十多張,節點數量的增長並不能給咱們的每次執行效率帶來提高,即不能實現水平擴展。算法
一、一致性(保證定時任務單獨執行)spring
二、高可用性 (當前定時任務因外部緣由執行失敗後有替代方案)sql
三、定時任務分佈式處理(並行處理)數據庫
而這些問題已經有前輩都考慮到了,提供了不少的解決方案apache
TBSchedule:由淘寶開源的分佈式調度解決方案,目前好像沒有維護了,文檔較少api
Elastic-Job:由噹噹網開源的分佈式調度解決方案,文檔豐富友好,上手簡單瀏覽器
Elastic-Job官網地址:http://elasticjob.io/index_zh.html安全
Elastic-Job是一個分佈式調度解決方案,由兩個相互獨立的子項目Elastic-Job-Lite和Elastic-Job-Cloud組成。
Elastic-Job-Lite定位爲輕量級無中心化解決方案,使用jar包的形式提供分佈式任務的協調服務。
Elastic-Job-Cloud使用Mesos + Docker(TBD)的解決方案,額外提供資源治理、應用分發以及進程隔離等服務,Elastic-Job-Lite和Elastic-Job-Cloud提供同一套API開發做業,開發者僅需一次開發,便可根據須要以Lite或Cloud的方式部署
分佈式調度 Elastic-Job-Lite並沒有做業調度中心節點,而是基於部署做業框架的程序在到達相應時間點時各自觸發調度。 註冊中心僅用於做業註冊和監控信息存儲。而主做業節點僅用於處理分片和清理等功能
做業高可用 Elastic-Job-Lite提供最安全的方式執行做業。將分片總數設置爲1,並使用多於1臺的服務器執行做業,做業將會以1主n從的方式執行。 一旦執行做業的服務器崩潰,等待執行的服務器將會在下次做業啓動時替補執行。開啓失效轉移功能效果更好,能夠保證在本次做業執行時崩潰,備機當即啓動替補執行。
最大限度利用資源 Elastic-Job-Lite也提供最靈活的方式,最大限度的提升執行做業的吞吐量。將分片項設置爲大於服務器的數量,最好是大於服務器倍數的數量,做業將會合理的利用分佈式資源,動態的分配分片項。 例如:3臺服務器,分紅10片,則分片項分配結果爲服務器A=0,1,2;服務器B=3,4,5;服務器C=6,7,8,9。 若是服務器C崩潰,則分片項分配結果爲服務器A=0,1,2,3,4;服務器B=5,6,7,8,9。在不丟失分片項的狀況下,最大限度的利用現有資源提升吞吐量。
運維平臺:提供web控制檯用於管理做業
請先安裝3.4.6版本以上的zookeeper
一、maven添加jar依賴
<!-- elastic-job start --> <dependency> <groupId>com.dangdang</groupId> <artifactId>elastic-job-lite-core</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>com.dangdang</groupId> <artifactId>elastic-job-lite-spring</artifactId> <version>2.0.0</version> </dependency> <!-- elastic-job end --> <!-- zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency>
二、elastic-job與spring整合配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:reg="http://www.dangdang.com/schema/ddframe/reg" xmlns:job="http://www.dangdang.com/schema/ddframe/job" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.dangdang.com/schema/ddframe/reg http://www.dangdang.com/schema/ddframe/reg/reg.xsd http://www.dangdang.com/schema/ddframe/job http://www.dangdang.com/schema/ddframe/job/job.xsd"> <!--配置做業註冊中心 --> <reg:zookeeper id="regCenter" server-lists="172.18.7.133:2181" namespace="dd-job" base-sleep-time-milliseconds="1000" max-sleep-time-milliseconds="3000" max-retries="3" /> <!-- 配置做業--> <job:simple id="mySimpleJob" class="com.muheda.sipm.task.TestTask" registry-center-ref="regCenter" sharding-total-count="1" cron="0 0/5 * * * ?" overwrite="true" failover="true"/> </beans>
server-lists:zookeeper地址
sharding-total-count爲分片數量,設置爲1的時候,做業只會被分配到第一臺機器進行處理;官網建議此值應爲物理機的倍數
①分片策略:
1)、基於平均分配算法的分片策略,也是默認的分片策略; 若是分片不能整除,則不能整除的多餘分片將依次追加到序號小的服務器
2)、根據做業名的哈希值奇偶數決定IP升降序算法的分片策略
3)、根據做業名的哈希值對服務器列表進行輪轉的分片策略
②分片後臺處理:
若是咱們的數據庫只有一個,而物理機設置有多臺集羣,怎麼使用分片查詢功能?這裏有一個處理辦法是,第三步中的定時任務業務類中能夠經過shardingContext.getShardingTotalCount()和shardingContext.getShardingItem()取到總的分片數量和當前分片編號,咱們在執行業務查詢的時候能夠在sql後面這樣處理:
shardingContext.getShardingItem() = Id%shardingContext.getShardingTotalCount()
三、定時任務業務類:
package com.muheda.sipm.task; import com.dangdang.ddframe.job.api.ShardingContext; import com.dangdang.ddframe.job.api.simple.SimpleJob; import org.apache.log4j.Logger; /** * @Author: Sorin * @Descriptions: * @Date: Created in 2018/2/1 */ public class TestTask implements SimpleJob { private static Logger logger = Logger.getLogger(SimpleJob.class); @Override public void execute(ShardingContext shardingContext) { logger.info(String.format("ShardingItem: %s | Thread: %s | %s", shardingContext.getShardingItem(), Thread.currentThread().getId(), "SIMPLE")); logger.info("定時任務測試"); } }
四、使用不依賴與Spring的方式:
LoadElasticJob在項目啓動後加載一次,裏面加載job
@Component public class LoadElasticJob implements InitializingBean { private Logger logger = Logger.getLogger(this.getClass()); static int initBoolean = 0; @Override public void afterPropertiesSet() throws Exception { if(initBoolean==0){ init(); } initBoolean++; } public void init(){ logger.info("定時任務開始啓動!"); CoordinatorRegistryCenter regCenter = ElasticJobUtils.setUpRegistryCenter(); // 店鋪統計定時任務 ElasticJobUtils.jobShopStat(regCenter); logger.info("定時任務啓動成功!"); } }
ElasticJobUtils中定義具體的job屬性,至關於以前的xml中的bean.業務類是同樣的,這裏就不貼出來了
public class ElasticJobUtils { private static final String ZOOKEEPER_URL = PropertiesUtil.getValue("ZOOKEEPER_URL"); private static final String JOB_NAMESPACE = PropertiesUtil.getValue("JOB_NAMESPACE"); public static CoordinatorRegistryCenter setUpRegistryCenter() { ZookeeperConfiguration zkConfig = new ZookeeperConfiguration(ZOOKEEPER_URL, JOB_NAMESPACE); CoordinatorRegistryCenter result = new ZookeeperRegistryCenter(zkConfig); result.init(); return result; } /** * @Descripton: 店鋪統計定時任務 * @Author: Sorin * @param regCenter * @param jobEventConfig * @Date: 2018/3/19 */ public static void jobShopStat(final CoordinatorRegistryCenter regCenter){ JobCoreConfiguration coreConfig = JobCoreConfiguration.newBuilder(PropertiesUtil.getValue("job_shop_stat"), PropertiesUtil.getValue("core_shop_stat"), 1).build(); SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(coreConfig, StatManageAction.class.getCanonicalName()); new JobScheduler(regCenter, LiteJobConfiguration.newBuilder(simpleJobConfig).build()).init(); } }
解壓縮elastic-job-lite-console-${version}.tar.gz並執行bin\start.sh。 打開瀏覽器訪問 http://localhost:8899/ 便可訪問控制檯