分布式唯一ID算法(Twitter Snowflake算法) c++实现

结构

snowflake的结构如下(每部分用-分开):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

c++源码与测试用例

#include <bits/stdc++.h>
using namespace std;
#include <sys/time.h>

template<typename ... Args>
std::string string_format(const std::string& format, Args ... args){
    size_t size = 1 + snprintf(nullptr, 0, format.c_str(), args ...);
    char bytes[size];
    snprintf(bytes, size, format.c_str(), args ...);
    return std::string(bytes);
}

class SnowflakeIdGenertor {
public:

    /**
         * 构造函数
         *
         * @param workerId     工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
    SnowflakeIdGenertor(long workerId, long datacenterId)
    {
        if (workerId > maxWorkerId || workerId < 0) {
            std::cout << (string_format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            return;
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            std::cout << (string_format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            return;
        }
        this->workerId = workerId;
        this->datacenterId = datacenterId;
    }

    /**
        * 获得下一个ID (该方法是线程安全的)
        *
        * @return SnowflakeId
        */
       long long nextId() {
           long long timestamp = timeGen();

           //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
           if (timestamp < lastTimestamp) {
                std::cout << (string_format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
                return 0;
           }

           //如果是同一时间生成的,则进行毫秒内序列
           if (lastTimestamp == timestamp) {
               sequence = (sequence + 1) & sequenceMask;
               //毫秒内序列溢出
               if (sequence == 0) {
                   //阻塞到下一个毫秒,获得新的时间戳
                   timestamp = tilNextMillis(lastTimestamp);
               }
           }
           //时间戳改变,毫秒内序列重置
           else {
               sequence = 0L;
           }

           //上次生成ID的时间截
           lastTimestamp = timestamp;

           //移位并通过或运算拼到一起组成64位的ID
           return ((timestamp - twepoch) << timestampLeftShift) //
                   | (datacenterId << datacenterIdShift) //
                   | (workerId << workerIdShift) //
                   | sequence;
       }

       /**
        * 阻塞到下一个毫秒,直到获得新的时间戳
        *
        * @param lastTimestamp 上次生成ID的时间截
        * @return 当前时间戳
        */
        long long tilNextMillis(long long  lastTimestamp) {
           long long  timestamp = timeGen();
           while (timestamp <= lastTimestamp) {
               timestamp = timeGen();
           }
           return timestamp;
       }

       /**
        * 返回以毫秒为单位的当前时间
        *
        * @return 当前时间(毫秒)
        */
        long long timeGen() {
           struct timeval tv;
           gettimeofday(&tv,NULL);
           long long nowTimeMs = (long long)tv.tv_sec*1000 + tv.tv_usec/1000;
           return nowTimeMs;
       }
private:
    /**
        * 开始时间截 (2020-01-01)
        */
    const long long  twepoch = 1577808000000L;

    /**
        * 机器id所占的位数
        */
    const long workerIdBits = 5L;

    /**
        * 数据标识id所占的位数
        */
    const long datacenterIdBits = 5L;

    /**
        * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
        */
    const long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
        * 支持的最大数据标识id,结果是31
        */
    const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
        * 序列在id中占的位数
        */
    const long sequenceBits = 12L;

    /**
        * 机器ID向左移12位
        */
    const long workerIdShift = sequenceBits;

    /**
        * 数据标识id向左移17位(12+5)
        */
    const long datacenterIdShift = sequenceBits + workerIdBits;

    /**
        * 时间截向左移22位(5+5+12)
        */
    const long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
        * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
        */
    const long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
        * 工作机器ID(0~31)
        */
    long workerId;

    /**
        * 数据中心ID(0~31)
        */
    long datacenterId;

    /**
        * 毫秒内序列(0~4095)
        */
    long sequence = 0L;

    /**
        * 上次生成ID的时间截
        */
    long long  lastTimestamp = -1L;
};

/************************************分割线**********************************************/
//以下为测试用例
#define THREAD_NUM 5
#define ONE_THREAD_PRODUCE_NUM 1000

int main()
{
    std::vector<std::thread *> vec_thread;
    std::set<long long> set_id;

    mutex mtx;
    auto say_hello = [&](int workid) {
        SnowflakeIdGenertor *idGenertor = new SnowflakeIdGenertor(workid, 1);
        for(int index = 0; index< ONE_THREAD_PRODUCE_NUM; index++)
        {
            mtx.lock();
            long long id = idGenertor->nextId();
            set_id.insert(id);
            std::cout<<workid << "_workid_id_"<< id <<std::endl;
            mtx.unlock();
        }
        delete idGenertor;
    };

    for(int i = 0; i < THREAD_NUM; ++i)
    {
        vec_thread.push_back(new std::thread(say_hello, i+1));
    }

    for(int i = 0; i < THREAD_NUM; ++i){
        vec_thread[i]->join();
        delete vec_thread[i];
    }

    vec_thread.clear();

    if (set_id.size() == THREAD_NUM * ONE_THREAD_PRODUCE_NUM) {
        std::cout << "no same id" << std::endl;
    }
    return 0;
}

测试结果展示

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值