• ID Generator


    Implementation

    Some details of this implementation borrow from sonyflake.
    Here using 8 bits to represent time in units of 500ms, 4 bits to represent sequence, 4 bits to represent machine.
    As a result:

    • The lifetime is (2^8 imes 500ms = 128s)
    • It can work in (2^4 = 16) distributed machines
    • It can generate (2^4 = 16) IDs per (500ms) at most in a single thread

    Code

    server:

    package main
    
    import (
    	"errors"
    	"net/http"
    	"os"
    	"sync"
    	"time"
    
    	"github.com/gin-gonic/gin"
    	logger "github.com/sirupsen/logrus"
    	"github.com/x-cray/logrus-prefixed-formatter"
    )
    
    func init() {
    	logger.SetFormatter(&prefixed.TextFormatter{
    		TimestampFormat: "2006-01-02 15:04:05",
    		FullTimestamp:   true,
    		ForceFormatting: true,
    		DisableColors:   true,
    	})
    	logger.SetOutput(os.Stdout)
    	logger.SetLevel(logger.DebugLevel)
    }
    
    const (
    	BitLenTick      = 8
    	BitLenSequence  = 4
    	BitLenMachineID = 4
    
    	tickTimeUnit = time.Millisecond * 500
    )
    
    var idFlake *IDFlake
    
    type IDFlake struct {
    	mu sync.Mutex
    
    	machineID   int64
    	startTick   int64 // ticks of the start time of IDFlake from UNIX epoch
    	elapsedTick int64 // elapsed ticks from start tick
    	sequence    int64
    }
    
    func toFlakeTime(t time.Time) int64 {
    	return t.UTC().UnixNano() / int64(tickTimeUnit)
    }
    
    func NewIDFlake(machineID int64, startTime time.Time) *IDFlake {
    	return &IDFlake{
    		machineID:   machineID,
    		startTick:   toFlakeTime(startTime),
    		elapsedTick: 0,
    		sequence:    0,
    	}
    }
    
    func (f *IDFlake) toID() (uint64, error) {
    	if f.elapsedTick >= 1<<BitLenTick {
    		return 0, errors.New("over the time limit")
    	}
    
    	return uint64(f.elapsedTick<<(BitLenMachineID+BitLenSequence)) | uint64(f.machineID<<BitLenSequence) | uint64(f.sequence), nil
    }
    
    func (f *IDFlake) getID() (uint64, error) {
    	f.mu.Lock()
    	defer f.mu.Unlock()
    
    	currentTick := toFlakeTime(time.Now()) - f.startTick
    	if f.elapsedTick < currentTick {
    		f.sequence = 0
    		f.elapsedTick = currentTick
    	} else {
    		f.sequence = (f.sequence + 1) & (1<<BitLenSequence - 1)
    		if f.sequence == 0 {
    			f.elapsedTick++
    			sleepFor := sleepTime(f.elapsedTick - currentTick)
    			time.Sleep(sleepFor)
    			logger.Infof("slept for %.8fms", sleepFor.Seconds()*1000)
    		}
    	}
    
    	return f.toID()
    }
    
    func sleepTime(overTick int64) time.Duration {
    	return time.Duration(overTick)*tickTimeUnit - time.Duration(time.Now().UTC().UnixNano())%tickTimeUnit
    }
    
    func Decompose(id uint64) map[string]uint64 {
    	return map[string]uint64{
    		"id":       id,
    		"tick":     id >> (BitLenMachineID + BitLenSequence),
    		"machine":  (id >> BitLenSequence) & (1<<BitLenMachineID - 1),
    		"sequence": id & (1<<BitLenSequence - 1),
    	}
    }
    
    func getID(c *gin.Context) {
    	id, err := idFlake.getID()
    	if err != nil {
    		logger.Warnf("getID error: %v", err)
    		c.JSON(http.StatusOK,
    			map[string]interface{}{
    				"error": err.Error(),
    			},
    		)
    		return
    	}
    	result := Decompose(id)
    	c.JSON(http.StatusOK, result)
    }
    
    func httpRun() {
    	r := gin.Default()
    	r.GET("/get_id", getID)
    	r.Run(":8080")
    }
    
    func main() {
    	idFlake = NewIDFlake(8, time.Now())
    	httpRun()
    }
    

    client:

    # coding: utf-8
    
    import requests
    
    
    def main():
        while True:
            res = requests.get('http://127.0.0.1:8080/get_id').json()
            print res
            if res.get('error'):
                break
    
    
    if __name__ == '__main__':
        main()
    

    Run

    server:

    [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
    
    [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
     - using env:	export GIN_MODE=release
     - using code:	gin.SetMode(gin.ReleaseMode)
    
    [GIN-debug] Listening and serving HTTP on :8080
    [2020-04-01 16:11:05]  INFO slept for 285.71300000ms
    [2020-04-01 16:11:06]  INFO slept for 446.46500000ms
    [2020-04-01 16:11:06]  INFO slept for 450.56500000ms
    [2020-04-01 16:11:07]  INFO slept for 449.93100000ms
    ......
    [2020-04-01 16:13:08]  INFO slept for 449.52100000ms
    [2020-04-01 16:13:09]  INFO slept for 441.43400000ms
    [2020-04-01 16:13:09]  INFO slept for 434.80600000ms
    [2020-04-01 16:13:10]  INFO slept for 445.62500000ms
    [2020-04-01 16:13:10]  INFO slept for 444.83400000ms
    [2020-04-01 16:13:11]  INFO slept for 437.82700000ms
    [2020-04-01 16:13:11]  WARN getID error: over the time limit
    

    client:

    {u'machine': 0, u'tick': 4, u'id': 1024, u'sequence': 0}
    {u'machine': 0, u'tick': 4, u'id': 1025, u'sequence': 1}
    {u'machine': 0, u'tick': 4, u'id': 1026, u'sequence': 2}
    {u'machine': 0, u'tick': 4, u'id': 1027, u'sequence': 3}
    {u'machine': 0, u'tick': 4, u'id': 1028, u'sequence': 4}
    {u'machine': 0, u'tick': 4, u'id': 1029, u'sequence': 5}
    {u'machine': 0, u'tick': 4, u'id': 1030, u'sequence': 6}
    {u'machine': 0, u'tick': 4, u'id': 1031, u'sequence': 7}
    {u'machine': 0, u'tick': 4, u'id': 1032, u'sequence': 8}
    {u'machine': 0, u'tick': 4, u'id': 1033, u'sequence': 9}
    {u'machine': 0, u'tick': 4, u'id': 1034, u'sequence': 10}
    {u'machine': 0, u'tick': 4, u'id': 1035, u'sequence': 11}
    {u'machine': 0, u'tick': 4, u'id': 1036, u'sequence': 12}
    {u'machine': 0, u'tick': 4, u'id': 1037, u'sequence': 13}
    {u'machine': 0, u'tick': 4, u'id': 1038, u'sequence': 14}
    {u'machine': 0, u'tick': 4, u'id': 1039, u'sequence': 15}
    {u'machine': 0, u'tick': 5, u'id': 1280, u'sequence': 0}
    {u'machine': 0, u'tick': 5, u'id': 1281, u'sequence': 1}
    {u'machine': 0, u'tick': 5, u'id': 1282, u'sequence': 2}
    {u'machine': 0, u'tick': 5, u'id': 1283, u'sequence': 3}
    {u'machine': 0, u'tick': 5, u'id': 1284, u'sequence': 4}
    ......
    {u'machine': 0, u'tick': 254, u'id': 65037, u'sequence': 13}
    {u'machine': 0, u'tick': 254, u'id': 65038, u'sequence': 14}
    {u'machine': 0, u'tick': 254, u'id': 65039, u'sequence': 15}
    {u'machine': 0, u'tick': 255, u'id': 65280, u'sequence': 0}
    {u'machine': 0, u'tick': 255, u'id': 65281, u'sequence': 1}
    {u'machine': 0, u'tick': 255, u'id': 65282, u'sequence': 2}
    {u'machine': 0, u'tick': 255, u'id': 65283, u'sequence': 3}
    {u'machine': 0, u'tick': 255, u'id': 65284, u'sequence': 4}
    {u'machine': 0, u'tick': 255, u'id': 65285, u'sequence': 5}
    {u'machine': 0, u'tick': 255, u'id': 65286, u'sequence': 6}
    {u'machine': 0, u'tick': 255, u'id': 65287, u'sequence': 7}
    {u'machine': 0, u'tick': 255, u'id': 65288, u'sequence': 8}
    {u'machine': 0, u'tick': 255, u'id': 65289, u'sequence': 9}
    {u'machine': 0, u'tick': 255, u'id': 65290, u'sequence': 10}
    {u'machine': 0, u'tick': 255, u'id': 65291, u'sequence': 11}
    {u'machine': 0, u'tick': 255, u'id': 65292, u'sequence': 12}
    {u'machine': 0, u'tick': 255, u'id': 65293, u'sequence': 13}
    {u'machine': 0, u'tick': 255, u'id': 65294, u'sequence': 14}
    {u'machine': 0, u'tick': 255, u'id': 65295, u'sequence': 15}
    {u'error': u'over the time limit'}
    
  • 相关阅读:
    Flink实时数仓(数据可视化)
    Hive调优
    面试(涉及技术一)
    启动Flink的yarnsession模式出错
    面试(涉及技术二)
    Filnk实时数仓(DWS层)
    Filnk实时数仓(Clickhouse)
    Filnk实时数仓(Prometheus监控)
    Filnk实时数仓(DWM层)
    Centos7下杀毒软件clamav的安装和使用
  • 原文地址:https://www.cnblogs.com/ToRapture/p/12613716.html
Copyright © 2020-2023  润新知