分布式系统中,有一些须要全局惟一ID的场景,为了防止ID冲突,通常都会使用发号器
最简单的方式即是采用UUID,但UUID无序。
分布式id生成算法的有不少种,Twitter的SnowFlake就是其中经典的一种,而且生成的ID在总体上有序。
如,mysql集群能够经过自增步长解决集群中生成ID的惟一性及有序性,可是集群中增删mysql节点时,须要对全部mysql节点的自增步长进行调整。java
对于SnowFlake的原理介绍,能够参考文章 理解分布式id生成算法SnowFlakemysql
依然国际惯例,先上代码 SnowFlakeWithZKgit
SnowFlakeWithZK能够轻松搭建发号器集群,并经过Zookeeper管理workId,免去频繁修改集群节点配置的麻烦github
下载并解压 SnowFlakeWithZK-1.0.1.zip算法
进入解压目录并执行 ./SnowFlakeWithZK.jar start
spring
修改SnowFlakeWithZK.conf
中RUN_ARGS
参数,新增--zookeeper.enable=false
sql
修改SnowFlakeWithZK.conf
中RUN_ARGS
参数,新增--zookeeper.enable=true --zookeeper.url=[zookeeper-host]:[zookeeper-port]
segmentfault
SnowFlakeWithZK经过Zookeeper管理workId
修改SnowFlakeWithZK.conf
中RUN_ARGS
参数,新增--zookeeper.enable=false --machineId.workId=[You workId]
api
注:集群中每一个SnowFlake实例的workId须要保证各不相同
--server.port 服务端口
--machineId.dataCenterId 数据中心id,0~31,默认16
--machineId.workerId 实例id,0~31,默认5,--zookeeper.enable=false时生效,同一数据中心的不一样实例,须要保证各不相同
--zookeeper.enable 是否使用zookeeper管理workerId,默认true
--zookeeper.url zookeeper 链接地址,默认localhost:2181,--zookeeper.enable=true时生效springboot
项目采用springboot框架,经过@ConditionalOnProperty
注解控制是否使用zookeeper
当配置zookeeper.enable
为false
时,经过配置中的machineId.workId
来启动worker
/** * 单机配置SnowFlake的Machine ID * * 设置 zookeeper.enable = false */ @ConditionalOnProperty("zookeeper.enable", matchIfMissing = true, havingValue = "false") @Configuration class SingletonConfiguration { private val logger = LoggerFactory.getLogger(SingletonConfiguration::class.java) @Value("\${machineId.dataCenterId:16}") private var dataCenterId: Long = 16 @Value("\${machineId.workerId:0}") private var workerId: Long = 0 @Bean fun idWorker(): IdWorker { logger.info("Singleton Detected! Create IdWorker using SingletonConfiguration!") return IdWorker(workerId, dataCenterId) } }
当配置zookeeper.enable
为true
时,经过配置中的zookeeper.url
链接zk,并在zk中建立临时有序节点,经过节点的序号控制workId
/** * 使用zookeeper配置SnowFlake集群的Machine ID * * 设置 zookeeper.enable = true */ @ConditionalOnProperty("zookeeper.enable") @Configuration class ZKConfiguration { private val logger = LoggerFactory.getLogger(ZKConfiguration::class.java) @Value("\${zookeeper.url}") private lateinit var url: String @Value("\${machineId.datacenterId:16}") private var dataCenterId: Long = 16 @Bean @Primary fun idWorker(): IdWorker { logger.info("Zookeeper Detected! Create IdWorker using ZKConfiguration!") val client = CuratorFrameworkFactory.builder() .connectString(url) .sessionTimeoutMs(5000) .connectionTimeoutMs(5000) .retryPolicy(ExponentialBackoffRetry(1000, 3)) .build() client.start() val parent = "/snowflake/$dataCenterId" val worker = "$parent/worker" client.checkExists().forPath("/snowflake/$dataCenterId") ?: client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(parent) // 利用临时节点序列设置workerId val name = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(worker) val workerId = name.substring(worker.length).toLong() var idWorker = IdWorker(workerId, dataCenterId) // 重连监听 client.connectionStateListenable.addListener(ConnectionStateListener { _client: CuratorFramework, state: ConnectionState -> when (state) { ConnectionState.RECONNECTED -> { val name = _client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(worker) val workerId = name.substring(worker.length).toLong() idWorker.workerId = workerId logger.info("ZK ReConnected. workerId changed: $workerId") } ConnectionState.LOST, ConnectionState.SUSPENDED -> { logger.warn("ZK is Abnormal. State is $state") } else -> { logger.info("ZK State Changed: $state") } } }) return idWorker } }
您能够fork该项目,轻松的接入Spring Cloud、Dubbo等微服务框架中若是本工具备帮助到您,欢迎star