Skip to content

Commit e1e8450

Browse files
authored
🐜 Update random sequence resolve to use real random (#37)
* 🐜 Update random sequence resolve to use real random * fix spelling errors * Update readme documentation * 😠 update test coverage
1 parent 550e3af commit e1e8450

10 files changed

+188
-103
lines changed

README-zh_CN.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
<div>
22
<p align="center">
3-
<image src="https://www.pngkey.com/png/full/105-1052235_snowflake-png-transparent-background-snowflake-with-clear-background.png" width="250" height="250">
3+
<image src="https://www.pngkey.com/png/full/105-1052235_snowflake-png-transparent-background-snowflake-with-clear-background.png" width="250" height="250"></image>
44
</p>
55
<p align="center">An ID Generator for PHP based on Snowflake Algorithm (Twitter announced).</p>
66
<p align="center">
77
<a href="https://github.com/godruoyi/php-snowflake/actions/workflows/php.yml">
8-
<image src="https://github.com/godruoyi/php-snowflake/actions/workflows/php.yml/badge.svg" alt="build passed">
8+
<image src="https://github.com/godruoyi/php-snowflake/actions/workflows/php.yml/badge.svg" alt="build passed"></image>
99
</a>
1010
<a href="https://codecov.io/gh/godruoyi/php-snowflake">
1111
<img src="https://codecov.io/gh/godruoyi/php-snowflake/branch/master/graph/badge.svg?token=7AAOYCJK97"/>
1212
</a>
1313
<a href="https://github.com/godruoyi/php-snowflake">
14-
<image src="https://poser.pugx.org/godruoyi/php-snowflake/license" alt="License">
14+
<image src="https://poser.pugx.org/godruoyi/php-snowflake/license" alt="License"></image>
1515
</a>
1616
<a href="https://packagist.org/packages/godruoyi/php-snowflake">
17-
<image src="https://poser.pugx.org/godruoyi/php-snowflake/v/stable" alt="Packagist Version">
17+
<image src="https://poser.pugx.org/godruoyi/php-snowflake/v/stable" alt="Packagist Version"></image>
1818
</a>
1919
<a href="https://github.com/godruoyi/php-snowflake">
20-
<image src="https://poser.pugx.org/godruoyi/php-snowflake/downloads" alt="Total Downloads">
20+
<image src="https://poser.pugx.org/godruoyi/php-snowflake/downloads" alt="Total Downloads"></image>
2121
</a>
2222
</p>
2323
</div>
@@ -49,7 +49,8 @@ Snowflake 是 Twitter 内部的一个 ID 生算法,可以通过一些简单的
4949
* LaravelSequenceResolver(基于 redis psetex 和 incrby 生成)
5050
* SwooleSequenceResolver(基于 swoole_lock 锁)
5151

52-
不同的提供者只需要保证**同一毫秒生成的序列号不同**,就能得到唯一的 ID。
52+
> **Warning**
53+
> RandomSequenceResolver 序列号提供者在高并发情况下可能会导致生成的 ID 重复,如果你的应用场景中可能会出现高并发的情况,建议使用 RedisSequenceResolver 或者 LaravelSequenceResolver。
5354
5455
## 要求
5556

@@ -111,15 +112,14 @@ class AppServiceProvider extends ServiceProvider
111112
*/
112113
public function register()
113114
{
114-
$this->app->singleton('snowflake', function () {
115+
$this->app->singleton('snowflake', function ($app) {
115116
return (new Snowflake())
116-
->setStartTimeStamp(strtotime('2019-08-08')*1000)
117-
->setSequenceResolver(
118-
new LaravelSequenceResolver($this->app->get('cache')->store()
119-
));
117+
->setStartTimeStamp(strtotime('2019-10-10')*1000)
118+
->setSequenceResolver(new LaravelSequenceResolver($app->get('cache.store')));
120119
});
121120
}
122121
}
122+
}
123123
```
124124

125125
2. 自定义序列号解决器

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
<div>
22
<p align="center">
3-
<image src="https://www.pngkey.com/png/full/105-1052235_snowflake-png-transparent-background-snowflake-with-clear-background.png" width="250" height="250" />
3+
<image src="https://www.pngkey.com/png/full/105-1052235_snowflake-png-transparent-background-snowflake-with-clear-background.png" width="250" height="250"></image>
44
</p>
55
<p align="center">An ID Generator for PHP based on Snowflake Algorithm (Twitter announced).</p>
66
<p align="center">
77
<a href="https://github.com/godruoyi/php-snowflake/actions/workflows/php.yml">
8-
<image src="https://github.com/godruoyi/php-snowflake/actions/workflows/php.yml/badge.svg" alt="build passed" />
8+
<image src="https://github.com/godruoyi/php-snowflake/actions/workflows/php.yml/badge.svg" alt="build passed"></image>
99
</a>
1010
<a href="https://codecov.io/gh/godruoyi/php-snowflake">
11-
<img src="https://codecov.io/gh/godruoyi/php-snowflake/branch/master/graph/badge.svg?token=7AAOYCJK97"/>
11+
<img src="https://codecov.io/gh/godruoyi/php-snowflake/branch/master/graph/badge.svg?token=7AAOYCJK97" alt=""/>
1212
</a>
1313
<a href="https://github.com/godruoyi/php-snowflake">
14-
<image src="https://poser.pugx.org/godruoyi/php-snowflake/license" alt="License" />
14+
<image src="https://poser.pugx.org/godruoyi/php-snowflake/license" alt="License"></image>
1515
</a>
1616
<a href="https://packagist.org/packages/godruoyi/php-snowflake">
17-
<image src="https://poser.pugx.org/godruoyi/php-snowflake/v/stable" alt="Packagist Version" />
17+
<image src="https://poser.pugx.org/godruoyi/php-snowflake/v/stable" alt="Packagist Version"></image>
1818
</a>
1919
<a href="https://github.com/godruoyi/php-snowflake">
20-
<image src="https://poser.pugx.org/godruoyi/php-snowflake/downloads" alt="Total Downloads" />
20+
<image src="https://poser.pugx.org/godruoyi/php-snowflake/downloads" alt="Total Downloads"></image>
2121
</a>
2222
</p>
2323
</div>
@@ -40,7 +40,7 @@ Snowflake is a network service for generating unique ID numbers at high scale wi
4040
> You must know, The ID generated by the snowflake algorithm is not guaranteed to be unique.
4141
> For example, when two different requests enter the same node of the same data center at the same time, and the sequence generated by the node is the same, the generated ID will be duplicated.
4242
43-
So if you want use the snowflake algorithm to generate unique ID, You must ensure: The sequence-number generated in the same millisecond of the same node is unique.
43+
So if you want to use the snowflake algorithm to generate unique ID, You must ensure: The sequence-number generated in the same millisecond of the same node is unique.
4444
Based on this, we created this package and integrated multiple sequence-number providers into it.
4545

4646
* RandomSequenceResolver (Random)
@@ -55,7 +55,7 @@ Each provider only needs to ensure that the serial number generated in the same
5555
5656
## Requirement
5757

58-
1. PHP >= 7.0
58+
1. PHP >= 7.2
5959
2. **[Composer](https://getcomposer.org/)**
6060

6161
## Installation
@@ -87,7 +87,7 @@ $snowflake->id();
8787

8888
```php
8989
$snowflake = new \Godruoyi\Snowflake\Snowflake;
90-
$snowflake->setStartTimeStamp(strtotime('2019-09-09')*1000);
90+
$snowflake->setStartTimeStamp(strtotime('2019-09-09')*1000); // millisecond
9191

9292
$snowflake->id();
9393
```
@@ -113,10 +113,10 @@ class AppServiceProvider extends ServiceProvider
113113
*/
114114
public function register()
115115
{
116-
$this->app->singleton('snowflake', function () {
116+
$this->app->singleton('snowflake', function ($app) {
117117
return (new Snowflake())
118118
->setStartTimeStamp(strtotime('2019-10-10')*1000)
119-
->setSequenceResolver(new LaravelSequenceResolver($this->app->get('cache')->store()));
119+
->setSequenceResolver(new LaravelSequenceResolver($app->get('cache.store')));
120120
});
121121
}
122122
}
@@ -132,7 +132,7 @@ class YourSequence implements SequenceResolver
132132
/**
133133
* {@inheritdoc}
134134
*/
135-
public function sequence(int $currentTime)
135+
public function sequence(int $currentMillisecond)
136136
{
137137
// Just test.
138138
return mt_rand(0, 1);
@@ -149,17 +149,17 @@ And you can use closure:
149149

150150
```php
151151
$snowflake = new \Godruoyi\Snowflake\Snowflake;
152-
$snowflake->setSequenceResolver(function ($currentTime) {
152+
$snowflake->setSequenceResolver(function ($currentMillisecond) {
153153
static $lastTime;
154154
static $sequence;
155155

156-
if ($lastTime == $currentTime) {
156+
if ($lastTime == $currentMillisecond) {
157157
++$sequence;
158158
} else {
159159
$sequence = 0;
160160
}
161161

162-
$lastTime = $currentTime;
162+
$lastTime = $currentMillisecond;
163163

164164
return $sequence;
165165
})->id();

src/LaravelSequenceResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class LaravelSequenceResolver implements SequenceResolver
1717
/**
1818
* The laravel cache instance.
1919
*
20-
* @var \Illuminate\Contracts\Cache\Repository
20+
* @var Repository
2121
*/
2222
protected $cache;
2323

src/RandomSequenceResolver.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
class RandomSequenceResolver implements SequenceResolver
1414
{
1515
/**
16-
* The las ttimestamp.
16+
* The last timestamp.
1717
*
1818
* @var null
1919
*/
@@ -26,21 +26,36 @@ class RandomSequenceResolver implements SequenceResolver
2626
*/
2727
protected $sequence = 0;
2828

29+
/**
30+
* Max sequence number in one ms.
31+
*
32+
* @var int
33+
*/
34+
protected $maxSequence = Snowflake::MAX_SEQUENCE_SIZE;
35+
2936
/**
3037
* {@inheritdoc}
3138
*/
3239
public function sequence(int $currentTime)
3340
{
3441
if ($this->lastTimeStamp === $currentTime) {
35-
++$this->sequence;
42+
$this->sequence++;
3643
$this->lastTimeStamp = $currentTime;
3744

3845
return $this->sequence;
3946
}
4047

41-
$this->sequence = 0;
48+
$this->sequence = mt_rand(0, $this->maxSequence);
4249
$this->lastTimeStamp = $currentTime;
4350

4451
return 0;
4552
}
53+
54+
/**
55+
* @param int $maxSequence
56+
*/
57+
public function setMaxSequence(int $maxSequence): void
58+
{
59+
$this->maxSequence = $maxSequence;
60+
}
4661
}

src/RedisSequenceResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class RedisSequenceResolver implements SequenceResolver
1818
/**
1919
* The redis client instance.
2020
*
21-
* @var \Redis
21+
* @var Redis
2222
*/
2323
protected $redis;
2424

@@ -61,7 +61,7 @@ public function sequence(int $currentTime)
6161
}
6262

6363
/**
64-
* Set cacge prefix.
64+
* Set cache prefix.
6565
*/
6666
public function setCachePrefix(string $prefix)
6767
{

src/Snowflake.php

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
namespace Godruoyi\Snowflake;
1212

13+
use Exception;
14+
1315
class Snowflake
1416
{
1517
public const MAX_TIMESTAMP_LENGTH = 41;
@@ -20,6 +22,8 @@ class Snowflake
2022

2123
public const MAX_SEQUENCE_LENGTH = 12;
2224

25+
public const MAX_SEQUENCE_SIZE = (-1 ^ (-1 << self::MAX_SEQUENCE_LENGTH));
26+
2327
public const MAX_FIRST_LENGTH = 1;
2428

2529
/**
@@ -39,7 +43,7 @@ class Snowflake
3943
/**
4044
* The Sequence Resolver instance.
4145
*
42-
* @var null|\Godruoyi\Snowflake\SequenceResolver
46+
* @var null|SequenceResolver
4347
*/
4448
protected $sequence;
4549

@@ -53,15 +57,15 @@ class Snowflake
5357
/**
5458
* Default sequence resolver.
5559
*
56-
* @var null|\Godruoyi\Snowflake\SequenceResolver
60+
* @var null|SequenceResolver
5761
*/
5862
protected $defaultSequenceResolver;
5963

6064
/**
6165
* Build Snowflake Instance.
6266
*
63-
* @param int $datacenter
64-
* @param int $workerid
67+
* @param int|null $datacenter
68+
* @param int|null $workerid
6569
*/
6670
public function __construct(int $datacenter = null, int $workerid = null)
6771
{
@@ -127,22 +131,24 @@ public function getCurrentMicrotime()
127131

128132
/**
129133
* Set start time (millisecond).
134+
*
135+
* @throws Exception
130136
*/
131-
public function setStartTimeStamp(int $startTime)
137+
public function setStartTimeStamp(int $millisecond)
132138
{
133-
$missTime = $this->getCurrentMicrotime() - $startTime;
139+
$missTime = $this->getCurrentMicrotime() - $millisecond;
134140

135141
if ($missTime < 0) {
136-
throw new \Exception('The start time cannot be greater than the current time');
142+
throw new Exception('The start time cannot be greater than the current time');
137143
}
138144

139145
$maxTimeDiff = -1 ^ (-1 << self::MAX_TIMESTAMP_LENGTH);
140146

141147
if ($missTime > $maxTimeDiff) {
142-
throw new \Exception(sprintf('The current microtime - starttime is not allowed to exceed -1 ^ (-1 << %d), You can reset the start time to fix this', self::MAX_TIMESTAMP_LENGTH));
148+
throw new Exception(sprintf('The current microtime - starttime is not allowed to exceed -1 ^ (-1 << %d), You can reset the start time to fix this', self::MAX_TIMESTAMP_LENGTH));
143149
}
144150

145-
$this->startTime = $startTime;
151+
$this->startTime = $millisecond;
146152

147153
return $this;
148154
}
@@ -154,7 +160,7 @@ public function setStartTimeStamp(int $startTime)
154160
*/
155161
public function getStartTimeStamp()
156162
{
157-
if ($this->startTime > 0) {
163+
if (! is_null($this->startTime)) {
158164
return $this->startTime;
159165
}
160166

@@ -167,7 +173,7 @@ public function getStartTimeStamp()
167173
/**
168174
* Set Sequence Resolver.
169175
*
170-
* @param callable|SequenceResolver $sequence
176+
* @param callable|SequenceResolver $sequence
171177
*/
172178
public function setSequenceResolver($sequence)
173179
{
@@ -179,7 +185,7 @@ public function setSequenceResolver($sequence)
179185
/**
180186
* Get Sequence Resolver.
181187
*
182-
* @return null|callable|\Godruoyi\Snowflake\SequenceResolver
188+
* @return SequenceResolver|null
183189
*/
184190
public function getSequenceResolver()
185191
{
@@ -189,7 +195,7 @@ public function getSequenceResolver()
189195
/**
190196
* Get Default Sequence Resolver.
191197
*
192-
* @return \Godruoyi\Snowflake\SequenceResolver
198+
* @return SequenceResolver
193199
*/
194200
public function getDefaultSequenceResolver(): SequenceResolver
195201
{
@@ -199,10 +205,7 @@ public function getDefaultSequenceResolver(): SequenceResolver
199205
/**
200206
* Call resolver.
201207
*
202-
* @param callable|\Godruoyi\Snowflake\SequenceResolver $resolver
203-
* @param int $maxSequence
204-
* @param mixed $currentTime
205-
*
208+
* @param mixed $currentTime
206209
* @return int
207210
*/
208211
protected function callResolver($currentTime)
@@ -213,7 +216,7 @@ protected function callResolver($currentTime)
213216
return $resolver($currentTime);
214217
}
215218

216-
return is_null($resolver) || !($resolver instanceof SequenceResolver)
219+
return ! ($resolver instanceof SequenceResolver)
217220
? $this->getDefaultSequenceResolver()->sequence($currentTime)
218221
: $resolver->sequence($currentTime);
219222
}

0 commit comments

Comments
 (0)