包含时间顺序的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客户端。
1234 | <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId></dependency> |
建立zookeeper连接,打开zkclient后,如果重复会使用,可以将其放入全局map中,作统一管理。
address:zookeeper的地址
RetryNTimes:重连策略(重连重试次数,重连间隔毫秒)
12 | CuratorFramework zkClient = CuratorFrameworkFactory.newClient(address,new RetryNTimes(10, 5000));zkClient.start(); |
获取分布式锁
按照业务逻辑,选择合适的锁,此处用的是可重入共享锁,即一个客户端在拥有锁的同时,可以再请求获取。
大专栏 分布式全局唯一ID与自增序列="gutter">1234567891011121314151617 | InterProcessMutex lock = new InterProcessMutex(zkClient, lockPath);try { if (lock.acquire(time, unit)) { consumer.accept(lockPath); return true; } return false;} catch (Exception e) { logger.error(e.getMessage(), e);} finally { try { lock.release(); } catch (Exception e) { logger.error(e.getMessage(), e); }}return false; |
获取下个序号
判断序列名称是否已经存在,如果不存在创建,存在则增加step并写入
creatingParentsIfNeeded:当zk节点的父级不存在的时候,迭代创建
sequenceName:序列名称
step:自增步长
123456789 | String path = "/seq/"+sequenceName;boolean exists = zkClient.checkExists().forPath(path) != null;if (!exists) { zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, (step + "").getBytes());} else { byte[] bytes = zkClient.getData().forPath(path); Long seq = Long.valueOf(new String(bytes)); zkClient.setData().forPath(path, (seq + step + "").getBytes());} |
因为需要实时修改zookeeper的节点信息,可以考虑建立序列池,例如直接取走10000个序列,由各个服务内部自己去生成,具体实现主要依赖到CAS,通过compareAndSet去实现单机内部的序号递增,避免锁的滥用
来源:https://www.cnblogs.com/lijianming180/p/12014177.html