在分散式系統中,如何精準判斷節點存活、避免「雙主」問題、實現資源自動回收?租約機制是核心解法。本文從理論基礎切入,深度剖析 HBase RegionServer 中 Lease 管理的實現細節與原始碼邏輯。
分散式系統依賴心跳維持高可用:
核心痛點:網路分區導致控制中心收不到心跳,無法區分「節點掛了」還是「網路斷了」。貿然啟動新節點極易造成「雙主」(Split-brain),導致數據衝突。
這實現了「寧可暫時不可用,絕不雙主」的安全語義。
客戶端長連接持有服務端資源(如 Scanner),若客戶端異常斷開未釋放,會拖垮服務端。租約機制強制客戶端定期續約,過期自動清理資源。
單點 Lease 服務是單點故障。生產環境通常由 ZooKeeper/etcd/Consul 叢集 提供高可用 Lease 服務,保證租約發放與檢測的可靠性。
// HRegionServer.run() 中
this.leases = new Leases(this.threadWakeFrequency); // 核心租約管理器
public class Leases extends HasThread {
private final Map leases = new ConcurrentHashMap<>();
private final int leaseCheckFrequency; // 檢查間隔
private volatile boolean stopRequested = false;
// Lease 實體
static class Lease {
private final String leaseName;
private final LeaseListener listener;
private int leaseTimeoutPeriod; // 租約時長
private long expirationTime; // 到期時間戳
}
}
Client Scan 請求拆分為多次 RPC,每次 RPC 租用一個 RegionScanner,由 ScannerLease 管理:
public class ScannerLease implements LeaseListener {
private final String scannerName;
private final RegionServer regionServer;
@Override
public void leaseExpired() {
// 1. 從 scanners Map 移除
// 2. 呼叫 RegionScanner.close()
// 3. 觸發 Coprocessor pre/postScannerClose 鉤子
}
}
public void run() {
while (!stopRequested || (stopRequested && !leases.isEmpty())) {
long nextLeaseDelay = Long.MAX_VALUE;
Lease nextLease = null;
// 遍歷所有租約
for (Iterator> it = leases.entrySet().iterator(); it.hasNext();) {
Lease lease = it.next().getValue();
long delay = lease.getDelay(TimeUnit.MILLISECONDS);
if (delay > 0) {
// 記錄最近即將過期的租約
if (nextLease == null || delay < nextLeaseDelay) {
nextLease = lease;
nextLeaseDelay = delay;
}
} else {
// 租約過期:觸發 listener,從 Map 移除
if (lease.getListener() != null) {
lease.getListener().leaseExpired();
}
it.remove();
}
}
// 睡眠至下一個最近過期時間
Thread.sleep(Math.min(nextLeaseDelay, leaseCheckFrequency));
}
}
關鍵設計:
深入理解租約機制,能幫助您設計出更健壯的分散式系統組件。