|
|
#!/usr/bin/env python
|
|
|
# -*- coding: utf-8 -*-
|
|
|
# @Time : 2023/4/9 14:54
|
|
|
# @Author : old tom
|
|
|
# @File : fu_id.py
|
|
|
# @Project : futool-tiny-datahub
|
|
|
# @Desc : ID 生成器
|
|
|
import time
|
|
|
import abc
|
|
|
from common.log_conf import Logger
|
|
|
|
|
|
logger = Logger().get_logger()
|
|
|
|
|
|
|
|
|
class IdGenerator(object):
|
|
|
"""
|
|
|
ID 生成器
|
|
|
"""
|
|
|
|
|
|
def __init__(self, id_type='snowflake'):
|
|
|
"""
|
|
|
:param id_type: id类型,默认雪花算法
|
|
|
"""
|
|
|
self.id_type = id_type
|
|
|
self.snowflake = SnowFlakeId(datacenter_id=1, worker_id=1)
|
|
|
self.uuid = UUID()
|
|
|
|
|
|
def get_id(self):
|
|
|
return {
|
|
|
'snowflake': self.snowflake,
|
|
|
'uuid': self.uuid
|
|
|
}[self.id_type].get_id()
|
|
|
|
|
|
|
|
|
class AbsIdGenerator(metaclass=abc.ABCMeta):
|
|
|
@abc.abstractmethod
|
|
|
def get_id(self):
|
|
|
pass
|
|
|
|
|
|
|
|
|
class SnowFlakeId(AbsIdGenerator):
|
|
|
"""
|
|
|
雪花ID生成
|
|
|
会生成一个64bit的整数,最终存到数据库就只占用8字节
|
|
|
1bit: 一般是符号位,代表正负数的所以这一位不做处理
|
|
|
41bit:这个部分用来记录时间戳,如果从1970-01-01 00:00:00来计算开始时间的话,它可以记录到2039年,足够我们用了,并且后续我们可以设置起始时间,这样就不用担心不够的问题, 这一个部分是保证我们生辰的id趋势递增的关键。
|
|
|
10bit:这是用来记录机器id的, 默认情况下这10bit会分成两部分前5bit代表数据中心,后5bit代表某个数据中心的机器id,默认情况下计算大概可以支持32*32 - 1= 1023台机器。
|
|
|
12bit:循环位,来对应1毫秒内产生的不同的id, 大概可以满足1毫秒并发生成2^12-1=4095次id的要求。
|
|
|
"""
|
|
|
# 64位ID的划分
|
|
|
WORKER_ID_BITS = 5
|
|
|
DATACENTER_ID_BITS = 5
|
|
|
SEQUENCE_BITS = 12
|
|
|
# 最大取值计算
|
|
|
MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS) # 2**5-1 0b11111
|
|
|
MAX_DATACENTER_ID = -1 ^ (-1 << DATACENTER_ID_BITS)
|
|
|
# 移位偏移计算
|
|
|
WOKER_ID_SHIFT = SEQUENCE_BITS
|
|
|
DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS
|
|
|
TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS
|
|
|
# 序号循环掩码
|
|
|
SEQUENCE_MASK = -1 ^ (-1 << SEQUENCE_BITS)
|
|
|
# Twitter元年时间戳
|
|
|
TWEPOCH = 1288834974657
|
|
|
|
|
|
def __init__(self, datacenter_id, worker_id, start_seq=0):
|
|
|
"""
|
|
|
:param datacenter_id: 数据中心ID
|
|
|
:param worker_id: 机器ID
|
|
|
:param start_seq: 起始序号
|
|
|
"""
|
|
|
# sanity check
|
|
|
if worker_id > self.MAX_WORKER_ID or worker_id < 0:
|
|
|
raise ValueError('worker_id值越界')
|
|
|
if datacenter_id > self.MAX_DATACENTER_ID or datacenter_id < 0:
|
|
|
raise ValueError('datacenter_id值越界')
|
|
|
self.worker_id = worker_id
|
|
|
self.datacenter_id = datacenter_id
|
|
|
self.sequence = start_seq
|
|
|
self.last_timestamp = -1 # 上次计算的时间戳
|
|
|
|
|
|
@staticmethod
|
|
|
def _gen_timestamp():
|
|
|
"""
|
|
|
生成整数时间戳
|
|
|
:return:int timestamp
|
|
|
"""
|
|
|
return int(time.time() * 1000)
|
|
|
|
|
|
def get_id(self):
|
|
|
"""
|
|
|
获取新ID
|
|
|
:return:
|
|
|
"""
|
|
|
timestamp = self._gen_timestamp()
|
|
|
# 时钟回拨
|
|
|
if timestamp < self.last_timestamp:
|
|
|
logger.error('clock is moving backwards. Rejecting requests until{}'.format(self.last_timestamp))
|
|
|
raise Exception
|
|
|
if timestamp == self.last_timestamp:
|
|
|
self.sequence = (self.sequence + 1) & self.SEQUENCE_MASK
|
|
|
if self.sequence == 0:
|
|
|
timestamp = self._til_next_millis(self.last_timestamp)
|
|
|
else:
|
|
|
self.sequence = 0
|
|
|
self.last_timestamp = timestamp
|
|
|
new_id = ((timestamp - self.TWEPOCH) << self.TIMESTAMP_LEFT_SHIFT) | (
|
|
|
self.datacenter_id << self.DATACENTER_ID_SHIFT) | \
|
|
|
(self.worker_id << self.WOKER_ID_SHIFT) | self.sequence
|
|
|
return new_id
|
|
|
|
|
|
def _til_next_millis(self, last_timestamp):
|
|
|
"""
|
|
|
等到下一毫秒
|
|
|
"""
|
|
|
timestamp = self._gen_timestamp()
|
|
|
while timestamp <= last_timestamp:
|
|
|
timestamp = self._gen_timestamp()
|
|
|
return timestamp
|
|
|
|
|
|
|
|
|
class UUID(AbsIdGenerator):
|
|
|
"""
|
|
|
UUID生成
|
|
|
"""
|
|
|
|
|
|
def __init__(self):
|
|
|
pass
|
|
|
|
|
|
def get_id(self):
|
|
|
pass
|
|
|
|
|
|
|
|
|
id_gen = IdGenerator()
|