教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 snowflake和snoyflake雪花算法以及golang实现

snowflake和snoyflake雪花算法以及golang实现

发布时间:2022-02-08   编辑:jiaochengji.com
教程集为您提供snowflake和snoyflake雪花算法以及golang实现等资源,欢迎您收藏本站,我们将为您提供最新的snowflake和snoyflake雪花算法以及golang实现资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg><h1>snowflake和snoyflake雪花算法学习与go实现</h1>

预备知识:

<blockquote>

Monotonic Clocks,即单调时间,所谓单调,就是只会不停的往前增长,不受校时操作的影响,这个时间是自进程启动以来的秒数

</blockquote>

参考文章:https://www.simpleapples.com/2018/10/26/understand-time-struct-in-go/

雪花算法是twitter开源的在分布式环境下生成的唯一id生成算法。

<h2>
1 推特雪花算法源码解读</h2>

推特雪花算法标准格式如下:

id 是64位整型的

<pre><code> -------------------------------------------------------------------------- | 1 Bit Unused | 41 Bit Timestamp | 10 Bit NodeID | 12 Bit Sequence ID | -------------------------------------------------------------------------- </code></pre> <ul><li>第一个bit未使用默认为0</li><li>41bit 存储毫秒级时间戳,时间单位为毫秒</li><li>10bit 存储节点的id 10个bit表示最多可以扩展1024个节点</li><li>12bit 自增id ,表示一个时间单位内,一个节点可生成的id最多为4096个</li></ul>

对于这样一个结构,一个机器,在1秒内 最多可以生成4096*1000约 400万个id。

<h3>
使用</h3> <pre><code class="lang-go hljs"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> <span class="token string">"github.com/bwmarrin/snowflake"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Create a new Node with a Node number of 1</span> node<span class="token punctuation">,</span> err <span class="token operator">:=</span> snowflake<span class="token punctuation">.</span><span class="token function">NewNode</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token comment">// Generate a snowflake ID.</span> id <span class="token operator">:=</span> node<span class="token punctuation">.</span><span class="token function">Generate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Print out the ID in a few different ways.</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Int64 ID: %d\n"</span><span class="token punctuation">,</span> id<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"String ID: %s\n"</span><span class="token punctuation">,</span> id<span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Base2 ID: %s\n"</span><span class="token punctuation">,</span> id<span class="token punctuation">.</span><span class="token function">Base2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Base64 ID: %s\n"</span><span class="token punctuation">,</span> id<span class="token punctuation">.</span><span class="token function">Base64</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// Print out the ID's timestamp</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"ID Time : %d\n"</span><span class="token punctuation">,</span> id<span class="token punctuation">.</span><span class="token function">Time</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// Print out the ID's node number</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"ID Node : %d\n"</span><span class="token punctuation">,</span> id<span class="token punctuation">.</span><span class="token function">Node</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// Print out the ID's sequence number</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"ID Step : %d\n"</span><span class="token punctuation">,</span> id<span class="token punctuation">.</span><span class="token function">Step</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> 输出: Int64 ID<span class="token punctuation">:</span> <span class="token number">1283328784765816832</span> String ID<span class="token punctuation">:</span> <span class="token number">1283328784765816832</span> Base2 ID<span class="token punctuation">:</span> <span class="token number">1000111001111010011001011101011111010000000000001000000000000</span> Base64 ID<span class="token punctuation">:</span> MTI4MzMyODc4NDc2NTgxNjgzMg<span class="token operator">==</span> ID Time <span class="token punctuation">:</span> <span class="token number">1594804400041</span> ID Node <span class="token punctuation">:</span> <span class="token number">1</span> ID Step <span class="token punctuation">:</span> <span class="token number">0</span> </code></pre>

生成的id 二进制格式为:

<code>0 00100011100111101001100101110101111101000 0000000001 000000000000</code>

<h3>
源码阅读:</h3>

<code>github.com/bwmarrin/snowflake</code>

<pre><code class="lang-go hljs"><span class="token keyword">var</span><span class="token punctuation">(</span> <span class="token comment">// Epoch is set to the twitter snowflake epoch of Nov 04 2010 01:42:54 UTC in milliseconds</span> <span class="token comment">// You may customize this to set a different epoch for your application.</span> Epoch <span class="token builtin">int64</span> <span class="token operator">=</span> <span class="token number">1288834974657</span> <span class="token comment">// 对应的是41bit的毫秒时间戳,</span> <span class="token comment">// NodeBits holds the number of bits to use for Node</span> <span class="token comment">// Remember, you have a total 22 bits to share between Node/Step</span> NodeBits <span class="token builtin">uint8</span> <span class="token operator">=</span> <span class="token number">10</span> <span class="token comment">// 节点id占用8个bit</span> <span class="token comment">// StepBits holds the number of bits to use for Step</span> <span class="token comment">// Remember, you have a total 22 bits to share between Node/Step</span> StepBits <span class="token builtin">uint8</span> <span class="token operator">=</span> <span class="token number">12</span> <span class="token comment">// 自增id占用12个bit</span> <span class="token punctuation">)</span> </code></pre>

Epoch;默认的是Nov 04 2010 01:42:54 UTC的毫秒时间戳,也可以自行调整

节点id和自增id总共占用22个bit,可以根据节点数自行调整

节点结构体定义:

<pre><code class="lang-go hljs"><span class="token keyword">type</span> Node <span class="token keyword">struct</span> <span class="token punctuation">{</span> mu sync<span class="token punctuation">.</span>Mutex epoch time<span class="token punctuation">.</span>Time time <span class="token builtin">int64</span> node <span class="token builtin">int64</span> step <span class="token builtin">int64</span> nodeMax <span class="token builtin">int64</span> <span class="token comment">// 节点的最大id值</span> nodeMask <span class="token builtin">int64</span> <span class="token comment">// 节点掩码</span> stepMask <span class="token builtin">int64</span> <span class="token comment">// 自增id掩码</span> timeShift <span class="token builtin">uint8</span> <span class="token comment">// 时间戳移位位数</span> nodeShift <span class="token builtin">uint8</span> <span class="token comment">// 节点移位位数</span> <span class="token punctuation">}</span> </code></pre>

生成节点函数:

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token function">NewNode</span><span class="token punctuation">(</span>node <span class="token builtin">int64</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>Node<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 输入的node值为节点id值。</span> <span class="token comment">// re-calc in case custom NodeBits or StepBits were set</span> <span class="token comment">// DEPRECATED: the below block will be removed in a future release.</span> mu<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> nodeMax <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">^</span> <span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span> <span class="token operator"><<</span> NodeBits<span class="token punctuation">)</span> nodeMask <span class="token operator">=</span> nodeMax <span class="token operator"><<</span> StepBits stepMask <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">^</span> <span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span> <span class="token operator"><<</span> StepBits<span class="token punctuation">)</span> timeShift <span class="token operator">=</span> NodeBits <span class="token operator"> </span> StepBits nodeShift <span class="token operator">=</span> StepBits mu<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> n <span class="token operator">:=</span> Node<span class="token punctuation">{</span><span class="token punctuation">}</span> n<span class="token punctuation">.</span>node <span class="token operator">=</span> node n<span class="token punctuation">.</span>nodeMax <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">^</span> <span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span> <span class="token operator"><<</span> NodeBits<span class="token punctuation">)</span><span class="token comment">//求节点id最大值,当notebits为10时,nodemax值位1023</span> n<span class="token punctuation">.</span>nodeMask <span class="token operator">=</span> n<span class="token punctuation">.</span>nodeMax <span class="token operator"><<</span> StepBits<span class="token comment">// 节点id掩码</span> n<span class="token punctuation">.</span>stepMask <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">^</span> <span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span> <span class="token operator"><<</span> StepBits<span class="token punctuation">)</span><span class="token comment">// 自增id掩码</span> n<span class="token punctuation">.</span>timeShift <span class="token operator">=</span> NodeBits <span class="token operator"> </span> StepBits<span class="token comment">//时间戳左移的位数</span> n<span class="token punctuation">.</span>nodeShift <span class="token operator">=</span> StepBits<span class="token comment">// 节点id左移的位数</span> <span class="token keyword">if</span> n<span class="token punctuation">.</span>node <span class="token operator"><</span> <span class="token number">0</span> <span class="token operator">||</span> n<span class="token punctuation">.</span>node <span class="token operator">></span> n<span class="token punctuation">.</span>nodeMax <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"Node number must be between 0 and "</span> <span class="token operator"> </span> strconv<span class="token punctuation">.</span><span class="token function">FormatInt</span><span class="token punctuation">(</span>n<span class="token punctuation">.</span>nodeMax<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> curTime <span class="token operator">=</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 这里n.epoch的值为默认epoch值,但单掉时间为一个负数,表示当前时间到默认事件的差值。</span> n<span class="token punctuation">.</span>epoch <span class="token operator">=</span> curTime<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span><span class="token function">Unix</span><span class="token punctuation">(</span>Epoch<span class="token operator">/</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>Epoch<span class="token operator">%</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token operator">*</span><span class="token number">1000000</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Sub</span><span class="token punctuation">(</span>curTime<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">&</span>n<span class="token punctuation">,</span> <span class="token boolean">nil</span> <span class="token punctuation">}</span> </code></pre>

节点生成id的方法:

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token punctuation">(</span>n <span class="token operator">*</span>Node<span class="token punctuation">)</span> <span class="token function">Generate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> ID <span class="token punctuation">{</span> n<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> now <span class="token operator">:=</span> time<span class="token punctuation">.</span><span class="token function">Since</span><span class="token punctuation">(</span>n<span class="token punctuation">.</span>epoch<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Nanoseconds</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000000</span> <span class="token comment">//求出当前时间,使用的是单调时间</span> <span class="token comment">// 如果在同一个时间单位内,就对自增id进行 1操作</span> <span class="token keyword">if</span> now <span class="token operator">==</span> n<span class="token punctuation">.</span>time <span class="token punctuation">{</span> n<span class="token punctuation">.</span>step <span class="token operator">=</span> <span class="token punctuation">(</span>n<span class="token punctuation">.</span>step <span class="token operator"> </span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">&</span> n<span class="token punctuation">.</span>stepMask <span class="token comment">// 当step达到最大值,再加1,就为0。即表示再这个时间单位内,不能再生成更多的id了,需要等待到下一个时间单位内。</span> <span class="token keyword">if</span> n<span class="token punctuation">.</span>step <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> now <span class="token operator"><=</span> n<span class="token punctuation">.</span>time <span class="token punctuation">{</span> now <span class="token operator">=</span> time<span class="token punctuation">.</span><span class="token function">Since</span><span class="token punctuation">(</span>n<span class="token punctuation">.</span>epoch<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Nanoseconds</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000000</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// 反之 自增id设为0</span> n<span class="token punctuation">.</span>step <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">}</span> <span class="token comment">// 将now值赋值给n.time</span> n<span class="token punctuation">.</span>time <span class="token operator">=</span> now <span class="token comment">// 合成id,将3部分移位并做或操作</span> r <span class="token operator">:=</span> <span class="token function">ID</span><span class="token punctuation">(</span><span class="token punctuation">(</span>now<span class="token punctuation">)</span><span class="token operator"><<</span>n<span class="token punctuation">.</span>timeShift <span class="token operator">|</span><span class="token punctuation">(</span>n<span class="token punctuation">.</span>node <span class="token operator"><<</span> n<span class="token punctuation">.</span>nodeShift<span class="token punctuation">)</span> <span class="token operator">|</span><span class="token punctuation">(</span>n<span class="token punctuation">.</span>step<span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">)</span> n<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">return</span> r <span class="token punctuation">}</span> </code></pre> <h3>
优缺点</h3>

原生的<code>Snowflake</code>算法是完全依赖于时间的,如果有时钟回拨的情况发生,会生成重复的ID,市场上的解决方案也是非常多的:

<ul><li>最简单的方案,就是关闭生成唯一ID机器的时间同步。</li><li>使用阿里云的的时间服务器进行同步,2017年1月1日的闰秒调整,阿里云服务器NTP系统24小时“消化”闰秒,完美解决了问题。</li><li>如果发现有时钟回拨,时间很短比如<code>5</code>毫秒,就等待,然后再生成。或者就直接报错,交给业务层去处理。</li><li>可以找2bit位作为时钟回拨位,发现有时钟回拨就将回拨位加1,达到最大位后再从0开始进行循环。</li></ul><h2>
2 索尼雪花算法</h2>

索尼雪花算法标准格式如下:

id 是64位整型的

<pre><code> -------------------------------------------------------------------------- | 1 Bit Unused | 39 Bit Timestamp | 8 Bit sequence number | 16 Bit machine id | -------------------------------------------------------------------------- </code></pre> <ul><li>第一个bit未使用默认为0</li><li>39bit 存储10毫秒级时间戳,时间单位为10毫秒</li><li>8bit 存储自增id,即一个时间单位内,一个节点可生成的id最多为521个</li><li>16bit 机器(或者线程)id,最多有2^16个机器或者线程</li></ul><h3>
使用</h3> <pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token function">getMachineID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token builtin">uint16</span><span class="token punctuation">,</span><span class="token builtin">error</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">uint16</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token boolean">nil</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">checkMachineID</span><span class="token punctuation">(</span>machineID <span class="token builtin">uint16</span><span class="token punctuation">)</span><span class="token builtin">bool</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> t<span class="token operator">:=</span>time<span class="token punctuation">.</span><span class="token function">Unix</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span> settings<span class="token operator">:=</span>sonyflake<span class="token punctuation">.</span>Settings<span class="token punctuation">{</span> StartTime<span class="token punctuation">:</span> t<span class="token punctuation">,</span> MachineID<span class="token punctuation">:</span> getMachineID<span class="token punctuation">,</span> CheckMachineID<span class="token punctuation">:</span> checkMachineID<span class="token punctuation">,</span> <span class="token punctuation">}</span> sf<span class="token operator">:=</span>sonyflake<span class="token punctuation">.</span><span class="token function">NewSonyflake</span><span class="token punctuation">(</span>settings<span class="token punctuation">)</span> id<span class="token punctuation">,</span><span class="token boolean">_</span><span class="token operator">:=</span>sf<span class="token punctuation">.</span><span class="token function">NextID</span><span class="token punctuation">(</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h3>源码解读</h3>

<code>"github.com/sony/sonyflake"</code>

<pre><code class="lang-go hljs"><span class="token keyword">type</span> Settings <span class="token keyword">struct</span> <span class="token punctuation">{</span> StartTime time<span class="token punctuation">.</span>Time MachineID <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">uint16</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> CheckMachineID <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token builtin">uint16</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">}</span> </code></pre>

在启动阶段,需要配置参数

<ul><li>starttime:起始时间,类似于snowflake的epoch,默认为2014-09-01 00:00:00 0000 UTC</li><li>machineID:可自定义当前的机器id(或者线程id),默认是本机ip的低16位</li><li>chenckmachineid:可自定义检查machineid是否合法或冲突的函数。默认不做验证。</li></ul><pre><code class="lang-go hljs"><span class="token keyword">type</span> Sonyflake <span class="token keyword">struct</span> <span class="token punctuation">{</span> mutex <span class="token operator">*</span>sync<span class="token punctuation">.</span>Mutex startTime <span class="token builtin">int64</span> elapsedTime <span class="token builtin">int64</span> <span class="token comment">// 上一次生成id的时间</span> sequence <span class="token builtin">uint16</span> <span class="token comment">// 自增id</span> machineID <span class="token builtin">uint16</span> <span class="token comment">// 机器id</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">NewSonyflake</span><span class="token punctuation">(</span>st Settings<span class="token punctuation">)</span> <span class="token operator">*</span>Sonyflake <span class="token punctuation">{</span> sf <span class="token operator">:=</span> <span class="token function">new</span><span class="token punctuation">(</span>Sonyflake<span class="token punctuation">)</span> sf<span class="token punctuation">.</span>mutex <span class="token operator">=</span> <span class="token function">new</span><span class="token punctuation">(</span>sync<span class="token punctuation">.</span>Mutex<span class="token punctuation">)</span> sf<span class="token punctuation">.</span>sequence <span class="token operator">=</span> <span class="token function">uint16</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token operator"><<</span>BitLenSequence <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token comment">// 11111111</span> <span class="token keyword">if</span> st<span class="token punctuation">.</span>StartTime<span class="token punctuation">.</span><span class="token function">After</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// starttime在当前时间之后,就返回空值</span> <span class="token keyword">return</span> <span class="token boolean">nil</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> st<span class="token punctuation">.</span>StartTime<span class="token punctuation">.</span><span class="token function">IsZero</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> sf<span class="token punctuation">.</span>startTime <span class="token operator">=</span> <span class="token function">toSonyflakeTime</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span><span class="token function">Date</span><span class="token punctuation">(</span><span class="token number">2014</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> time<span class="token punctuation">.</span>UTC<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> sf<span class="token punctuation">.</span>startTime <span class="token operator">=</span> <span class="token function">toSonyflakeTime</span><span class="token punctuation">(</span>st<span class="token punctuation">.</span>StartTime<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> err <span class="token builtin">error</span> <span class="token keyword">if</span> st<span class="token punctuation">.</span>MachineID <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> sf<span class="token punctuation">.</span>machineID<span class="token punctuation">,</span> err <span class="token operator">=</span> <span class="token function">lower16BitPrivateIP</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> sf<span class="token punctuation">.</span>machineID<span class="token punctuation">,</span> err <span class="token operator">=</span> st<span class="token punctuation">.</span><span class="token function">MachineID</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token operator">||</span> <span class="token punctuation">(</span>st<span class="token punctuation">.</span>CheckMachineID <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token operator">&&</span> <span class="token operator">!</span>st<span class="token punctuation">.</span><span class="token function">CheckMachineID</span><span class="token punctuation">(</span>sf<span class="token punctuation">.</span>machineID<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">nil</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> sf <span class="token punctuation">}</span> <span class="token comment">//返回 10ms为单位的时间戳。</span> <span class="token keyword">func</span> <span class="token function">toSonyflakeTime</span><span class="token punctuation">(</span>t time<span class="token punctuation">.</span>Time<span class="token punctuation">)</span> <span class="token builtin">int64</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> t<span class="token punctuation">.</span><span class="token function">UTC</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">UnixNano</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> sonyflakeTimeUnit <span class="token punctuation">}</span> </code></pre>

生成id源码:

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token punctuation">(</span>sf <span class="token operator">*</span>Sonyflake<span class="token punctuation">)</span> <span class="token function">NextID</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">uint64</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> maskSequence <span class="token operator">=</span> <span class="token function">uint16</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token operator"><<</span>BitLenSequence <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> sf<span class="token punctuation">.</span>mutex<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">defer</span> sf<span class="token punctuation">.</span>mutex<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> current <span class="token operator">:=</span> <span class="token function">currentElapsedTime</span><span class="token punctuation">(</span>sf<span class="token punctuation">.</span>startTime<span class="token punctuation">)</span><span class="token comment">//从起始时间到现在经过的时间</span> <span class="token keyword">if</span> sf<span class="token punctuation">.</span>elapsedTime <span class="token operator"><</span> current <span class="token punctuation">{</span><span class="token comment">//比上一次生成id时间大 则自增id变为0.</span> sf<span class="token punctuation">.</span>elapsedTime <span class="token operator">=</span> current sf<span class="token punctuation">.</span>sequence <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// sf.elapsedTime >= current</span> <span class="token comment">// 若相等,则还在同一个时间单位内,序列id 1</span> <span class="token comment">// 若此时大于, 经过的时间比上一次生成id时间小,说明发生了时间回拨,</span> sf<span class="token punctuation">.</span>sequence <span class="token operator">=</span> <span class="token punctuation">(</span>sf<span class="token punctuation">.</span>sequence <span class="token operator"> </span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">&</span> maskSequence <span class="token keyword">if</span> sf<span class="token punctuation">.</span>sequence <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span> sf<span class="token punctuation">.</span>elapsedTime<span class="token operator"> </span> <span class="token comment">// 貌似睡眠这段代码处理时间回拨问题,但是不明白为啥要在swquence=0处理</span> overtime <span class="token operator">:=</span> sf<span class="token punctuation">.</span>elapsedTime <span class="token operator">-</span> current time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token function">sleepTime</span><span class="token punctuation">(</span><span class="token punctuation">(</span>overtime<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> sf<span class="token punctuation">.</span><span class="token function">toID</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// 生成id的时候,用的不是curret,而是elapsedTime</span> <span class="token keyword">func</span> <span class="token punctuation">(</span>sf <span class="token operator">*</span>Sonyflake<span class="token punctuation">)</span> <span class="token function">toID</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">uint64</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> sf<span class="token punctuation">.</span>elapsedTime <span class="token operator">>=</span> <span class="token number">1</span><span class="token operator"><<</span>BitLenTime <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">,</span> errors<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token string">"over the time limit"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token function">uint64</span><span class="token punctuation">(</span>sf<span class="token punctuation">.</span>elapsedTime<span class="token punctuation">)</span><span class="token operator"><<</span><span class="token punctuation">(</span>BitLenSequence<span class="token operator"> </span>BitLenMachineID<span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token function">uint64</span><span class="token punctuation">(</span>sf<span class="token punctuation">.</span>sequence<span class="token punctuation">)</span><span class="token operator"><<</span>BitLenMachineID <span class="token operator">|</span> <span class="token function">uint64</span><span class="token punctuation">(</span>sf<span class="token punctuation">.</span>machineID<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token boolean">nil</span> <span class="token punctuation">}</span> </code></pre>

关于时间回拨问题:

<ul><li>只有当current大于elapsedTime,才会将current赋值给elapsedTime,也就是说elapsedTime是一直增大的,即使时钟回拨,也不会改变elapsedTime。</li><li>如果没有发生时间回拨,就是sf.elapsedTime = current,自增id满了以后,这个单位时间内不能再生成id了,就需要睡眠一下,等到下一个时间单位。</li><li>当发生时间回拨,sequence自增加1。当sequence加满,重新变为0后,为了防止重复id,将elapsedTime 1,这个时候elapsedTime还大于current,睡眠一会儿。</li></ul>

这个对处理时间回拨就是简单粗暴的睡眠等待。

到此这篇关于“snowflake和snoyflake雪花算法以及golang实现”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
PHP实现Snowflake生成分布式唯一ID
snowflake和snoyflake雪花算法以及golang实现
如何使用HTML5 canvas实现雪花飘落
PHP 实现 Snowflake 生成分布式唯一 ID
[golang]简单雪花算法_SnowFlake
想系统学习GO语言(Golang
用canvas实现简单的下雪效果(附代码)
让你的博客飘雪花超出屏幕依然看得见
圣诞节Merry Christmas给博客添加浪漫的下雪效果基于jquery实现
雪花算法golang实现

[关闭]
~ ~