在SpringBoot中使用雪花算法 生成分布式ID.
使用方式:
IdGenerator idGenerator = SpringContextHolder.getBean("idGenerator");
String transCodeId = idGenerator.nextId() + "";
Application.java
@Bean(name = "idGenerator")
public IdGeneratorFactoryBean idGeneratorFactoryBean() {
IdGeneratorFactoryBean bean = new IdGeneratorFactoryBean();
bean.setZkAddress(ResourceUtils.getProperty("zookeeper.servers"));
return bean;
}
IdGeneratorFactoryBean.java
package com.common.generator;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.apache.zookeeper.CreateMode.EPHEMERAL;
import static org.apache.zookeeper.Watcher.Event.KeeperState.SyncConnected;
import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;
public class IdGeneratorFactoryBean implements FactoryBean<IdGenerator>, InitializingBean, DisposableBean {
private static final Logger LOGGER = LoggerFactory.getLogger(IdGeneratorFactoryBean.class);
private ZooKeeper zooKeeper;
private String zkAddress;
private String root = "/idGenerator";
private IdGenerator idGenerator;
private int sessionTimeout = 3000;
private long connectTimeout = sessionTimeout;
@Override
public void afterPropertiesSet() throws Exception {
Objects.requireNonNull(zkAddress);
final CountDownLatch countDownLatch = new CountDownLatch(1);
zooKeeper = new ZooKeeper(zkAddress, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == SyncConnected) {
countDownLatch.countDown();
}
}
});
boolean await = countDownLatch.await(connectTimeout, TimeUnit.SECONDS);
if (await) {
LOGGER.info("connected zookeeper success ~");
} else {
throw new Exception("connected zookeeper failed ~");
}
Stat exists = zooKeeper.exists(root, false);
if (exists == null) {
zooKeeper.create(root, new byte[0], OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
long index = 0;
for (; ; ) {
String path = root + "/" + index;
exists = zooKeeper.exists(path, false);
if (exists == null) {
zooKeeper.create(path, new byte[0], OPEN_ACL_UNSAFE, EPHEMERAL);
break;
} else {
LOGGER.info(String.format("work id %s existed in zookeeper storage", index));
}
index = index + 1;
}
this.idGenerator = new IdGenerator(index % 1024 % 32, (index % 1024) / 32);
LOGGER.info(String.format("id generator created success at %s ,workid : %s datacenterid: %s ", this.idGenerator, index % 1024 % 32, (index % 1024) / 32));
}
@Override
public void destroy() throws Exception {
zooKeeper.close();
}
@Override
public IdGenerator getObject() throws Exception {
return idGenerator;
}
@Override
public Class<IdGenerator> getObjectType() {
return IdGenerator.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setZkAddress(String zkAddress) {
this.zkAddress = zkAddress;
}
public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public void setConnectTimeout(long connectTimeout) {
this.connectTimeout = connectTimeout;
}
}
IdGenerator.java
package com.common.generator;
/**
* Snowflake
*/
public class IdGenerator {
private final long twepoch = 1288834974657L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public IdGenerator(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
}