前言
分布式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配置給不同的應用實體指定機器碼即可,
二、雪花演算法
這里不講雪花演算法的原理了,相信大家隨意百度一下就能知道原理、其核心演算法是左移,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/350882.html
標籤:其他
上一篇:高性能處理器架構與編程實驗(基礎實驗1:構建實驗環境)
下一篇:Linux運維從入門到進階
