雪花算法生成id
启动类
(关于后端id为Long类型造成的精度丢失问题,前端用bigNumber的类型,可以处理长整数,也可避免此问题)
/**
* 雪花算法.
*
* @param dataSource
* @param environment
* @return
*/
@Bean
public TwitterSnowflakeIdGenerator twitterSnowflakeIdGenerator(DataSource dataSource, Environment environment) {
return TwitterSnowflakeIdGenerator.createInstance(dataSource, environment.getProperty("spring.application.name"));
}
/**
* 转化long类型为String,处理前后端精度问题.
* @return
*/
@Bean("jackson2ObjectMapperBuilderCustomizer")
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
.serializerByType(Long.TYPE, ToStringSerializer.instance);
}
配置类
package com.mn.common.utils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 基于Twitter/snowflake 算法的分布式全局id生成器 64位ID (42(毫秒)+10位(统一分配的业务号workerIdBits)+12(重复累加))
*
* 整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分), 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
*
* @author xuwei
* @date 2021年9月27日
* @version 1.0
*/
public final class TwitterSnowflakeIdGenerator {
private final static long twepoch = 1288834974657L;
// 机器标识位数
private final static long workerIdBits = 10L;
// 机器ID最大值
private final static long maxWorkerId = ~(-1L << workerIdBits);
// 毫秒内自增位
private final static long sequenceBits = 12L;
// 机器ID偏左移12位
private final static long workerIdShift = sequenceBits;
// 时间毫秒左移22位
private final static long timestampLeftShift = sequenceBits + workerIdBits;
private final static long sequenceMask = ~(-1L << sequenceBits);
private long lastTimestamp = -1L;
private long sequence = 0L;
private final long workerId; // 此workId通过集中产生
private static TwitterSnowflakeIdGenerator generator = null;
/**
* @param ds 包含sys_set的数据源
* @param webDisplayName,web的上下文路径,即web.xml里面的displayName
* @return
*/
public static TwitterSnowflakeIdGenerator createInstance(DataSource ds, String webDisplayName) {
if (generator == null) {
synchronized (TwitterSnowflakeIdGenerator.class) {
if (generator == null) {
long wId = getWorkerId(ds, webDisplayName);
if (wId == -1L)
return null;
generator = new TwitterSnowflakeIdGenerator(wId);
}
}
}
return generator;
}
private static long getWorkerId(DataSource ds, String webDisplayName) {
long wId = -1L;
try {
// 获取主机名
String hostname = NetworkUtil.getFirstIp();
/*
* InitialContext initialContext = new InitialContext(); DataSource ds =
* (DataSource)initialContext.lookup(dbJndiName);
*/
JdbcTemplate t = new JdbcTemplate(ds);
// 主机名+web.xml里面的displayName,这个目前是唯一不变的,由它来决定唯一号,保存在SYS_SET表中
// 命名规则为 主机名_displayName_id
String key = hostname + "_" + webDisplayName + "_id";
List<Map<String, Object>> list = t.queryForList("select svalue from sys_set where skey = ?", key);
if (list.isEmpty()) {// 数据库没有这个记录
// 这两个地方需要事务,暂不加
List<Map<String, Object>> list2 = t.queryForList(
"select svalue from sys_set where status = 1 and class = ? order by sort", "ID_GENERATOR");
boolean found = false;
for (int i = 0; i < list2.size(); i++) {
Map<String, Object> map = list2.get(i);
if (map != null) {
int value = Integer.parseInt(map.get("svalue").toString());
if (value == i + 1) {
continue;
}
if (i < value) {// 找到一个小值,占用这个值
wId = i + 1;
t.update(
"insert into sys_set(skey,sname,svalue,sort,status,class,remark,time_update) "
+ "values(?,'id_generator',?,?,1,'ID_GENERATOR','',current_timestamp)",
key, wId, wId);// 插入数据库中
found = true;
break;
}
}
}
if (!found) {
wId = list2.size() + 1;
t.update(
"insert into sys_set(skey,sname,svalue,sort,status,class,remark,time_update) "
+ "values(?,'id_generator',?,?,1,'ID_GENERATOR','',current_timestamp)",
key, wId, wId);// 插入数据库中
}
} else {
wId = Long.parseLong(list.get(0).get("svalue").toString());
}
} catch (Exception e) {
// Auto-generated catch block
e.printStackTrace();
}
return wId;
}
/**
* @param workerId - 机器ID,范围[0,1024]
*/
private TwitterSnowflakeIdGenerator(long workerId) {
Assert.isTrue(workerId <= maxWorkerId && workerId >= 0, "worker Id can't be greater than %d or less than 0");
this.workerId = workerId;
}
public synchronized Long nextId() {
return getNextId();
}
public List<Long> nextIds(int batchSize) {
synchronized (TwitterSnowflakeIdGenerator.class) {
Assert.isTrue(batchSize > 1, "worker Id can't be less than 1");
List<Long> list = new ArrayList<>((int) (batchSize * 1.2));
for (int i = 0; i < batchSize; i++) {
list.add(getNextId());
}
return list;
}
}
private long getNextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
try {
throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp)
+ " milliseconds");
} catch (Exception e) {
e.printStackTrace();
}
}
if (lastTimestamp == timestamp) {
// 当前毫秒内,则+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 当前毫秒内计数满了,则等待下一秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
lastTimestamp = timestamp;
// ID偏移组合生成最终的ID,并返回ID
return ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
package com.mn.common.utils;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
public class NetworkUtil {
private NetworkUtil() {
}
public static boolean internalIp(byte[] addr) {
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0) {
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
return true;
}
case SECTION_5:
if(b1 == SECTION_6) {
return true;
}
// switch (b1) {
// case SECTION_6:
// return true;
// default:
// break;
// }
default:
return false;
}
}
// 获取第一个内网地址
public static String getFirstIp() {
String ipAddress = "";
try {
Enumeration<NetworkInterface> interfaces;
interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
Enumeration<InetAddress> addresss = ni.getInetAddresses();
while (addresss.hasMoreElements()) {
InetAddress nextElement = addresss.nextElement();
nextElement.isLoopbackAddress();
String hostAddress = nextElement.getHostAddress();
if (nextElement instanceof Inet4Address) { // 只关心 IPv4 地址
boolean isIn = internalIp(nextElement.getAddress());
if (isIn) {// 是内网地址
return hostAddress;
}
System.out.println("网卡接口地址:" + nextElement.getHostAddress());
System.out.println("网卡接口地址:" + isIn);
// 判断能否到达数据库,如果能到达,选这个
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ipAddress;
}
}