git.zd.zone/hrpc/hrpc@v0.0.12/utils/uniqueid/uniqueid.go (about)

     1  package uniqueid
     2  
     3  import (
     4  	"errors"
     5  	"strconv"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  // 因为snowFlake目的是解决分布式下生成唯一id 所以ID中是包含集群和节点编号在内的
    11  const (
    12  	workerBits uint8 = 10 // 每台机器(节点)的ID位数 10位最大可以有2^10=1024个节点
    13  	numberBits uint8 = 12 // 表示每个集群下的每个节点,1毫秒内可生成的id序号的二进制位数 即每毫秒可生成 2^12-1=4096个唯一ID
    14  	// 这里求最大值使用了位运算,-1 的二进制表示为 1 的补码,感兴趣的同学可以自己算算试试 -1 ^ (-1 << nodeBits) 这里是不是等于 1023
    15  	nodeMax     int64 = -1 ^ (-1 << workerBits) // 节点ID的最大值,用于防止溢出
    16  	numberMax   int64 = -1 ^ (-1 << numberBits) // 同上,用来表示生成id序号的最大值
    17  	timeShift   uint8 = workerBits + numberBits // 时间戳向左的偏移量
    18  	workerShift uint8 = numberBits              // 节点ID向左的偏移量
    19  	// 41位字节作为时间戳数值的话 大约68年就会用完
    20  	// 假如你2010年1月1日开始开发系统 如果不减去2010年1月1日的时间戳 那么白白浪费40年的时间戳啊!
    21  	// 这个一旦定义且开始生成ID后千万不要改了 不然可能会生成相同的ID
    22  	epoch int64 = 1597472019000 // 这个是我在写epoch这个变量时的时间戳(毫秒)
    23  )
    24  
    25  // serviceNode should be used for global
    26  var (
    27  	serviceNode *Node
    28  	nodeID      int64
    29  	myIP        string
    30  )
    31  
    32  // Node 定义一个Node工作节点所需要的基本参数
    33  type Node struct {
    34  	mu        sync.Mutex // 添加互斥锁 确保并发安全
    35  	timestamp int64      // 记录时间戳
    36  	nodeID    int64      // 该节点的ID
    37  	number    int64      // 当前毫秒已经生成的id序列号(从0开始累加) 1毫秒内最多生成4096个ID
    38  	uniqueID  int64
    39  }
    40  
    41  type Option struct {
    42  	NodeID int64
    43  }
    44  
    45  func init() {
    46  	var err error
    47  	nodeID, err = createNodeID()
    48  	if err != nil {
    49  		panic(err)
    50  	}
    51  	serviceNode = &Node{nodeID: nodeID}
    52  }
    53  
    54  // NewNode 实例化一个工作节点
    55  // 优先级是: 传入参数 > 已存在的NodeID > createNodeID
    56  func New(options ...Option) *Node {
    57  	options = append(options, Option{NodeID: nodeID})
    58  
    59  	// Check the nodeID is valid or not
    60  	// 此时options
    61  	for _, v := range options {
    62  		if err := v.validate(); err == nil {
    63  			// 一旦有校验通过的,则赋值 return
    64  			serviceNode = &Node{nodeID: nodeID}
    65  			return serviceNode
    66  		}
    67  	}
    68  	return serviceNode
    69  }
    70  
    71  func (n *Node) Load() error {
    72  	return nil
    73  }
    74  
    75  func (n *Node) Name() string {
    76  	return "hrpc-uniqueid"
    77  }
    78  
    79  func (n *Node) DependsOn() []string {
    80  	return nil
    81  }
    82  
    83  func (o Option) validate() error {
    84  	if o.NodeID < 0 || o.NodeID > nodeMax {
    85  		return errors.New("Node ID excess of quantity")
    86  	}
    87  	return nil
    88  }
    89  
    90  // newNode will return an unique ID for different uses
    91  func newNode() *Node {
    92  	// 获取生成时的时间戳
    93  	now := time.Now().UnixNano() / 1e6 // 纳秒转毫秒
    94  	if serviceNode.timestamp == now {
    95  		serviceNode.number++
    96  		// 这里要判断,当前工作节点是否在1毫秒内已经生成numberMax个ID
    97  		if serviceNode.number > numberMax {
    98  			// 如果当前工作节点在1毫秒内生成的ID已经超过上限 需要等待1毫秒再继续生成
    99  			for now <= serviceNode.timestamp {
   100  				now = time.Now().UnixNano() / 1e6
   101  			}
   102  		}
   103  	} else {
   104  		// 如果当前时间与工作节点上一次生成ID的时间不一致 则需要重置工作节点生成ID的序号
   105  		serviceNode.number = 0
   106  		serviceNode.timestamp = now // 将机器上一次生成ID的时间更新为当前时间
   107  	}
   108  	// 第一段 now - epoch 为该算法目前已经奔跑了xxx毫秒
   109  	// 如果在程序跑了一段时间修改了epoch这个值 可能会导致生成相同的ID
   110  	serviceNode.uniqueID = int64((now-epoch)<<timeShift | (serviceNode.nodeID << workerShift) | (serviceNode.number))
   111  	return serviceNode
   112  }
   113  
   114  func String() string {
   115  	serviceNode.mu.Lock()
   116  	defer serviceNode.mu.Unlock()
   117  
   118  	return strconv.FormatInt(newNode().uniqueID, 10)
   119  }
   120  
   121  func Number() int64 {
   122  	serviceNode.mu.Lock()
   123  	defer serviceNode.mu.Unlock()
   124  
   125  	return newNode().uniqueID
   126  }
   127  
   128  func NodeID() int64 {
   129  	return nodeID
   130  }
   131  
   132  func IP() string {
   133  	return myIP
   134  }