etcd:一款比Redis更騷的分布式鎖的實現方式,kafka入門書籍

程序猿不想掉頭發 2021-09-19 11:46:06 阅读数:298

etcd 一款 redis 分布式 分布

/**

  • @program: distributed-lock

  • @description: 各種分布式鎖的基類,模板方法

  • @author: 行百裏者

  • @create: 2020/10/14 12:29
    **/
    public class AbstractLock implements Lock {
    @Override
    public void lock() {
    throw new RuntimeException(“請自行實現該方法”);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
    throw new RuntimeException(“請自行實現該方法”);
    }

    @Override
    public boolean tryLock() {
    throw new RuntimeException(“請自行實現該方法”);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
    throw new RuntimeException(“請自行實現該方法”);
    }

    @Override
    public void unlock() {
    throw new RuntimeException(“請自行實現該方法”);
    }

    @Override
    public Condition newCondition() {
    throw new RuntimeException(“請自行實現該方法”);
    }
    }


> 有了這個模板方法之後,後續分布式鎖的實現均可以繼承這個模板方法類。
**etcd分布式鎖的實現**:

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

@Data
public class EtcdDistributedLock extends AbstractLock {
private final static Logger LOGGER = LoggerFactory.getLogger(EtcdDistributedLock.class);

private Client client;
private Lock lockClient;
private Lease leaseClient;
private String lockKey;
//鎖路徑,方便記錄日志
private String lockPath;
//鎖的次數
private AtomicInteger lockCount;
//租約有效期。作用 1:客戶端崩潰,租約到期後自動釋放鎖,防止死鎖 2:正常執行自動進行續租
private Long leaseTTL;
//續約鎖租期的定時任務,初次啟動延遲,默認為1s,根據實際業務需要設置
private Long initialDelay = 0L;
//定時任務線程池
ScheduledExecutorService scheduledExecutorService;
//線程與鎖對象的映射
private final ConcurrentMap<Thread, LockData> threadData = Maps.newConcurrentMap();
public EtcdDistributedLock(Client client, String lockKey, Long leaseTTL, TimeUnit unit) {
this.client = client;
this.lockClient = client.getLockClient();
this.leaseClient = client.getLeaseClient();
this.lockKey = lockKey;
this.leaseTTL = unit.toNanos(leaseTTL);
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
}
@Override
public void lock() {
}
@Override
public void unlock() {
}

}


其中lock方法的實現:

  • 1.
  • 2.
  • 3.

@Override
public void lock() {
Thread currentThread = Thread.currentThread();
LockData existsLockData = threadData.get(currentThread);
 //System.out.println(currentThread.getName() + " 加鎖 existsLockData:" + existsLockData);
//鎖重入
if (existsLockData != null && existsLockData.isLockSuccess()) {
int lockCount = existsLockData.lockCount.incrementAndGet();
if (lockCount < 0) {
throw new Error(“超出etcd鎖可重入次數限制”);
}
return;
}
//創建租約,記錄租約id
long leaseId;
try {
leaseId = leaseClient.grant(TimeUnit.NANOSECONDS.toSeconds(leaseTTL)).get().getID();
//續租心跳周期
long period = leaseTTL - leaseTTL / 5;
//啟動定時續約
scheduledExecutorService.scheduleAtFixedRate(new KeepAliveTask(leaseClient, leaseId),
initialDelay,
period,
TimeUnit.NANOSECONDS);

 //加鎖
LockResponse lockResponse = lockClient.lock(ByteSequence.from(lockKey.getBytes()), leaseId).get();
if (lockResponse != null) {
lockPath = lockResponse.getKey().toString(StandardCharsets.UTF_8);
LOGGER.info("線程:{} 加鎖成功,鎖路徑:{}", currentThread.getName(), lockPath);
}
//加鎖成功,設置鎖對象
LockData lockData = new LockData(lockKey, currentThread);
lockData.setLeaseId(leaseId);
lockData.setService(scheduledExecutorService);
threadData.put(currentThread, lockData);
lockData.setLockSuccess(true);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

}


簡而言之,加鎖的代碼就是按照如下步驟來的:
1. 檢查鎖重入性
2. 設置租約
3. 開啟定時任務心跳檢查
4. 阻塞獲取鎖
5. 加鎖成功,設置鎖對象
業務處理完成(扣减庫存)後,解鎖:

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

@Override
public void unlock() {
Thread currentThread = Thread.currentThread();
 //System.out.println(currentThread.getName() + " 釋放鎖…“);
LockData lockData = threadData.get(currentThread);
 //System.out.println(currentThread.getName() + " lockData " + lockData);
if (lockData == null) {
throw new IllegalMonitorStateException(“線程:” + currentThread.getName() + " 沒有獲得鎖,lockKey:” + lockKey);
}
int lockCount = lockData.lockCount.decrementAndGet();
if (lockCount > 0) {
return;
}
if (lockCount < 0) {
throw new IllegalMonitorStateException(“線程:” + currentThread.getName() + " 鎖次數為負數,lockKey:" + lockKey);
}
try {
//正常釋放鎖
if (lockPath != null) {
lockClient.unlock(ByteSequence.from(lockPath.getBytes())).get();
}
//關閉續約的定時任務
lockData.getService().shutdown();
//删除租約
if (lockData.getLeaseId() != 0L) {
leaseClient.revoke(lockData.getLeaseId());
}
} catch (InterruptedException | ExecutionException e) {
 //e.printStackTrace();
LOGGER.error(“線程:” + currentThread.getName() + “解鎖失敗。”, e);
} finally {
//移除當前線程資源
threadData.remove(currentThread);
}
LOGGER.info(“線程:{} 釋放鎖”, currentThread.getName());
}


解鎖過程:
1. 重入性檢查
2. 移除當前鎖的節點路徑釋放鎖
3. 清除重入的線程資源
# 接口測試

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

最後

針對最近很多人都在面試,我這邊也整理了相當多的面試專題資料,也有其他大廠的面經。希望可以幫助到大家。

最新整理面試題
etcd:一款比Redis更騷的分布式鎖的實現方式,kafka入門書籍_程序員

上述的面試題答案都整理成文檔筆記。也還整理了一些面試資料&最新2021收集的一些大廠的面試真題

 CodeChina開源項目:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視頻】

最新整理電子書

etcd:一款比Redis更騷的分布式鎖的實現方式,kafka入門書籍_Java_02

最新整理大廠面試文檔

etcd:一款比Redis更騷的分布式鎖的實現方式,kafka入門書籍_後端_03

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持。

版权声明:本文为[程序猿不想掉頭發]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/09/20210919114606036v.html