分布式ID生成器
分布式ID生成器分布式ID生成器-IdWorker具体使用方式启动类业务使用IdWorker
分布式ID生成器
由于我们的数据库在生产环境中要分片部署(MyCat),所以我们不能使用数据库本身的自增功能来产生主键值,只能由程序来生成唯一的主键值。
我们采用的是开源的twitter( 非官方中文惯称:推特.是国外的一个网站,是一个社交网络及微博客服务) 的 snowflake (雪花)算法。
默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1024台机器,序列号支持1毫秒产生4096个自增序列id . SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右
分布式ID生成器-IdWorker
package util
;
import java
.lang
.management
.ManagementFactory
;
import java
.net
.InetAddress
;
import java
.net
.NetworkInterface
;
public class IdWorker {
private final static long twepoch
= 1288834974657L
;
private final static long workerIdBits
= 5L
;
private final static long datacenterIdBits
= 5L
;
private final static long maxWorkerId
= -1L
^ (-1L
<< workerIdBits
);
private final static long maxDatacenterId
= -1L
^ (-1L
<< datacenterIdBits
);
private final static long sequenceBits
= 12L
;
private final static long workerIdShift
= sequenceBits
;
private final static long datacenterIdShift
= sequenceBits
+ workerIdBits
;
private final static long timestampLeftShift
= sequenceBits
+ workerIdBits
+ datacenterIdBits
;
private final static long sequenceMask
= -1L
^ (-1L
<< sequenceBits
);
private static long lastTimestamp
= -1L
;
private long sequence
= 0L
;
private final long workerId
;
private final long datacenterId
;
public IdWorker(){
this.datacenterId
= getDatacenterId(maxDatacenterId
);
this.workerId
= getMaxWorkerId(datacenterId
, maxWorkerId
);
}
public IdWorker(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
;
long nextId
= ((timestamp
- twepoch
) << timestampLeftShift
)
| (datacenterId
<< datacenterIdShift
)
| (workerId
<< workerIdShift
) | sequence
;
return nextId
;
}
private long tilNextMillis(final long lastTimestamp
) {
long timestamp
= this.timeGen();
while (timestamp
<= lastTimestamp
) {
timestamp
= this.timeGen();
}
return timestamp
;
}
private long timeGen() {
return System
.currentTimeMillis();
}
protected static long getMaxWorkerId(long datacenterId
, long maxWorkerId
) {
StringBuffer mpid
= new StringBuffer();
mpid
.append(datacenterId
);
String name
= ManagementFactory
.getRuntimeMXBean().getName();
if (!name
.isEmpty()) {
mpid
.append(name
.split("@")[0]);
}
return (mpid
.toString().hashCode() & 0xffff) % (maxWorkerId
+ 1);
}
protected static long getDatacenterId(long maxDatacenterId
) {
long id
= 0L
;
try {
InetAddress ip
= InetAddress
.getLocalHost();
NetworkInterface network
= NetworkInterface
.getByInetAddress(ip
);
if (network
== null
) {
id
= 1L
;
} else {
byte[] mac
= network
.getHardwareAddress();
id
= ((0x000000FF & (long) mac
[mac
.length
- 1])
| (0x0000FF00 & (((long) mac
[mac
.length
- 2]) << 8))) >> 6;
id
= id
% (maxDatacenterId
+ 1);
}
} catch (Exception e
) {
System
.out
.println(" getDatacenterId: " + e
.getMessage());
}
return id
;
}
}
具体使用方式
启动类
在启动类中增加IdWorker @Bean;
package com
.tensquare
.base
;
import org
.springframework
.boot
.SpringApplication
;
import org
.springframework
.boot
.autoconfigure
.SpringBootApplication
;
import org
.springframework
.cloud
.netflix
.eureka
.EnableEurekaClient
;
import org
.springframework
.context
.annotation
.Bean
;
import util
.IdWorker
;
@SpringBootApplication
@EnableEurekaClient
public class BaseApplication {
public static void main(String
[] args
) {
SpringApplication
.run(BaseApplication
.class, args
);
}
@Bean
public IdWorker
idWorker(){
return new IdWorker(1, 1);
}
}
业务使用IdWorker
@Autowired
private IdWorker idWorker;
public void save(Label label) {
label.setId(idWorker.nextId()+"");
labelDao.save(label);
}