结合Redis在Spring架构体系中使用雪花算法
Posted 孙广明的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结合Redis在Spring架构体系中使用雪花算法相关的知识,希望对你有一定的参考价值。
前言
分布式ID生成策略有多种,各有利弊。这里记录下在工作中我结合Redis在Spring架构体系中使用雪花算法生成分布式ID的方式。
一、代码部分
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @author sungm
* @since 2021-11-06 21:16
*/
@Slf4j
@Service
public class SnowflakeManager {
/** 开始时间戳: 2020-01-01 00:00:00 */
private static final Long START_TIMESTAMP = 1577808000000L;
/** 12位最大序号: 2^12 - 1 */
private static final Long MAX_SEQ = ~(-1L << 12);
/** 10位最大机器码: 2^10 -1 */
private static final Long MAX_MACHINE = ~(-1L << 10);
/** 当前机器码 */
private Long machine;
/** 最后生成的序号 */
private Long lastSeq = 0L;
/** 最后一个序号生成的时间 */
private Long lastSqlTimestamp = 0L;
@Resource
private StringRedisTemplate stringRedisTemplate;
/** 定义雪花算法的 Key,把机器码存到Redis中 */
private static final String RECORD_SNOWFLAKE_MACHINE_SEQ_REDIS_KEY = "RECORD_SNOWFLAKE_MACHINE_SEQ";
private static final String RECORD_SNOWFLAKE_MACHINE_MAP_REDIS_KEY = "RECORD_SNOWFLAKE_MACHINE_MAP";
@PostConstruct
public void init() throws UnknownHostException {
//获取当前机器的IP地址
final String hostAddress = InetAddress.getLocalHost().getHostAddress();
//初始化Redis缓存
stringRedisTemplate.opsForValue().setIfAbsent(RECORD_SNOWFLAKE_MACHINE_SEQ_REDIS_KEY, "0");
stringRedisTemplate.opsForHash().putIfAbsent(RECORD_SNOWFLAKE_MACHINE_MAP_REDIS_KEY, "default", "0");
//不包含当前主机IP地址时,设置递增的值
if (!stringRedisTemplate.opsForHash().keys(RECORD_SNOWFLAKE_MACHINE_MAP_REDIS_KEY).contains(hostAddress)) {
stringRedisTemplate.opsForHash().put(RECORD_SNOWFLAKE_MACHINE_MAP_REDIS_KEY, hostAddress
, stringRedisTemplate.opsForValue().increment(RECORD_SNOWFLAKE_MACHINE_SEQ_REDIS_KEY, 1L).toString());
}
//获取当前主机对应的编码
machine = Long.parseLong((String) stringRedisTemplate.opsForHash().get(RECORD_SNOWFLAKE_MACHINE_MAP_REDIS_KEY, hostAddress));
log.info("主机:{},机器码:{}", hostAddress, machine);
//做个校验
if (machine > MAX_MACHINE) {
throw new RuntimeException("机器码已达到最大值" + MAX_MACHINE + ", 请排查无效数据!");
}
}
public synchronized Long nextId() {
//获取当前时间
Long now = System.currentTimeMillis();
if (lastSqlTimestamp.equals(now) && ++lastSeq > MAX_SEQ) {
throw new RuntimeException("同一毫秒内生成的序号达到" + MAX_SEQ + ", 请注意并发量!");
}
if (!lastSqlTimestamp.equals(now)) {
lastSeq = 0L;
}
lastSqlTimestamp = now;
/* 0 - 41位时间戳 - 10位机器码 - 12位序列*/
return ((now - START_TIMESTAMP) << 22) | machine << 12 | lastSeq;
}
}
说明:同一应用多个实例部署在不同宿主机上面,利用了宿主机的IP地址不重复的特性,结合Redis单线程的特点来给不同的应用实例生成不同的机器码。如果可以指定每个实例都机器码,则可以简化这一操作,直接通过application.yml配置给不同的应用实例指定机器码即可。
二、雪花算法
这里不讲雪花算法的原理了,相信大家随意百度一下就能知道原理、其核心算法是左移。
以上是关于结合Redis在Spring架构体系中使用雪花算法的主要内容,如果未能解决你的问题,请参考以下文章