包含時間順序的ID
此場景最簡單的實現方案,就是采用 twitter 的 Snowflake 算法。
ID總長64位,第1位不可用,41位表示時間戳,10位表示生成機器的id,后12位表示序列號。
- 為什么第一位不可用?第一位為0,可以確保ID在java的long類型數據一直為正整數遞增
- 同一時間戳即毫秒內,能產生多少個ID? 2^12 = 4096 個ID [ 0 ~ 4095 ]
- 唯一性?通過機器ID預先已經做了一次空間隔離,再通過時間戳做了一次時間隔離,最后通過時間戳內的計數實現了一定程度內的唯一
- 高性能?可以通過增加IDWorker來緩解高並發時的單機負載壓力
- 缺點?時間受限,41位可以表示69年(不過可以減少機器位來增加時間位數)
自增序列
原理
根據key獲取分布式鎖,獲得鎖后取得序號,並偏移配置的偏移量,替換原先的序號,最后釋放鎖。
基於zookeeper實現
基於zookeeper可以很快實現自增序列服務,引入apache的curator封裝的zookeeper客戶端。
1 |
<dependency> |
建立zookeeper連接,打開zkclient后,如果重復會使用,可以將其放入全局map中,作統一管理。
address:zookeeper的地址
RetryNTimes:重連策略(重連重試次數,重連間隔毫秒)
1 |
CuratorFramework zkClient = CuratorFrameworkFactory.newClient(address,new RetryNTimes(10, 5000)); |
獲取分布式鎖
按照業務邏輯,選擇合適的鎖,此處用的是可重入共享鎖,即一個客戶端在擁有鎖的同時,可以再請求獲取。
大專欄 分布式全局唯一ID與自增序列="gutter">1 |
InterProcessMutex lock = new InterProcessMutex(zkClient, lockPath); |
獲取下個序號
判斷序列名稱是否已經存在,如果不存在創建,存在則增加step並寫入
creatingParentsIfNeeded:當zk節點的父級不存在的時候,迭代創建
sequenceName:序列名稱
step:自增步長
1 |
String path = "/seq/"+sequenceName; |
因為需要實時修改zookeeper的節點信息,可以考慮建立序列池,例如直接取走10000個序列,由各個服務內部自己去生成,具體實現主要依賴到CAS,通過compareAndSet去實現單機內部的序號遞增,避免鎖的濫用