diff --git a/example/Snowflake/Snowflake.go b/example/Snowflake/Snowflake.go new file mode 100644 index 0000000..f7ce300 --- /dev/null +++ b/example/Snowflake/Snowflake.go @@ -0,0 +1,96 @@ +/* + * @Author : huangzj + * @Time : 2020/12/16 9:32 + * @Description: + */ + +package Snowflake + +import ( + "errors" + "sync" + "time" +) + +const ( + workerBits uint8 = 10 + numberBits uint8 = 12 + workerMax int64 = -1 ^ (-1 << workerBits) + numberMax int64 = -1 ^ (-1 << numberBits) + timeShift uint8 = workerBits + numberBits + workerShift uint8 = numberBits + startTime int64 = 1525705533000 // 如果在程序跑了一段时间修改了epoch这个值 可能会导致生成相同的ID +) + +const ( + workerIDBits = uint64(5) // 10bit 工作机器ID中的 5bit workerID + dataCenterIDBits = uint64(5) // 10 bit 工作机器ID中的 5bit dataCenterID + sequenceBits = uint64(12) + + maxWorkerID = int64(-1) ^ (int64(-1) << workerIDBits) //节点ID的最大值 用于防止溢出 + maxDataCenterID = int64(-1) ^ (int64(-1) << dataCenterIDBits) + maxSequence = int64(-1) ^ (int64(-1) << sequenceBits) + + timeLeft = uint8(22) // timeLeft = workerIDBits + sequenceBits // 时间戳向左偏移量 + dataLeft = uint8(17) // dataLeft = dataCenterIDBits + sequenceBits + workLeft = uint8(12) // workLeft = sequenceBits // 节点IDx向左偏移量 + // 2020-05-20 08:00:00 +0800 CST + twepoch = int64(1589923200000) // 常量时间戳(毫秒) +) + +type Worker struct { + mu sync.Mutex + LastStamp int64 // 记录上一次ID的时间戳 + WorkerID int64 // 该节点的ID + DataCenterID int64 // 该节点的 数据中心ID + Sequence int64 // 当前毫秒已经生成的ID序列号(从0 开始累加) 1毫秒内最多生成4096个ID +} + +//分布式情况下,我们应通过外部配置文件或其他方式为每台机器分配独立的id +func NewWorker(workerID, dataCenterID int64) *Worker { + return &Worker{ + WorkerID: workerID, + LastStamp: 0, + Sequence: 0, + DataCenterID: dataCenterID, + } +} + +func (w *Worker) getMilliSeconds() int64 { + return time.Now().UnixNano() / 1e6 +} + +func (w *Worker) NextID() (uint64, error) { + w.mu.Lock() + defer w.mu.Unlock() + + return w.nextID() +} + +func (w *Worker) nextID() (uint64, error) { + timeStamp := w.getMilliSeconds() + if timeStamp < w.LastStamp { + return 0, errors.New("time is moving backwards,waiting until") + } + + if w.LastStamp == timeStamp { + + w.Sequence = (w.Sequence + 1) & maxSequence + + if w.Sequence == 0 { + for timeStamp <= w.LastStamp { + timeStamp = w.getMilliSeconds() + } + } + } else { + w.Sequence = 0 + } + + w.LastStamp = timeStamp + id := ((timeStamp - twepoch) << timeLeft) | + (w.DataCenterID << dataLeft) | + (w.WorkerID << workLeft) | + w.Sequence + + return uint64(id), nil +} diff --git a/example/Snowflake/Snowflake_test.go b/example/Snowflake/Snowflake_test.go new file mode 100644 index 0000000..0914311 --- /dev/null +++ b/example/Snowflake/Snowflake_test.go @@ -0,0 +1,49 @@ +/* + * @Author : huangzj + * @Time : 2020/12/16 9:32 + * @Description: + */ + +package Snowflake + +import ( + "fmt" + "sync" + "testing" +) + +var wg sync.WaitGroup + +func TestSnowflake(t *testing.T) { + w := NewWorker(5, 5) + + ch := make(chan uint64, 10000) + count := 10000 + wg.Add(count) + defer close(ch) + //并发 count个goroutine 进行 snowFlake ID 生成 + for i := 0; i < count; i++ { + go func() { + defer wg.Done() + id, _ := w.NextID() + ch <- id + }() + } + wg.Wait() + m := make(map[uint64]int) + for i := 0; i < count; i++ { + id := <-ch + // 如果 map 中存在为 id 的 key, 说明生成的 snowflake ID 有重复 + _, ok := m[id] + if ok { + fmt.Printf("repeat id %d\n", id) + return + } else { + fmt.Printf("id: %d", id) + } + // 将 id 作为 key 存入 map + m[id] = i + } + // 成功生成 snowflake ID + fmt.Println("All", len(m), "snowflake ID Get successed!") +} diff --git a/example/Snowflake/readme.md b/example/Snowflake/readme.md new file mode 100644 index 0000000..00f6089 --- /dev/null +++ b/example/Snowflake/readme.md @@ -0,0 +1,5 @@ +雪花算法 + +参考:[https://zhuanlan.zhihu.com/p/251066557](https://zhuanlan.zhihu.com/p/251066557) + +![image](雪花.png) \ No newline at end of file diff --git a/example/Snowflake/雪花.png b/example/Snowflake/雪花.png new file mode 100644 index 0000000..d6058a5 Binary files /dev/null and b/example/Snowflake/雪花.png differ diff --git a/readme.md b/readme.md index 0ff4476..643c8cc 100644 --- a/readme.md +++ b/readme.md @@ -19,6 +19,8 @@ json与struct之间转换处理工具使用示例 2020/12/15: 新增通过pinyin包获取中文拼音的使用示例 +2020/12/16: 新增雪花算法实现 + # mod vendor模式加载包 通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到