当前位置: 首页 > news >正文

详细介绍:雪花id改多workerID依赖redis

package com.meisoo.clearingplat.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** IdUtil.getSnowflake(SnowflakeConfig.staticWorkerId).nextId()* @author Hj* @date 2025/11/20*/
@Slf4j
@Configuration
public class SnowflakeConfig {public static Integer staticWorkerId;@Value("${spring.application.name}")private String appName;@Autowiredprivate RedisTemplate redisTemplate;private String WORKER_ID_KEY = ":snowflake:workerids";private String WORKER_ID_LOCK_KEY = ":snowflake:workerid:lock";private final long expireTTL = 20 * 60 * 1000; // 20分钟过期@PostConstructpublic void init() {this.staticWorkerId = generateDistributedWorkerId();}public int generateDistributedWorkerId() {Integer workerId = null;String appNameMap = appName + WORKER_ID_KEY;String lockKey = appName + WORKER_ID_LOCK_KEY;// 生成唯一的锁标识,防止误删其他线程的锁String lockValue = UUID.randomUUID().toString();try {// 尝试获取分布式锁boolean locked = tryLock(lockKey, lockValue);if (!locked) {// 如果获取锁失败,重试几次for (int i = 0; i < 1000; i++) {Thread.sleep(100); // 等待100ms后重试locked = tryLock(lockKey, lockValue);if (locked) break;}}if (!locked) {throw new RuntimeException("无法获取分布式锁,workerId分配失败");}// 获取锁成功,分配workerIdworkerId = allocateWorkerId(appNameMap);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException("workerId分配过程被中断", e);} finally {// 释放锁unlock(lockKey, lockValue);}if (workerId == null || workerId > 1023) {throw new RuntimeException("workerId分配失败,没有可用的workerId");}// 启动心跳更新任务startHeartbeatTask(appNameMap, workerId);return workerId;}/*** 尝试获取分布式锁*/private boolean tryLock(String lockKey, String lockValue) {Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue);return Boolean.TRUE.equals(success);}/*** 释放分布式锁*/private void unlock(String lockKey, String lockValue) {try {// 使用Lua脚本保证原子性,只有锁的值匹配时才删除String luaScript ="if redis.call('get', KEYS[1]) == ARGV[1] then " +"    return redis.call('del', KEYS[1]) " +"else " +"    return 0 " +"end";RedisScript script = new DefaultRedisScript<>(luaScript, Long.class);Long result = redisTemplate.execute(script, Collections.singletonList(lockKey), lockValue);if (result == null || result == 0) {log.warn("释放锁失败,锁可能已被其他线程释放或已过期");}} catch (Exception e) {log.error("释放锁时发生异常", e);}}/*** 分配workerId(在锁的保护下执行)*/private Integer allocateWorkerId(String appNameMap) {for (int workerId = 0; workerId <= 1023; workerId++) {if (!redisTemplate.opsForHash().hasKey(appNameMap, String.valueOf(workerId))) {// 找到可用的workerId,立即占用redisTemplate.opsForHash().put(appNameMap, String.valueOf(workerId), System.currentTimeMillis());log.info("成功分配workerId: {} for application: {}", workerId, appName);return workerId;}}return null;}/*** 启动心跳更新任务*/private void startHeartbeatTask(String appNameMap, Integer workerId) {ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();scheduledExecutorService.scheduleAtFixedRate(() -> {try {log.debug("定期更新workerId: {} 的心跳", workerId);// 更新自己的心跳redisTemplate.opsForHash().put(appNameMap, String.valueOf(workerId), System.currentTimeMillis());// 清理过期节点(这里也需要加锁,避免并发清理)cleanupExpiredWorkers(appNameMap);} catch (Exception e) {log.error("心跳更新任务执行异常", e);}}, 2, 10, TimeUnit.MINUTES); // 每10分钟执行一次,比过期时间短}/*** 清理过期的worker节点(需要加锁)*/private void cleanupExpiredWorkers(String appNameMap) {String lockKey = appName + WORKER_ID_LOCK_KEY + ":cleanup";String lockValue = UUID.randomUUID().toString();try {// 尝试获取清理锁,避免多个实例同时清理boolean locked = tryLock(lockKey, lockValue); // 10秒锁超时if (!locked) {return; // 如果没有获取到锁,跳过本次清理}long currentTime = System.currentTimeMillis();Set workerIds = redisTemplate.opsForHash().keys(appNameMap);for (Object workerIdObj : workerIds) {try {String workerIdStr = String.valueOf(workerIdObj);Long lastHeartbeat = (Long) redisTemplate.opsForHash().get(appNameMap, workerIdStr);if (lastHeartbeat != null && currentTime - lastHeartbeat > expireTTL) {log.info("清理过期的workerId: {}, 最后心跳时间: {}", workerIdStr, lastHeartbeat);redisTemplate.opsForHash().delete(appNameMap, workerIdStr);}} catch (Exception e) {log.warn("清理workerId: {} 时发生异常", workerIdObj, e);}}} finally {unlock(lockKey, lockValue);}}
}
http://www.gsyq.cn/news/123085.html

相关文章:

  • 斯特林数杂谈
  • 第六十三篇
  • 1. Markdown语法
  • 优化:三数之和,最接近的三数之和
  • FFmpeg 关键的结构体
  • [Non] - 选举
  • GitCode项目创建分支并提交代码
  • 修改 OBS-Studio 的字体
  • Linux上位机Windows上位机C++(QT)开发三菱上位机MC 1E 二进制通信 源码 C++快速实现三菱 MC 1E 二进制 支持三菱FX和A系列PLC A-1E 帧 国产化系统上位机
  • 1d 人工势场法路径规划Matlab代码实战
  • 图论
  • Python - dataclass
  • 云计算与边缘计算:未来数字化转型的关键驱动力 - 实践
  • 文献可视化分析期末复习与应用研究:基于知识图谱的核心概念与实践方法探讨
  • SIGSEGV段错误排查全攻略
  • AI元人文构想的理论构建过程与深层意义分析(二)
  • C++输入输出(cin和cout)的用法
  • 实用指南:LeetCode算法日记 - Day 107: 最长重复子数组
  • 【Agent】MemOS 源码笔记---(6)---MemScheduler -- 总体
  • 三菱PLC与组态王打造饮料自动装箱机控制系统
  • 【Nature Communications‘24‘06】预训练多模态大语言模型经过 SkinGPT-4 提升皮肤病学诊断能力
  • 品牌营销战略策划公司选哪家靠谱?奇正沐古 - 资讯焦点
  • 幻方的 “已知” 与 “未知”:三阶唯一解、多阶构造及未解之谜
  • 宪法守护童年:向霸凌和诈骗说“不” - 资讯焦点
  • Neo4j启动
  • 2025年郑州头部吊顶式空调机组设计多少钱,空气幕/表冷器/卧式暗装风机盘管/吊顶式空调机组/工业暖风机吊顶式空调机组采购找哪家 - 品牌推荐师
  • 2025年嘉兴排行前列的卧式暗装风机盘管采购多少钱,卡式风机盘管/吊顶式空调机组/空气幕/消防排烟防火阀卧式暗装风机盘管采购怎么选择 - 品牌推荐师
  • 无代码解决方案:解锁数字化转型的普惠路径
  • Oracle索引技术:理论与实操全解析
  • 深入理解Golang并发模型与CSP理论