diff --git a/CPP/README.md b/CPP/README.md new file mode 100644 index 0000000..c037a11 --- /dev/null +++ b/CPP/README.md @@ -0,0 +1,49 @@ +# ❄ idgenerator-CPP + +## 编译说明 + +跨平台,只需要该系统有C++11或以上编译器就可以编译使用。 + +## 使用方式 +只需要包含 IdGenerator.h 头文件即可 +#include "idgen/IdGenerator.h" + +## 调用示例(CPP) + +第1步,**全局** 初始化(应用程序启动时执行一次,实际上代码保证了初始化只能运行一次): +```c +// 初始化方式1: +idgen::IdGeneratorOptions options; +options.Method = method; +options.WorkerId = 1; // Your_Unique_Worker_Id +options.SeqBitLength = 10; // 默认值6,限制每毫秒生成的ID个数。若生成速度超过5万个/秒,建议加大 SeqBitLength 到 10。 +// options.WorkerIdBitLength = 10; // 默认值6,限定 WorkerId 最大值为2^6-1,即默认最多支持64个节点。 +// options.BaseTime = Your_Base_Time; // 如果要兼容老系统的雪花算法,此处应设置为老系统的BaseTime。 +idgen::IdGenerator::CreateInstance(options); + +// 初始化方式2: +// 或者如果使用默认的options参数, 只需要 +//idgen::IdGenerator::CreateInstance(Your_Unique_Worker_Id); + +// 以上过程只需全局一次,且应在生成ID之前完成。 +``` + +第2步,生成ID: +```c +// 初始化后,在任何需要生成ID的地方,调用以下方法: +long newId = idgen::IdGenerator::NextId(); +``` + +## 编译示例(CPP) +```shell +如果c++编译器默认是c++11以前的,但支持c++11, 编译的时候需要加上 -std=c++11, 类似: +g++ -std=c++11 -pthread -g -W -O2 main.cpp -o testcpp + +如果c++编译器默认就是c++11或以上的,只需类似: +g++ -pthread -g -W -O2 main.cpp -o testcpp + +编译完毕后,可以运行,例如: +./testcpp > testcpp.log + + +``` diff --git a/CPP/source/idgen/IdGenerator.h b/CPP/source/idgen/IdGenerator.h new file mode 100644 index 0000000..25e3961 --- /dev/null +++ b/CPP/source/idgen/IdGenerator.h @@ -0,0 +1,428 @@ +/* + * 版权属于:yitter(yitter@126.com) + * 翻译为C++代码:fuzhufang(fuzhufang@126.com) + * 开源地址:https://github.com/yitter/idgenerator + */ + +#pragma once +#include +#include +#include +#include +#include +#include +// #include + +namespace idgen +{ + typedef struct IdGenOptions + { + /// 雪花计算方法,(1-漂移算法|2-传统算法),默认1 + uint8_t Method{1}; + + /// 基础时间(ms单位),不能超过当前系统时间 + uint64_t BaseTime{1582136402000}; + + /// 机器码,必须由外部设定,最大值 2^WorkerIdBitLength-1 + uint32_t WorkerId{0}; + + /// 机器码位长,默认值6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过22) + uint8_t WorkerIdBitLength{6}; + + /// 序列数位长,默认值6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过22) + uint8_t SeqBitLength{6}; + + /// 最大序列数(含),设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值0,表示最大序列数取最大值(2^SeqBitLength-1]) + uint32_t MaxSeqNumber{0}; + + /// 最小序列数(含),默认值5,取值范围 [5, MaxSeqNumber],每毫秒的前5个序列数对应编号0-4是保留位,其中1-4是时间回拨相应预留位,0是手工新值预留位 + uint32_t MinSeqNumber{5}; + + /// 最大漂移次数(含),默认2000,推荐范围 500-20000(与计算能力有关) + uint32_t TopOverCostCount{2000}; + + } IdGeneratorOptions; + + typedef struct SnowFlakeWorker + { + /// 雪花计算方法,(1-漂移算法|2-传统算法),默认1 + uint8_t Method{0}; + + /// 基础时间 + uint64_t BaseTime{0}; + + /// 机器码 + uint32_t WorkerId{0}; + + /// 机器码位长 + uint8_t WorkerIdBitLength{0}; + + /// 自增序列数位长 + uint8_t SeqBitLength{0}; + + /// 最大序列数(含) + uint32_t MaxSeqNumber{0}; + + /// 最小序列数(含) + uint32_t MinSeqNumber{0}; + + /// 最大漂移次数(含) + uint32_t TopOverCostCount{0}; + + uint8_t _TimestampShift{0}; + uint32_t _CurrentSeqNumber{0}; + int64_t _LastTimeTick{0}; + int64_t _TurnBackTimeTick{0}; + uint8_t _TurnBackIndex{0}; + bool _IsOverCost{false}; + uint32_t _OverCostCountInOneTerm{0}; + uint32_t _GenCountInOneTerm{0}; + uint32_t _TermIndex{0}; + + } SnowFlakeWorker; + + inline static int64_t GetCurrentTime() + { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count(); + } + + inline static int64_t GetCurrentMicroTime() + { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count(); + } + + class SnowWorker + { + public: + int64_t GetCurrentTimeTick() + { + return GetCurrentTime() - (int64_t)_SnowFlakeWorker.BaseTime; + } + + int64_t GetNextTimeTick() + { + int64_t tempTimeTicker = GetCurrentTimeTick(); + while (tempTimeTicker <= _SnowFlakeWorker._LastTimeTick) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // 暂停1ms + tempTimeTicker = GetCurrentTimeTick(); + } + return tempTimeTicker; + } + + void EndOverCostAction(int64_t useTimeTick) + { + // if (_SnowFlakeWorker._TermIndex > 10000) { + // _SnowFlakeWorker._TermIndex = 0; + // } + } + + int64_t NextOverCostId() + { + int64_t currentTimeTick = GetCurrentTimeTick(); + if (currentTimeTick > _SnowFlakeWorker._LastTimeTick) + { + EndOverCostAction(currentTimeTick); + _SnowFlakeWorker._LastTimeTick = currentTimeTick; + _SnowFlakeWorker._CurrentSeqNumber = _SnowFlakeWorker.MinSeqNumber; + _SnowFlakeWorker._IsOverCost = false; + _SnowFlakeWorker._OverCostCountInOneTerm = 0; + _SnowFlakeWorker._GenCountInOneTerm = 0; + return CalcId(); + } + if (_SnowFlakeWorker._OverCostCountInOneTerm > _SnowFlakeWorker.TopOverCostCount) + { + EndOverCostAction(currentTimeTick); + _SnowFlakeWorker._LastTimeTick = GetNextTimeTick(); + _SnowFlakeWorker._CurrentSeqNumber = _SnowFlakeWorker.MinSeqNumber; + _SnowFlakeWorker._IsOverCost = false; + _SnowFlakeWorker._OverCostCountInOneTerm = 0; + _SnowFlakeWorker._GenCountInOneTerm = 0; + return CalcId(); + } + if (_SnowFlakeWorker._CurrentSeqNumber > _SnowFlakeWorker.MaxSeqNumber) + { + _SnowFlakeWorker._LastTimeTick++; + _SnowFlakeWorker._CurrentSeqNumber = _SnowFlakeWorker.MinSeqNumber; + _SnowFlakeWorker._IsOverCost = true; + _SnowFlakeWorker._OverCostCountInOneTerm++; + _SnowFlakeWorker._GenCountInOneTerm++; + return CalcId(); + } + + _SnowFlakeWorker._GenCountInOneTerm++; + return CalcId(); + } + + int64_t NextNormalId() + { + int64_t currentTimeTick = GetCurrentTimeTick(); + if (currentTimeTick < _SnowFlakeWorker._LastTimeTick) + { + if (_SnowFlakeWorker._TurnBackTimeTick < 1) + { + _SnowFlakeWorker._TurnBackTimeTick = _SnowFlakeWorker._LastTimeTick - 1; + _SnowFlakeWorker._TurnBackIndex++; + // 每毫秒序列数的前 5 位是预留位,0 用于手工新值,1-4 是时间回拨次序 + // 支持 4 次回拨次序(避免回拨重叠导致 ID 重复),可无限次回拨(次序循环使用)。 + if (_SnowFlakeWorker._TurnBackIndex > 4) + { + _SnowFlakeWorker._TurnBackIndex = 1; + } + } + + // std::this_thread::sleep_for(std::chrono::milliseconds(1));; // 暂停1ms + return CalcTurnBackId(); + } + + if (_SnowFlakeWorker._TurnBackTimeTick > 0) + { + _SnowFlakeWorker._TurnBackTimeTick = 0; + } + + if (currentTimeTick > _SnowFlakeWorker._LastTimeTick) + { + _SnowFlakeWorker._LastTimeTick = currentTimeTick; + _SnowFlakeWorker._CurrentSeqNumber = _SnowFlakeWorker.MinSeqNumber; + return CalcId(); + } + + if (_SnowFlakeWorker._CurrentSeqNumber > _SnowFlakeWorker.MaxSeqNumber) + { + _SnowFlakeWorker._TermIndex++; + _SnowFlakeWorker._LastTimeTick++; + _SnowFlakeWorker._CurrentSeqNumber = _SnowFlakeWorker.MinSeqNumber; + _SnowFlakeWorker._IsOverCost = true; + _SnowFlakeWorker._OverCostCountInOneTerm = 1; + _SnowFlakeWorker._GenCountInOneTerm = 1; + return CalcId(); + } + + return CalcId(); + } + + int64_t CalcId() + { + uint64_t result = (_SnowFlakeWorker._LastTimeTick << _SnowFlakeWorker._TimestampShift) | (_SnowFlakeWorker.WorkerId << _SnowFlakeWorker.SeqBitLength) | + (_SnowFlakeWorker._CurrentSeqNumber); + _SnowFlakeWorker._CurrentSeqNumber++; + return result; + } + + int64_t CalcTurnBackId() + { + uint64_t result = (_SnowFlakeWorker._LastTimeTick << _SnowFlakeWorker._TimestampShift) | (_SnowFlakeWorker.WorkerId << _SnowFlakeWorker.SeqBitLength) | + (_SnowFlakeWorker._TurnBackIndex); + _SnowFlakeWorker._TurnBackTimeTick--; + return result; + } + + int64_t WorkerM1NextId() + { + std::lock_guard lock(_ThreadMutex); + int64_t id = _SnowFlakeWorker._IsOverCost ? NextOverCostId() : NextNormalId(); + return id; + } + + int64_t WorkerM2NextId() + { + std::lock_guard lock(_ThreadMutex); + + int64_t currentTimeTick = GetCurrentTimeTick(); + if (_SnowFlakeWorker._LastTimeTick == currentTimeTick) + { + _SnowFlakeWorker._CurrentSeqNumber = (++_SnowFlakeWorker._CurrentSeqNumber) & _SnowFlakeWorker.MaxSeqNumber; + if (_SnowFlakeWorker._CurrentSeqNumber == 0) + { + currentTimeTick = GetNextTimeTick(); + } + } + else + { + _SnowFlakeWorker._CurrentSeqNumber = _SnowFlakeWorker.MinSeqNumber; + } + + _SnowFlakeWorker._LastTimeTick = currentTimeTick; + int64_t id = (int64_t)((currentTimeTick << _SnowFlakeWorker._TimestampShift) | + (_SnowFlakeWorker.WorkerId << _SnowFlakeWorker.SeqBitLength) | + _SnowFlakeWorker._CurrentSeqNumber); + + return id; + } + + void SetOptions(IdGeneratorOptions options) + { + // 1.BaseTime + if (options.BaseTime == 0) + { + _SnowFlakeWorker.BaseTime = 1582136402000; + } + else if (options.BaseTime < 631123200000 || (int64_t)options.BaseTime > GetCurrentTime()) + { + throw std::invalid_argument("BaseTime error."); + } + else + { + _SnowFlakeWorker.BaseTime = options.BaseTime; + } + + // 2.WorkerIdBitLength + if (options.WorkerIdBitLength <= 0) + { + throw std::invalid_argument("WorkerIdBitLength error.(range:[1, 21])"); + } + if (options.SeqBitLength + options.WorkerIdBitLength > 22) + { + throw std::invalid_argument("error:WorkerIdBitLength + SeqBitLength <= 22"); + } + else + { + // _SnowFlakeWorker.WorkerIdBitLength = options.WorkerIdBitLength; + _SnowFlakeWorker.WorkerIdBitLength = options.WorkerIdBitLength <= 0 ? 6 : options.WorkerIdBitLength; + } + + // 3.WorkerId + uint32_t maxWorkerIdNumber = (1 << options.WorkerIdBitLength) - 1; + if (maxWorkerIdNumber == 0) + { + maxWorkerIdNumber = 63; + } + if (options.WorkerId > maxWorkerIdNumber) + { + throw std::invalid_argument("WorkerId error. (range:[0, {2^options.WorkerIdBitLength-1]}"); + } + else + { + _SnowFlakeWorker.WorkerId = options.WorkerId; + } + + // 4.SeqBitLength + if (options.SeqBitLength < 2 || options.SeqBitLength > 21) + { + throw std::invalid_argument("SeqBitLength error. (range:[2, 21])"); + } + else + { + _SnowFlakeWorker.SeqBitLength = options.SeqBitLength <= 0 ? 6 : options.SeqBitLength; + } + + // 5.MaxSeqNumber + uint32_t maxSeqNumber = (1 << options.SeqBitLength) - 1; + if (maxSeqNumber == 0) + { + maxSeqNumber = 63; + } + if (options.MaxSeqNumber > maxSeqNumber) + { + throw std::invalid_argument("MaxSeqNumber error. (range:[1, {2^options.SeqBitLength-1}]"); + } + else + { + _SnowFlakeWorker.MaxSeqNumber = options.MaxSeqNumber <= 0 ? maxSeqNumber : options.MaxSeqNumber; + } + + // 6.MinSeqNumber + if (options.MinSeqNumber < 5 || options.MinSeqNumber > maxSeqNumber) + { + throw std::invalid_argument("MinSeqNumber error. (range:[5, {options.MinSeqNumber}]"); + } + else + { + _SnowFlakeWorker.MinSeqNumber = options.MinSeqNumber <= 0 ? 5 : options.MinSeqNumber; + } + + // 7.TopOverCostCount + if (options.TopOverCostCount > 10000) + { + throw std::invalid_argument("TopOverCostCount error. (range:[0, 10000]"); + } + else + { + //_SnowFlakeWorker.TopOverCostCount = options.TopOverCostCount <= 0 ? 2000 : options.TopOverCostCount; + _SnowFlakeWorker.TopOverCostCount = options.TopOverCostCount; + } + + // 8.Others + _SnowFlakeWorker._TimestampShift = _SnowFlakeWorker.WorkerIdBitLength + _SnowFlakeWorker.SeqBitLength; + _SnowFlakeWorker._CurrentSeqNumber = _SnowFlakeWorker.MinSeqNumber; + + _SnowFlakeWorker.Method = options.Method; + if (options.Method == 2) + { + this->_NextId = &SnowWorker::WorkerM2NextId; + } + else + { + this->_NextId = &SnowWorker::WorkerM1NextId; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + ; // 暂停500ms + } + } + + inline int64_t NextId() + { + return (this->*_NextId)(); + } + + protected: + std::mutex _ThreadMutex; + SnowFlakeWorker _SnowFlakeWorker; + int64_t (SnowWorker::*_NextId)(); + }; + + class IdGenerator + { + public: + // 禁止拷贝构造函数和赋值操作符 + IdGenerator(const IdGenerator &) = delete; + IdGenerator &operator=(const IdGenerator &) = delete; + + static void CreateInstance(uint32_t WorkerId) + { + IdGeneratorOptions options; + options.WorkerId = WorkerId; + CreateInstance(options); + } + + static void CreateInstance(IdGeneratorOptions options) + { + static std::once_flag initInstanceFlag; + // 使用std::once_flag和std::call_once保证只初始化一次 + std::call_once(initInstanceFlag, [options]() + { createInstance(options); }); + } + + static int64_t NextId() + { + assert(instance && "Please call CreateInstance first to create an instance"); + return instance->_sworker.NextId(); + } + + private: + IdGenerator() + { + } + + // 使用std::once_flag和std::call_once保证只初始化一次 + static void createInstance(IdGeneratorOptions options) + { + static IdGenerator obj; + // 理论上保证了这个函数只会被调用一次,就算是多线程并发调用也不例外 + assert(nullptr == instance && "Theoretically, it is guaranteed that this function will only be called once, even if it is called concurrently by multiple threads."); + instance = &obj; + instance->_sworker.SetOptions(options); + // printf("调用 %s \n", __FUNCTION__); // 在多个线程调用 CreateInstance 时候,看是否出现打印多次 + } + + private: + SnowWorker _sworker; + + private: + static IdGenerator *instance; + }; + + IdGenerator *IdGenerator::instance = nullptr; + +} // namespace idgen diff --git a/CPP/source/main.cpp b/CPP/source/main.cpp new file mode 100644 index 0000000..3c777c7 --- /dev/null +++ b/CPP/source/main.cpp @@ -0,0 +1,83 @@ +#include +#include +#include "idgen/IdGenerator.h" + +const int GenIdCount = 500000; +const bool multiThread = true; +const int threadCount = 50; +const int method = 1; + +void RunMultiThread() +{ + int64_t start = idgen::GetCurrentMicroTime(); + for (int i = 0; i < GenIdCount / threadCount; i++) + { + int64_t id = idgen::IdGenerator::NextId(); + printf("ID: %" PRId64 "\n", id); + } + + int64_t end = idgen::GetCurrentMicroTime(); + printf("%s,total:%" PRId64 " μs\n", method == 1 ? "1" : "2", (end - start)); +} + +void RunSingle() +{ + int64_t start = idgen::GetCurrentMicroTime(); + for (int i = 0; i < GenIdCount; i++) + { + int64_t id = idgen::IdGenerator::NextId(); + printf("ID: %" PRId64 "\n", id); + } + + int64_t end = idgen::GetCurrentMicroTime(); + + printf("%s, total: %" PRId64 " us\n", method == 1 ? "1" : "2", (end - start)); +} + +void create_instance_fun() +{ + idgen::IdGeneratorOptions options; + options.Method = method; + options.WorkerId = 1; + options.SeqBitLength = 10; + idgen::IdGenerator::CreateInstance(options); +} + +void test_multithread_run_create_instance() +{ + std::vector threads; + for (int i = 0; i < threadCount; i++) + { + threads.emplace_back(create_instance_fun); + } + for (auto &t : threads) + { + t.join(); + } +} + +int main() +{ + if (multiThread) { + test_multithread_run_create_instance(); // 测试多线程调用同时初始化,也确保只会安全初始化一次,确保单例唯一性 + } else { + create_instance_fun(); // 单线程调用初始化 + } + + if (multiThread) + { + std::vector threads; + for (int i = 0; i < threadCount; i++) + { + threads.emplace_back(RunMultiThread); + } + for (auto &t : threads) + { + t.join(); + } + } + else + { + RunSingle(); + } +}