集群环境下为雪花算法生成全局唯一机器ID策略

xiaoxiao2025-10-07  5

雪花算法是生成数据id非常好的一种方式,机器id是雪花算法不可分割的一部分。但是对于集群应用,让不同的机器自动产生不同的机器id传统做法就是针对每一个机器进行单独配置,但这样做不利于集群水平扩展,且操作过程非常复杂,所以每一个机器在集群环境下是一个头疼的问题。现在借助spring+redis,给出一种策略,支持随意水平扩展,肥肠好用。 大致策略分为4步: 1.对机器ip进行hash,对某一个(大于你的机器的个数)数取模,作为初始的机器ID。 2.将机器id存储到redis中(注册)。 3.以此类推,每一个机器初始化的时候都向redis注册一个数,若这个数注册过了,则+1重新注册。 4.在spring容器销毁时从redis中将注册信息删除。 下面给出具体实现代码:

@Service("machineIdUtil") public class MachineIdUtil { //日誌 private static Logger logger = LogUtil.getLogger(MachineIdUtil.class); /** *redis 实例 */ @Autowired private Cluster jimClient; /** * 机器id */ public static Integer machine_id; /** * 本地ip地址 */ private static String localIp; private static TimeUnit timeUnit = TimeUnit.DAYS; /** * hash机器IP初始化一个机器ID */ @PostConstruct public void initMachineId() throws Exception { localIp = IpUtil.getInet4Address(); Long ip_ = Long.parseLong(localIp.replaceAll("\\.", "")); //这里取128,为后续机器Ip调整做准备。 machine_id = ip_.hashCode()% 128; //创建一个机器ID createMachineId(); logger.info("初始化 machine_id :{}", machine_id); SnowFlakeGenerator.initMachineId(machine_id); } /** * 容器销毁前清除注册记录 */ @PreDestroy public void destroyMachineId() { jimClient.del(RedisConstant.OPLOG_MACHINE_ID_kEY + machine_id); } /** * 主方法:获取一个机器id * * @return */ public Integer createMachineId() { try { //向redis注册,并设置超时时间 Boolean aBoolean = registMachine(machine_id); //注册成功 if (aBoolean) { //启动一个线程更新超时时间 updateExpTimeThread(); //返回机器Id return machine_id; } //检查是否被注册满了.不能注册,就直接返回 if (!checkIfCanRegist()) { //注册满了,加一个报警 Profiler.businessAlarm("medicine-oplog-createMachineId", System.currentTimeMillis(), "128个机器码已经注册满!"); return machine_id; } logger.info("createMachineId->ip:{},machineId:{}, time:{}", localIp, machine_id, DateUtil.getDate()); //递归调用 createMachineId(); } catch (Exception e) { getRandomMachineId(); return machine_id; } getRandomMachineId(); return machine_id; } /** * 检查是否被注册满了 * * @return */ private Boolean checkIfCanRegist() { Boolean flag = true; //判断0~127这个区间段的机器IP是否被占满 for (int i = 0; i <= 127; i++) { flag = jimClient.exists(RedisConstant.OPLOG_MACHINE_ID_kEY + i); //如果不存在。说明还可以继续注册。直接返回i if (!flag) { machine_id = i; break; } } return !flag; } /** * 1.更新超時時間 * 注意,更新前检查是否存在机器ip占用情况 */ private void updateExpTimeThread() { //开启一个线程执行定时任务: //1.每23小时更新一次超时时间 new Timer(localIp).schedule(new TimerTask() { @Override public void run() { //检查缓存中的ip与本机ip是否一致,一致則更新時間,不一致則重新取一個机器ID Boolean b = checkIsLocalIp(String.valueOf(machine_id)); if (b) { logger.info("更新超时时间 ip:{},machineId:{}, time:{}", localIp, machine_id, DateUtil.getDate("yyyy-MM-dd HH:mm:ss")); jimClient.expire(RedisConstant.OPLOG_MACHINE_ID_kEY + machine_id, 1, timeUnit); } else { logger.info("重新生成机器ID ip:{},machineId:{}, time:{}", localIp, machine_id, DateUtil.getDate("yyyy-MM-dd HH:mm:ss")); //重新生成机器ID,并且更改雪花中的机器ID getRandomMachineId(); //重新生成并注册机器id createMachineId(); //更改雪花中的机器ID SnowFlakeGenerator.initMachineId(machine_id); //結束當前任務 logger.info("Timer->thread->name:{}", Thread.currentThread().getName()); this.cancel(); } } }, 10 * 1000, 1000 * 60 * 60 * 23); } /** * 获取1~127随机数 */ public void getRandomMachineId() { machine_id = (int) (Math.random() * 127); } /** * 机器ID顺序获取 */ public void incMachineId() { if (machine_id >= 127) { machine_id = 0; } else { machine_id += 1; } } /** * @param mechineId * @return */ private Boolean checkIsLocalIp(String mechineId) { String ip = jimClient.get(RedisConstant.OPLOG_MACHINE_ID_kEY + mechineId); logger.info("checkIsLocalIp->ip:{}", ip); return localIp.equals(ip); } /** * 1.注册机器 * 2.设置超时时间 * * @param mechineId 取值为0~127 * @return */ private Boolean registMachine(Integer mechineId) throws Exception { return jimClient.set(RedisConstant.OPLOG_MACHINE_ID_kEY + mechineId, localIp, 1, TimeUnit.DAYS, false); } }
转载请注明原文地址: https://www.6miu.com/read-5037518.html

最新回复(0)