ElasticJob 快速上手

1.  ElasticJob 是什麼

ElasticJob 是一個分佈式調度解決方案,由兩個相互獨立的子項目 ElasticJob-Lite 和 ElasticJob-Cloud 組成。 

ElasticJob-Lite 定位爲輕量級無中心化解決方案,使用jar的形式提供分佈式任務的協調服務。

ElasticJob 已於2020年5月28日成爲 Apache ShardingSphere 的子項目。 

ElasticJob特性:

  • 彈性調度
    • 支持任務在分佈式場景下的分片和高可用
    • 能夠水平擴展任務的吞吐量和執行效率
    • 任務處理能力隨資源配備彈性伸縮 
  • 資源分配
    • 在適合的時間將適合的資源分配給任務並使其生效
    • 相同任務聚合至相同的執行器統一處理
    • 動態調配追加資源至新分配的任務  
  • 作業治理
    • 失效轉移
    • 錯過作業重新執行
    • 自診斷修復
  • 作業開放生態
    • 可擴展的作業類型統一接口
    • 豐富的作業類型庫,如數據流、腳本、HTTP、文件、大數據等
    • 易於對接業務作業,能夠與 Spring 依賴注入無縫整合  
  • 可視化管控端
    • 作業管控端
    • 作業執行歷史數據追蹤
    • 註冊中心管理 

2.  實例演示

這裏採用最新版本 3.0.0-RC1 

1、啓動zookeeper服務

首先,下載zookeeper-3.6.0版本,解壓後複製一份zoo_sample.cfg,重命名未zoo.cfg,保持默認配置即可

注意,zookeeper-3.6.0啓動以後會佔用三個端口,其中包括8080哦

2、編寫定時任務業務邏輯

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>elasticjob-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <elasticjob-lite.version>3.0.0-RC1</elasticjob-lite.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere.elasticjob</groupId>
            <artifactId>elasticjob-lite-spring-boot-starter</artifactId>
            <version>${elasticjob-lite.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-jdbc</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere.elasticjob</groupId>
            <artifactId>elasticjob-error-handler-dingtalk</artifactId>
            <version>${elasticjob-lite.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

elasticjob:
  regCenter:
    serverLists: 192.168.100.15:2181
    namespace: elasticjob-demo
    baseSleepTimeMilliseconds: 2000
    maxSleepTimeMilliseconds: 4000
    maxRetries: 3
  jobs:
    firstJob:
      elasticJobClass: com.example.job.FirstJob
      cron: 0/6 * * * * ?
      shardingTotalCount: 3
      jobErrorHandlerType: DINGTALK
      props:
        dingtalk:
          webhook: https://oapi.dingtalk.com/robot/send?access_token=xxx
          secret: ASDF
          connectTimeout: 3000
          readTimeout: 5000
    secondJob:
      elasticJobClass: com.example.job.SecondJob
      cron: 0/10 * * * * ?
      shardingTotalCount: 1
      jobErrorHandlerType: DINGTALK
      props:
        dingtalk:
          webhook: https://oapi.dingtalk.com/robot/send?access_token=xxx
          secret: ASDF
          connectTimeout: 3000
          readTimeout: 5000 

兩個定時任務

FirstJob.java

package com.example.job;

import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.simple.job.SimpleJob;
import org.springframework.stereotype.Component;

/**
 * @author ChengJianSheng
 * @date 2021/1/13
 */
@Component
public class FirstJob implements SimpleJob {
    @Override
    public void execute(ShardingContext shardingContext) {
        switch (shardingContext.getShardingItem()) {
            case 0:
                // do something by sharding item 0
                System.out.println(0);
                // int a = 1 / 0;
                break;
            case 1:
                // do something by sharding item 1
                System.out.println(1);
                break;
            case 2:
                // do something by sharding item 2
                System.out.println(2);
                break;
            // case n: ...
        }
    }
}

SecondJob.java

package com.example.job;

import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.simple.job.SimpleJob;
import org.springframework.stereotype.Component;

/**
 * @author ChengJianSheng
 * @date 2021/1/18
 */
@Component
public class SecondJob implements SimpleJob {
    @Override
    public void execute(ShardingContext shardingContext) {
        System.out.println("hello");
    }
} 

項目結構

運行項目即可

通過 ElasticJob-UI 查看任務

https://shardingsphere.apache.org/elasticjob/current/cn/downloads/

3.  啓動報錯排查

項目啓動過程中,可能會報如下錯誤

org.apache.zookeeper.ClientCnxn$EndOfStreamException: Unable to read additional data from server sessionid 0x1000bdf48160002, likely server has closed socket

org.apache.shardingsphere.elasticjob.reg.exception.RegException: org.apache.zookeeper.KeeperException$OperationTimeoutException: KeeperErrorCode = OperationTimeout

Caused by: org.apache.zookeeper.KeeperException$OperationTimeoutException: KeeperErrorCode = OperationTimeout

最開始,我以爲是zookeeper版本的問題,後來換了版本也不行,防火牆關了也不行

然後,我懷疑是開發環境問題,於是在本地運行zookeeper,程序連127.0.0.1:2181,居然可以了

於是我陷入了沉思,爲今之計,只剩下一個辦法了,打斷點調試

找到了異常拋出的位置,如下圖

baseSleepTimeMilliseconds 表示 等待重試的間隔時間的初始值

maxSleepTimeMilliseconds  表示 等待重試的間隔時間的最大值

maxRetries 表示 最大重試次數

根據代碼中意思,如果在 maxSleepTimeMilliseconds * maxRetries 毫秒內還沒有連接成功,則連接關閉,並拋出操作超時異常

聯想到,連接本地zookeeper可以,連開發環境zk就不行,再加上觀察日誌從連接開始到拋異常的時間間隔,我猜到應該是maxSleepTimeMilliseconds設置太短了

於是,application.yml配置文件中將maxSleepTimeMilliseconds設置爲4000,baseSleepTimeMilliseconds設置爲2000

然後好使

回想剛開始報的那些錯,其實根本就還沒有連上zookeeper

4.  作業分片

ElasticJob 中任務分片項的概念,使得任務可以在分佈式的環境下運行,每臺任務服務器只運行分配給該服務器的分片。 隨着服務器的增加或宕機,ElasticJob 會近乎實時的感知服務器數量的變更,從而重新爲分佈式的任務服務器分配更加合理的任務分片項,使得任務可以隨着資源的增加而提升效率。

任務的分佈式執行,需要將一個任務拆分爲多個獨立的任務項,然後由分佈式的服務器分別執行某一個或幾個分片項。

也就是說,分片是爲了在分佈式環境下高效合理利用任務服務器資源的。簡單地來講,一個定時任務,我們運行多臺服務器,這意味着有多個實例在執行同一項任務,分片就是爲了告訴這些實例各自該處理那些數據,最大限度的降低數據重複處理的問題,同時加快任務處理速度。每個任務實例該處理哪些數據,是根據分片項來的,在任務代碼層面,就可以根據分片項來進行邏輯判斷。

舉例說明,如果作業分爲 4 片,用兩臺服務器執行,則每個服務器分到 2 片,分別負責作業的 50% 的負載

分片項

ElasticJob 並不直接提供數據處理的功能,而是將分片項分配至各個運行中的作業服務器,開發者需要自行處理分片項與業務的對應關係。 分片項爲數字,始於 0 而終於分片總數減 1。

個性化分片參數

個性化參數可以和分片項匹配對應關係,用於將分片項的數字轉換爲更加可讀的業務代碼。 

合理使用個性化參數可以讓代碼更可讀。例如,如果配置爲 0=北京,1=上海,2=廣州,那麼代碼中直接使用北京,上海,廣州的枚舉值即可完成分片項和業務邏輯的對應關係。

分片策略

平均分片策略

根據分片項平均分片。如果作業服務器數量與分片總數無法整除,多餘的分片將會順序的分配至每一個作業服務器。

舉例說明:

  • 如果 3 臺作業服務器且分片總數爲9, 則分片結果爲:1=[0,1,2], 2=[3,4,5], 3=[6,7,8]
  • 如果 3 臺作業服務器且分片總數爲8, 則分片結果爲:1=[0,1,6], 2=[2,3,7], 3=[4,5]
  • 如果 3 臺作業服務器且分片總數爲10,則分片結果爲:1=[0,1,2,9], 2=[3,4,5], 3=[6,7,8]

奇偶分片策略 

根據作業名稱哈希值的奇偶數決定按照作業服務器 IP 升序或是降序的方式分片。

如果作業名稱哈希值是偶數,則按照 IP 地址進行升序分片; 如果作業名稱哈希值是奇數,則按照 IP 地址進行降序分片。 可用於讓服務器負載在多個作業共同運行時分配的更加均勻。

舉例說明:

  • 如果 3 臺作業服務器,分片總數爲2且作業名稱的哈希值爲偶數,則分片結果爲:1 = [0], 2 = [1], 3 = []
  • 如果 3 臺作業服務器,分片總數爲2且作業名稱的哈希值爲奇數,則分片結果爲:3 = [0], 2 = [1], 1 = [] 

輪詢分片策略

根據作業名稱輪詢分片。

5.  官方文檔

https://shardingsphere.apache.org/elasticjob/current/cn/features/elastic/

https://shardingsphere.apache.org/elasticjob/current/cn/user-manual/elasticjob-lite/ 

https://shardingsphere.apache.org/elasticjob/current/cn/user-manual/elasticjob-lite/configuration/ 

https://shardingsphere.apache.org/elasticjob/current/cn/dev-manual/ 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章