源码地址
UidGenerator是Java实现的,基于Snowflake算法的唯一ID生成器。
UidGenerator以组件形式工作在应用项目中,支持自定义workerId位数和初始化策略,从而适用于docker等虚拟化环境下实例自动重启、漂移等场景。
在实现上,UidGenerator通过借用未来时间来解决sequence天然存在的并发限制;采用RingBuffer来缓存已生成的UID,并行化UID的生产和消费,同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题,最终单机QPS可达600万。
注意事项:请在需要集成UidGenerator的项目进行导包操作,输入如下命令进行如下操作进行导包,注意修改jar包位置
mvn install:install-file -Dfile=/opt/uid-generator-1.0.0-SNAPSHOT.jar -DgroupId=com.generator -DartifactId=uid-generator -Dversion=1.0.0-SNAPSHOT -Dpackaging=jar
注意事项:由于UidGenerator默认集成了MyBatis和MyBatis Spring相关依赖,本项目使用的是MyBatis Plus,因此要把MyBatis和MyBatis Spring的依赖排除。
<dependency>
<groupId>com.generatorgroupId>
<artifactId>uid-generatorartifactId>
<version>1.0.0-SNAPSHOTversion>
<exclusions>
<exclusion>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
exclusion>
<exclusion>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>commons-langgroupId>
<artifactId>commons-langartifactId>
<version>2.6version>
dependency>
DROP TABLE IF EXISTS `worker_node`;
CREATE TABLE `worker_node` (
`work_node_id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`host_name` varchar(64) NOT NULL COMMENT '主机名称',
`port` varchar(64) NOT NULL COMMENT '端口',
`type` int NOT NULL COMMENT '类型(ACTUAL or CONTAINER)',
`launch_date` date NOT NULL COMMENT '年月日',
`create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
`update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '修改时间',
PRIMARY KEY (`work_node_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
@Data
@TableName("worker_node")
public class WorkNode {
@TableId(value = "work_node_id", type = IdType.AUTO)
private Long workNodeId;
/**
* 主机名称
*/
private String hostName;
/**
* 端口
*/
private String port;
/**
* 类型(ACTUAL or CONTAINER)
*/
private Integer type;
/**
* 年月日
*/
private Date launchDate;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改时间
*/
@TableField(update = "now()")
private Date updateTime;
}
@Repository
public interface WorkerNodeMapper extends BaseMapper<WorkNode> {
}
public interface WorkNodeService extends IService<WorkNodeDO> {
WorkNodeDO getWorkerNodeByHostPort(String host, String port);
void insertWorkerNode(WorkNodeDO workNode);
}
@Service
public class WorkNodeServiceImpl extends ServiceImpl<WorkerNodeMapper, WorkNodeDO> implements WorkNodeService {
@Override
public WorkNodeDO getWorkerNodeByHostPort(String host, String port) {
return null;
}
@Override
public void insertWorkerNode(WorkNodeDO workNode) {
}
}
public class DisposableWorkerIdAssigner implements WorkerIdAssigner {
@Autowired
private WorkNodeService workNodeService;
/**
* Assign worker id base on database.
* If there is host name & port in the environment, we considered that the node runs in Docker container
* Otherwise, the node runs on an actual machine.
*
* @return assigned worker id
*/
@Transactional(rollbackFor = Exception.class)
@Override
public long assignWorkerId() {
WorkNodeDO workNode = buildWorkerNode();
workNodeService.insertWorkerNode(workNode);
return workNode.getWorkNodeId();
}
/**
* Build worker node entity by IP and PORT
*/
private WorkNodeDO buildWorkerNode() {
WorkNodeDO workNode = new WorkNodeDO();
if (DockerUtils.isDocker()) {
workNode.setType(WorkerNodeType.CONTAINER.value());
workNode.setHostName(DockerUtils.getDockerHost());
workNode.setPort(DockerUtils.getDockerPort());
workNode.setLaunchDate(new Date());
} else {
workNode.setType(WorkerNodeType.ACTUAL.value());
workNode.setHostName(NetUtils.getLocalAddress());
workNode.setPort(System.currentTimeMillis() + "-" + new Random().nextInt(100000));
workNode.setLaunchDate(new Date());
}
return workNode;
}
}
@Configuration
public class IdGeneratorConfiguration {
@Bean
public DisposableWorkerIdAssigner disposableWorkerIdAssigner() {
return new DisposableWorkerIdAssigner();
}
@Bean
public CachedUidGenerator cachedUidGenerator() {
CachedUidGenerator cachedUidGenerator = new CachedUidGenerator();
cachedUidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner());
return cachedUidGenerator;
}
}
@RestController
@RequestMapping("/v1/test")
public class TestController {
@Autowired
private UidGenerator uidGenerator;
@GetMapping("/uid")
public Long index() {
return uidGenerator.getUID();
}
}