这里使用OpenResty 实现一个支持高并发的雪花算法Id生成器。
需要安装的软件
(1)OpenResty
我使用的版本是:
[root@promote snowflake]# openresty -v
nginx version: openresty/1.17.8.2
(2)需要安装 lua 5.1版本的 socket 包和bit 包
lua 5.1 只支持32位,下面是lub 的bit库位移计算得到的结果。所以如果要在lua5.1环境下实现雪花算法只能借助其他的方式。
> print(bit.lshift(1,30))
1073741824
> print(bit.lshift(1,31))
-2147483648
> print(bit.lshift(1,32))
1
使用shell 脚本实现位移计算
文件名 id.sh
##((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence
let "value= (($1 - $2) << $3) | ($4 << $5) | ($6 << $7) | $8 "
echo ${value}
文件名:snowflake.lua
-- 测试代码
local _M = {}
socket = require "socket"
math = require "math"
bit = require "bit"
-- 开始时间截 (2015-01-01)
twepoch = 1420041600000;
-- 机器id所占的位数
workerIdBits = 5;
-- 数据标识id所占的位数
datacenterIdBits = 5;
-- 序列在id中占的位数
sequenceBits = 12;
-- 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
maxWorkerId = bit.lshift(1 , workerIdBits)-1;
-- 支持的最大数据标识id,结果是31
maxDatacenterId = bit.lshift(1 , datacenterIdBits)-1;
-- 机器ID向左移12位
workerIdShift = sequenceBits;
-- 数据标识id向左移17位(12+5)
datacenterIdShift = sequenceBits + workerIdBits;
-- 时间截向左移22位(5+5+12)
timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
-- 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
sequenceMask = bit.lshift(1 , sequenceBits) -1;
-- 工作机器ID(0~31)
workerId=1;
-- 数据中心ID(0~31)
datacenterId=1;
-- 毫秒内序列(0~4095)
sequence = 0;
-- 上次生成ID的时间截
lastTimestamp = -1;
function _M.SnowflakeIdWorker (workerIds, datacenterIds)
if (workerIds > maxWorkerId or workerIds < 0)
then
return -1
end
if (datacenterIds > maxDatacenterId or datacenterIds < 0)
then
return -1
end
workerId=workerIds
datacenterId=datacenterIds
end
local function tilNextMillis(lastTimestamp)
local timestamp = math.floor(socket.gettime() * 1000 );
while (timestamp <= lastTimestamp)
do
timestamp = math.floor(socket.gettime() * 1000 );
end
return timestamp;
end
function _M.nextId ()
local timestamp = math.floor(socket.gettime() * 1000 );
-- 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp)
then
return -1
end
-- 如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp)
then
sequence = bit.band((sequence + 1), sequenceMask);
-- 毫秒内序列溢出
if (sequence == 0)
then
--阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp)
end
else
sequence = 0
end
lastTimestamp = timestamp;
cmd =("sh ./id.sh " .. timestamp .. '\t' ..twepoch .. '\t'.. timestampLeftShift .. '\t'.. datacenterId .. '\t'.. datacenterIdShift .. '\t'.. workerId .. '\t'.. workerIdShift .. '\t'.. sequence)
id = os.execute(cmd)
return id
end
return _M
[root@promote snowflake]# lua
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> snowflake = require "snowflake" ;
> snowflake.SnowflakeIdWorker(1,2);
> print(snowflake.nextId())
754836761179590656
0
> print(snowflake.nextId())
754836766644768768
0
> print(snowflake.nextId())
754836769094242304
0
> print(snowflake.nextId())
754836771271086080
0
> print(snowflake.nextId())
754836773481484288
0
> print(snowflake.nextId())
754836775914180608
0
>
1、在oprenresty中引入新创建的包,首先创建目录 :/usr/local/openresty/lualib/resty/snowflake,然后并将 id.sh 和 snowflake.lua两个文件放到目录里。
2、Nginx 配置 。。。。待完善
3、简单测试 。。。。。待完善
待完善。。。。。。