Scheduled Job多實例下只跑一臺實例設計

        Java 代碼開發過程中,常常需要跑一些定時任務,而部署的時候爲了提供高可用服務,往往部署在啓多臺實例。這就會引發一個問題,每臺實例上的scheduled job都會同時運行,這種情況下可以加實例鎖,保證同一時刻只會有一臺實例會跑scheduled job.當然,這個問題也可繼續做延伸: 做分佈式部署的時候,如何保證線程安全?感興趣的可以在評論區,把日常用的方案寫出來,歡迎大家多多交流。

        本文方案是使用鎖機制,由於定時任務是多臺實例在同一時間開始執行,可以採用鎖機制:每臺實例在跑之前,先去獲取鎖,成功獲取鎖的,開始執行定時任務,獲取不到的,放棄執行。lockid 存儲在mongodb中,當然也可以存儲在redis中。

第一步,創建一個model,用於保存lockid.

@Data
@Document(collection = "scheduled_lock")
public class ScheduledLock{
@Id
  private String id;

  @CreatedDate
  @Field("created_date")
  @JsonIgnore
  @Indexed(expireAfterSeconds = 3600) // = 3600 seconds
  private ZonedDateTime createdDate = ZonedDateTime.now();
}

expireAfterSeconds 這個屬性用來設置TTL.

第二步,實現lock, unlock方法。

public T lock(T lock) {
    log.debug("Request to define lock : [{}]", lock);
    return mongoLockRepository.insert(lock);
}

public void unlock(T lock) {
    log.debug("Request to release lock : [{}]", lock);
    mongoLockRepository.delete(lock.getId());
}

第三步,scheduled job跑之前先獲取鎖,獲取不到的就不運行。

@Scheduled
public void  scheduledJob() {
  log.info("begin to run sheduled job ....");
  ScheduledLock scheduledLock = new ScheduledLock();
  scheduledLock.setId("scheduled_job");
  try {
    // lock the current instance
    scheduledLockService.lock(scheduledLock);
    try {
      log.info("I get the lock and run schuduled job ...");
      handleData();
    } catch (Exception e) {
      log.error("handle data error", e);
    } finally {
      log.info("Finished to process.");
      // release lock
      scheduledLockService.unlock(scheduledLock);
    }
  } catch (DuplicateKeyException exception) {
    // don't get the lock
    log.warn("run scheduled job can't get the lock id");
  }
}

 

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