開篇語
上一篇文章介紹了elastic-job-lite的入門,架構。使用和一些流程,裡面提到elastic-job-lite是一個去中心化,輕量級的任務排程框架,那為什麼elastic-jib-lite在啟動時要選取主節點呢?難道我看錯了,哈哈,不可能的,後文 elastic-job-lite簡稱ejl。
leader選舉
ejl定位為輕量級,去中心化,其任務排程由各自的機器驅動,各臺機器之間透過zk去協調,ejl為每個任務都建立一個JobScheduler,而在JobScheduler的初始化中回為每個job選舉一個主節點,記住不是全域性一個主節點,而是每個任務一個主節。如下圖,每個節點上都執行兩個任務job1,job2,那麼在啟動時每個節點就會建立兩個JobScheduler物件,為每一個任務在叢集中選舉一個leader。
這個leader是怎麼選舉出來的呢?什麼時候開始選舉?一、在整個叢集啟動時為每個任務選舉leader; 二、當有些任務的leader下線時,會重新選舉。
叢集啟動時選舉leader
在JobScheduler中
public void init(){
schedulerFacder.registerStartUpInfo();
}
public void registerStartUpInfo(){
leaderService.electLeader();
}
/** * 選舉主節點. */
public void electLeader() {
log.debug("Elect a new leader now.");
jobNodeStorage.executeInLeader(LeaderNode.LATCH,
new LeaderElectionExecutionCallBack());
log.debug("Leader election completed.");
}
public void executeInLeader(final String latchNode,
final LeaderExecutionCallback callback) {
//透過LeaderLatch進行選舉
//這是curator(zk的客戶端)中類
LeaderLatch latch =
new LeaderLatch(getClient()jobNodePath.getFullPath(latchNode));
//開始選舉
latch.start();
//阻塞,直到選舉成功
latch.await();
//在回撥方法中寫入主節點標記
callback.execute();
}
在callback.execute()中執行如下,再次判斷沒有主節點,將當前機器示例id寫入
if (!hasLeader()) {
jobNodeStorage.fillEphemeralJobNode(
LeaderNode.INSTANCE,instanceId));
}
leader重新選舉,主要是透過LeaderElectionJobListener
這個監聽器來實現leader重新選舉,當一個job還在執行,但leader節點下線了,就要重新選舉leader
class LeaderElectionJobListener extends AbstractJobListener{
protected void dataChanged(){
//具體選舉看上面的程式碼
leaderService.electLeader()
}
}
主節點的選舉的本質就是大夥競爭一個zk的分散式鎖。誰先得到鎖,誰就是主節點。
何時使用leader?有什麼作用?
分散式系統中,在一個任務執行過程中,有多個機器,多個分片,那麼如何去分配呢?哪些機器執行哪些分片呢,如果大家都參與豈不是亂了,這個時候就需要一個領導者來拍板。在ejl中有兩處需要leader節點來參與:
- 機器啟動後,任務開始第一次執行時,需要leader來分片
- 當叢集中有新的節點增加時,分片的數量有變化時或者有一些節點下線時都會觸發重新分片
主要程式碼如下,大家閱讀原始碼時可從AbstractElasticJobExecute
類中execute
方法開始看起。
AbstractElasticJobExecute類中
public final void execute(){
//獲取分片,這個方法中主節點leader會分片
ShardingContexts shardingContexts =
jobFacade.getShardingContexts();
}
在 getShardingContexts()中,有如下方法
shardingService.shardingIfNecessary();
/**
如果需要分片且當前節點為主節點, 則作業分片.
如果當前無可用節點則不分片.
*/
public void shardingIfNecessary() {
//不是主節點直接返回,不允許分片
if (!leaderService.isLeaderUntilBlock()) {
blockUntilShardingCompleted();
return;
}
....
}
leader節點刪除的時機
leader節點刪除的時機有三處,一,在leader節點所在機器程式CRASHED時,jvm透過鉤子方法刪除自己;二,作業被禁用時刪除leader節點,三,主節點程式遠端關閉
leader機器程式關閉
JobShutdownHookPlugin類中
public void shutdown() {
if (leaderService.isLeader()) {
leaderService.removeLeader();
}
}
作業被禁用時
LeaderAbdicationJobListener類中
protected void dataChanged(
//判斷是leader,並且作業被禁用
if (leaderService.isLeader()
&& isLocalServerDisabled(path, data)) {
leaderService.removeLeader();
}
}
作業終止排程時
InstanceShutdownStatusJobListener類中
protected void dataChanged(
//當job未暫停,
//並且排程控制器未暫停,
//並且事件是移除這個例項,
//並且執行例項未被移除
if (!JobRegistry.getInstance().isShutdown(jobName)
&&
!JobRegistry.getInstance()
.getJobScheduleController(jobName).isPaused()
&&
isRemoveInstance(path, eventType)
&&
!isReconnectedRegistryCenter()) {
//在這個方法中removeLeader
schedulerFacade.shutdownInstance();
}
}
EJL的leader在zk中的資料結構
程式碼在LeaderNode
類中
leader在zk中的根路徑
String ROOT = "leader";
這是leader進行選舉的父路徑 /leader/election
String ELECTION_ROOT = ROOT + "/election";
儲存主節點的地址 /leader/election/instance 這是一個臨時節點,leader所在的機器下線後,這個路徑就會消失,對於重新選舉有作用
String INSTANCE = ELECTION_ROOT + "/instance";
leader選舉的分散式鎖 /leader/election/latch
String LATCH = ELECTION_ROOT + "/latch";
本作品採用《CC 協議》,轉載必須註明作者和本文連結