寫在最前:
實現一個簡單的發號器。即根據 SnowFlake 算法的原理實現一個簡單的發號器,產生不重複、自增的 ID。
1、什麼是 SnowFlake 算法❓
2、Python3 實現
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
'''=================================================================================
@Project -> File :python_imggen -> snowflake_id.py
@IDE :PyCharm
@Author :Mr. Wufei
@Date :2020/1/19 11:23
@Desc :通過雪花算法(SnowFlake)用 Python3 實現一個簡單的發號器
@CSDN :什麼是 SnowFlake(https://showufei.blog.csdn.net/article/details/104041576)
=================================================================================='''
import sys
import time
import logging
class MySnow(object):
def __init__(self, datacenter_id, worker_id):
# 初始毫秒級時間戳(2014-08-22)
self.initial_time_stamp = int(time.mktime(time.strptime('2014-08-22 00:00:00', "%Y-%m-%d %H:%M:%S")) * 1000)
# 機器 ID 所佔的位數
self.worker_id_bits = 5
# 數據表示 ID 所佔的位數
self.datacenter_id_bits = 5
# 支持的最大機器 ID,結果是 31(這個位移算法可以很快的計算出幾位二進制數所能表示的最大十進制數)
# 2**5-1 0b11111
self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
# 支持最大標識 ID,結果是 31
self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
# 序列號 ID所佔的位數
self.sequence_bits = 12
# 機器 ID 偏移量(12)
self.workerid_offset = self.sequence_bits
# 數據中心 ID 偏移量(12 + 5)
self.datacenterid_offset = self.sequence_bits + self.datacenter_id_bits
# 時間戳偏移量(12 + 5 + 5)
self.timestamp_offset = self.sequence_bits + self.datacenter_id_bits + self.worker_id_bits
# 生成序列的掩碼,這裏爲 4095(0b111111111111 = 0xfff = 4095)
self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
# 初始化日誌
self.logger = logging.getLogger('snowflake')
# 數據中心 ID(0 ~ 31)
if datacenter_id > self.max_datacenter_id or datacenter_id < 0:
err_msg = 'datacenter_id 不能大於 %d 或小於 0' % self.max_worker_id
self.logger.error(err_msg)
sys.exit()
self.datacenter_id = datacenter_id
# 工作節點 ID(0 ~ 31)
if worker_id > self.max_worker_id or worker_id < 0:
err_msg = 'worker_id 不能大於 %d 或小於 0' % self.max_worker_id
self.logger.error(err_msg)
sys.exit()
self.worker_id = worker_id
# 毫秒內序列(0 ~ 4095)
self.sequence = 0
# 上次生成 ID 的時間戳
self.last_timestamp = -1
def _gen_timestamp(self):
"""
生成整數毫秒級時間戳
:return: 整數毫秒級時間戳
"""
return int(time.time() * 1000)
def next_id(self):
"""
獲得下一個ID (用同步鎖保證線程安全)
:return: snowflake_id
"""
timestamp = self._gen_timestamp()
# 如果當前時間小於上一次 ID 生成的時間戳,說明系統時鐘回退過這個時候應當拋出異常
if timestamp < self.last_timestamp:
self.logger.error('clock is moving backwards. Rejecting requests until {}'.format(self.last_timestamp))
# 如果是同一時間生成的,則進行毫秒內序列
if timestamp == self.last_timestamp:
self.sequence = (self.sequence + 1) & self.sequence_mask
# sequence 等於 0 說明毫秒內序列已經增長到最大值
if self.sequence == 0:
# 阻塞到下一個毫秒,獲得新的時間戳
timestamp = self._til_next_millis(self.last_timestamp)
else:
# 時間戳改變,毫秒內序列重置
self.sequence = 0
# 上次生成 ID 的時間戳
self.last_timestamp = timestamp
# 移位並通過或運算拼到一起組成 64 位的 ID
new_id = ((timestamp - self.initial_time_stamp) << self.timestamp_offset) | \
(self.datacenter_id << self.datacenterid_offset) | \
(self.worker_id << self.workerid_offset) | \
self.sequence
return new_id
def _til_next_millis(self, last_timestamp):
"""
阻塞到下一個毫秒,直到獲得新的時間戳
:param last_timestamp: 上次生成 ID 的毫秒級時間戳
:return: 當前毫秒級時間戳
"""
timestamp = self._gen_timestamp()
while timestamp <= last_timestamp:
timestamp = self._gen_timestamp()
return timestamp
"""
if __name__ == '__main__':
mysnow = MySnow(1, 2)
id = mysnow.next_id()
print(id)
"""
其中:
- datacenter_id 爲數據中心 ID(0 ~ 31)
- worker_id 爲工作節點 ID(0 ~ 31)
結果展示:
716603571604430848 |