网游服务器中的GUID(唯一标识码)实现-基于snowflake算法

本文中的算法采用twitter的snowflake算法,具体请搜索介绍,原来是用Scala写的,因我项目需要,改写成C++语言,主要用于高效的生成唯一的ID, 核心算法就是毫秒级时间(41位)+机器ID(10位)+毫秒内序列(12位).

网上也有好多PHP写的插件模块,核心用了网络通讯将生成的ID发送给PHP使用,没深入研究PHP的模块写法。

废话不多说了,还是直接上代码好了。

uuid.h

#ifndef __UTIL_UUID_H__
#define __UTIL_UUID_H__

#include <stdint.h>

namespace utils
{
// twitter snowflake算法
// 64       63--------------22---------12---------0
// 符号位   |     41位时间   |10位机器码|12位自增码|
extern uint64_t get_time();

class unique_id_t
{
public:
    unique_id_t();
    ~unique_id_t();

    void set_epoch(uint64_t epoch);
    void set_machine(int32_t machine);
    int64_t generate();

private:
    uint64_t epoch_;
    uint64_t time_;
    int32_t machine_;
    int32_t sequence_;
};

}

#endif // !__UTIL_UUID_H__

uuid.cpp

#include "uuid.h"
#if defined(__GUNC__)
#include <sys/time.h>
#include <unistd.h>
#define EPOCHFILETIME 11644473600000000ULL
#else
#include <windows.h>
#include <time.h>
#define EPOCHFILETIME 11644473600000000Ui64
#endif

namespace utils
{
    uint64_t get_time()
    {
#ifdef __GUNC__
        struct timeval tv;
        gettimeofday(&tv, NULL);
        uint64 time = tv.tv_usec;
        time /= 1000;
        time += (tv.tv_sec * 1000);
        return time;
#else
        FILETIME filetime;
        uint64_t time = 0;
        GetSystemTimeAsFileTime(&filetime);

        time |= filetime.dwHighDateTime;
        time <<= 32;
        time |= filetime.dwLowDateTime;

        time /= 10;
        time -= EPOCHFILETIME;
        return time / 1000;
#endif
    }

    unique_id_t::unique_id_t()
    {
        epoch_ = 0;
        time_ = 0;
        machine_ = 0;
        sequence_ = 0;
    }

    unique_id_t::~unique_id_t()
    {

    }

    void unique_id_t::set_epoch(uint64_t epoch)
    {
        epoch_ = epoch;
    }

    void unique_id_t::set_machine(int32_t machine)
    {
        machine_ = machine;
    }

    int64_t unique_id_t::generate()
    {
        int64_t value = 0;
        uint64_t time = get_time() - epoch_;

        // 保留后41位时间
        value = time << 22;

        // 中间10位是机器ID
        value |= (machine_ & 0x3FF) << 12;

        // 最后12位是sequenceID
        value |= sequence_++ & 0xFFF;
        if (sequence_ == 0x1000)
        {
            sequence_ = 0;
        }

        return value;
    }
}

#ifdef __TEST__
#include <iostream>
void test()
{
    utils::unique_id_t* u_id_ptr = new utils::unique_id_t();
    u_id_ptr->set_epoch(uint64_t(1367505795100));
    u_id_ptr->set_machine(int32_t(100));
    for (int i = 0; i < 1024; ++i)
    {
        std::cout << u_id_ptr->generate() << std::endl;;
    }
}
#endif

这样的唯一ID就可以用来表示你系统中使用的例如物品唯一ID,坐骑唯一ID等等数据,方便记录和追踪。

  1. LQF说道:

    time = tv.tv_usec / 1000;//取毫秒time += ((uint64_t)tv.tv_sec * 1000);

  2. LQF说道:

    tv.tv_sec * 1000 这个地方加个强转((uint64_t)tv.tv_sec * 1000)就不会溢出了。。

  3. LQF说道:

    #include #include #include int main(int argc, char** argv){ printf("%dn", sizeof(time_t)); printf("%dn", sizeof(int64_t)); return 0;}48centos 32位机器

  4. LQF说道:

    用你这个在linux上面跑出现了负数。。

    1. eliteYang说道:

      恩,我知道会有负数的,不过不影响使用,因为C++是移位的,跟原本的snowflake算法中的字符串没法比

  5. LQF说道:

    linux time_t是long吧。。。。。所以要溢出。。

  6. LQF说道:

    time += (tv.tv_sec * 1000); 这个要溢出。

    1. eliteYang说道:

      为善么会溢出,second的位数乘以1000,离64位还远呢

  7. dfd说道:
    Firefox Windows XP

    参见UUID库啊

    1. eliteYang说道:
      Google Chrome Windows 7

      哪个UUID库,给个地址看看

  8. penwei说道:
    Google Chrome Windows 7

    机器ID需要根据每台服务器配置还是自动算的

    1. eliteYang说道:
      Google Chrome Windows 7

      可以通过服务器的配置,也可以通过自定义的算法来计算,这个就看自己使用了,只要这个ID能区分开就可以了

  9. jerry说道:
    Google Chrome Windows 8.1

    utils::unique_id_t* u_id_ptr = new utils::unique_id_t();
    没有delete...指针有这么好么,为毛不用对象

    1. eliteYang说道:
      Google Chrome Windows 7

      因为如果在逻辑模块中使用,指针获得和判空比较方便,虽然对象也可以通过&方法取出指针,但总没有指针用指针干净利落。所以,什么时候用指针,什么时候用对象,是要区分环境的,没有说哪个好哪个不好的。

  10. 爱浮夸说道:
    Google Chrome Windows 7

    高级东西呀。

    1. eliteYang说道:
      Google Chrome Windows 7

      也不怎么高级,基础内容, :lol:

发表评论

电子邮件地址不会被公开。必填项已用 * 标注