当前位置: 首页 > 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.jsqmd.com/news/111473/

相关文章:

  • 最小二乘问题详解9:使用Ceres求解非线性最小二乘
  • 第六十三篇
  • ArchLinux 开机自动打开 NumLock
  • P5782 [POI 2001] 和平委员会
  • 1. Markdown语法
  • 斯特林数杂谈
  • [Non] - 选举
  • 浅析Cursor Rules了解(工作原理、四种规则类型对比、文件结构、分层设计)及如何在项目中使用的最佳实践
  • 辛普森法则
  • GitCode克隆输入账号密码后报错
  • 《嘟嘟脸恶作剧》:说是捏脸,你还真捏脸啊?
  • [生存技能] 速冻包子热处理工艺优化研究:基于家庭厨房环境的操作规程
  • 英语_阅读_if Your computer broke down_待读
  • 《C语言电子书-2026最新版》-C语言数据类型概述
  • RadeGS——PCC损失
  • FFmpeg 关键的结构体
  • 优化:三数之和,最接近的三数之和
  • 自动化机器学习AutoML:TPOT工具从零到实战完整使用教程
  • 实用指南:GitHub 热榜项目 - 日榜(2025-11-20)
  • JAVA国际版同城跑腿源码快递代取帮买帮送同城服务源码支持Android+IOS+H5 - 实践
  • 第六十二篇
  • RadeGS——添加法向量损失
  • 12月18日总结 - 作业----
  • XSS(跨站脚本攻击)
  • 自考ScrumMaster-PSM:经验分享~
  • Python - dataclass
  • P1330 封锁阳光大学
  • 9 个降AI率工具,研究生必看!
  • [POI 2021/2022 R1] Domino 题解
  • 07_软考_程序设计语言