介绍
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.generator</groupId>
<artifactId>uid-generator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</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) {
}
}
创建UidGenerator配置类
public class DisposableWorkerIdAssigner implements WorkerIdAssigner {
@Autowired
private WorkNodeService workNodeService;
/**
* Assign worker id base on database.<p>
* If there is host name & port in the environment, we considered that the node runs in Docker container<br>
* 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();
}
}