ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hyperf Snowflake 分布式全局唯一 ID 生成器:算法原理、配置实战与源码解析

Hyperf Snowflake 分布式全局唯一 ID 生成器:算法原理、配置实战与源码解析 后端Web框架微服务RPC框架异步编程【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/hyperf/hyperf点击查看免费下载导读本文围绕 Hyperf 框架内置的hyperf/snowflake组件展开完整讲解 Twitter Snowflake 算法的 64 位二进制结构、组件的安装与配置、IdGeneratorInterface与MetaGeneratorInterface两个核心接口的用法、自定义 Meta 生成器的扩展方式以及如何在数据库 Model 中把 Snowflake ID 直接作为主键使用。读完本文你将掌握在 Hyperf 项目中快速集成全局唯一、随时间递增的分布式 ID 生成能力并理解其底层位运算与 Redis 分配机制的实现原理。一、Snowflake 算法原理Snowflake是 Twitter 提出的一种分布式全局唯一 ID 生成算法算法产出的结果是64bit的整数。在标准算法中其二进制结构从高位到低位分为以下四段位段长度用途符号位1 bit不使用恒为 0时间戳41 bits记录毫秒级时间戳机器 ID10 bits记录机器 ID由5 bits DatacenterId5 bits WorkerId组成序列号12 bits同一毫秒内产生的不同 ID 序号1. 符号位1 bit二进制中的最高位是符号位。我们生成的ID通常是正整数因此该位固定设置为0。2. 时间戳41 bits41 bits最多可以表示2^41 - 1个数值即可以记录2^41 - 1个毫秒值。换算成年份约为(2^41 - 1) / (1000 * 60 * 60 * 24 * 365)大约是69年。结合 Hyperf 组件中默认的起始时间begin_second 1560960000即 2019-06-20参见 MetaGeneratorInterface.php时间戳段的理论可用跨度可覆盖相当长的时间范围。3. 机器 ID10 bits10 bits最多可部署2^10 1024个节点其中包含5 bits的DatacenterId和5 bits的WorkerId。4. 序列号12 bits12 bits可以表示的最大正整数是2^12 - 1 4095即同一台机器在同一个毫秒内最多可以产生4095个序列号超出后需要等待下一个毫秒。Snowflake 算法的核心保证所有生成的ID都随时间的推移呈递增趋势整个分布式系统中不会产生重复ID因为DatacenterId5 bits与WorkerId5 bits被用于节点区分。Hyperf 的hyperf/snowflake组件在设计上具有良好的可扩展性允许你通过简单的扩展来实现基于 Snowflake 的各种变体算法例如下文介绍的基于UserId而非WorkerId的生成方式。二、安装组件在 Hyperf 项目中通过 Composer 安装composer require hyperf/snowflake组件依赖说明见 composer.json运行时依赖hyperf/context、hyperf/contract、hyperf/coroutinePHP 版本要求 8.2若使用基于 Redis 的RedisMilliSecondMetaGenerator/RedisSecondMetaGenerator还需要安装hyperf/redis读取配置需要hyperf/config使用MetaGeneratorFactory需要psr/container。三、配置文件与默认实现框架默认使用的MetaGeneratorInterface实现是基于Redis的毫秒级生成器RedisMilliSecondMetaGenerator。配置文件位于config/autoload/snowflake.php如果文件不存在可以通过以下命令发布默认配置php bin/hyperf.php vendor:publish hyperf/snowflake发布后的配置文件内容如下该文件在仓库中的模板见 publish/snowflake.php?php declare(strict_types1); use Hyperf\Snowflake\MetaGenerator\RedisMilliSecondMetaGenerator; use Hyperf\Snowflake\MetaGenerator\RedisSecondMetaGenerator; use Hyperf\Snowflake\MetaGeneratorInterface; return [ begin_second MetaGeneratorInterface::DEFAULT_BEGIN_SECOND, RedisMilliSecondMetaGenerator::class [ // Redis Pool pool default, // 用于计算 WorkerId 的 Key key RedisMilliSecondMetaGenerator::DEFAULT_REDIS_KEY ], RedisSecondMetaGenerator::class [ // Redis Pool pool default, // 用于计算 WorkerId 的 Key key RedisSecondMetaGenerator::DEFAULT_REDIS_KEY ], ];各配置项的含义与默认值如下配置项说明默认值begin_secondID 时间戳段的起始秒数ID 的可用时间跨度以此为基准MetaGeneratorInterface::DEFAULT_BEGIN_SECOND即1560960000pool用于计算WorkerId的 Redis 连接池名称defaultkey用于在 Redis 中递增计数、进而分配WorkerId的 KeyRedisMilliSecondMetaGenerator::DEFAULT_REDIS_KEY即hyperf:snowflake:workerId从源码看begin_second在毫秒级生成器中会被乘以1000后作为起始时间戳见 RedisMilliSecondMetaGenerator.php而pool与key的读取逻辑位于 RedisMetaGenerator.php。四、基本使用生成与反解 ID在框架中使用Snowflake非常简单只需从DI容器中取出IdGeneratorInterface对象?php use Hyperf\Snowflake\IdGeneratorInterface; use Hyperf\Context\ApplicationContext; $container ApplicationContext::getContainer(); $generator $container-get(IdGeneratorInterface::class); $id $generator-generate();当需要把ID反解回对应的Meta即还原出时间戳、机器 ID、序列号等信息时调用degenerate即可?php use Hyperf\Snowflake\IdGeneratorInterface; use Hyperf\Context\ApplicationContext; $container ApplicationContext::getContainer(); $generator $container-get(IdGeneratorInterface::class); $meta $generator-degenerate($id);接口契约定义见 IdGeneratorInterface.phpgenerate(?Meta $meta null): int在传入Meta时按指定元信息生成否则使用默认Metadegenerate(int $id): Meta负责反解。底层位运算原理IdGenerator抽象类见 IdGenerator.php通过位移与或运算完成 ID 的组装与拆分生成时间差 时间戳左移位数 | DataCenterId 数据中心位移 | WorkerId 机器位移 | 序列号其中位移量由Configuration根据位段长度计算timestampLeftShift sequenceBits workerIdBits dataCenterIdBits见 Configuration.php反解分别对 ID 做右移运算再结合位掩码^异或从各段中还原出interval、dataCenterId、workerId与sequence最终构造出Meta。Meta类见 Meta.php承载四段元信息取值范围分别为dataCenterId在[0, 31]workerId在[0, 31]sequence在[0, 4095]。其中时间戳与起始时间戳的单位既可以是秒也可以是毫秒由具体生成器决定。Redis 自动分配机器 ID 的机制默认的毫秒级/秒级生成器都继承自RedisMetaGenerator见 RedisMetaGenerator.php。其分配逻辑如下首次使用时通过Hyperf\Coroutine\Locker加锁保证协程环境下只初始化一次从配置中读取pool与key通过RedisProxy执行$redis-incr($key)获取一个全局自增序号按下式计算workerId与dataCenterIdworkerId 序号 % maxWorkerId()dataCenterId intval(序号 / maxWorkerId()) % maxDataCenterId()其中maxWorkerId()、maxDataCenterId()由 Configuration.php 通过-1 ^ (-1 位数)计算得出5 bits 对应最大值 31。这样每个进程首次运行时都会从 Redis 获得一个互不重复的机器标识从而保证分布式环境下的全局唯一性。五、重写 Meta Generator基于 UserId 的变体实现实现分布式全局唯一 ID 的方式很多基于 Snowflake 算法的变体也层出不穷。虽然都是 Snowflake 算法但它们并不完全相同例如有些人会基于UserId而非WorkerId来生成Meta。下面实现一个简单的自定义MetaGenerator由于UserId通常会超过10 bits默认的DataCenterId与WorkerId显然无法容纳因此需要对UserId取模后再放入WorkerId位段。?php declare(strict_types1); use Hyperf\Snowflake\IdGenerator\SnowflakeIdGenerator; class UserDefinedIdGenerator { protected SnowflakeIdGenerator $idGenerator; public function __construct(SnowflakeIdGenerator $idGenerator) { $this-idGenerator $idGenerator; } public function generate(int $userId) { $meta $this-idGenerator-getMetaGenerator()-generate(); return $this-idGenerator-generate($meta-setWorkerId($userId % 31)); } public function degenerate(int $id) { return $this-idGenerator-degenerate($id); } } use Hyperf\Context\ApplicationContext; $container ApplicationContext::getContainer(); $generator $container-get(UserDefinedIdGenerator::class); $userId 20190620; $id $generator-generate($userId);关键点说明SnowflakeIdGenerator见 SnowflakeIdGenerator.php是IdGenerator的直接子类本身不增加额外逻辑通过$this-idGenerator-getMetaGenerator()-generate()先生成一份默认Meta再用setWorkerId($userId % 31)将UserId取模后写入WorkerId段5 bits 最大值 31最后调用generate($meta)按定制元信息产出 ID该示例对应的测试桩可见 UserDefinedIdGenerator.php。六、在数据库 Model 中应用配置好 Snowflake 后可以创建一个以 Snowflake ID 作为主键的数据库模型?php use Hyperf\Database\Model\Model; use Hyperf\Snowflake\Concern\Snowflake; class User extends Model { use Snowflake; }上面的User模型在创建creating时会默认使用 Snowflake 算法生成主键。从 Concern/Snowflake.php 的源码可以看到该 Trait 的具体行为creating()钩子中若当前模型尚无主键! $this-getKey()则从ApplicationContext容器取出IdGeneratorInterface并调用generate()写入主键字段getIncrementing()返回false关闭自增主键getKeyType()返回int声明主键类型为整型。处理 creating 冲突由于 Snowflake Trait 会覆写creating方法而用户可能也需要自定义自己的creating方法此时会出现ID无法生成的问题。解决方式是在类中显式地对 Trait 方法做别名重命名再在自己的creating中手动调用?php use Hyperf\Database\Model\Model; use Hyperf\Snowflake\Concern\Snowflake; class User extends Model { use Snowflake { creating as create; } public function creating() { $this-create(); // 做其他事情 ... } }这样既保留了 Snowflake 自动生成主键的能力又不影响用户在创建模型时追加自定义逻辑。七、小结hyperf/snowflake组件在标准 Snowflake 算法之上提供了清晰的两层抽象MetaGeneratorInterface负责生成携带时间戳与机器标识的MetaIdGeneratorInterface负责把Meta通过位运算组装成最终 ID 并支持反向解析。默认的 Redis 实现通过自增 Key 为分布式节点自动分配WorkerId与DataCenterId保证了全局唯一同时组件的可扩展设计允许你基于UserId、业务分片等维度定制属于自己的 Snowflake 变体并可直接与 Hyperf 的 ORM 模型无缝集成作为主键方案使用。赞分享后端Web框架微服务RPC框架异步编程【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/hyperf/hyperf点击查看免费下载相关推荐Hyperf Snowflake 组件实战分布式全局唯一 ID 生成算法解析与扩展指南Hyperf Snowflake 组件实战分布式全局唯一 ID 生成算法解析与扩展指南 导读 在微服务与分布式系统架构中生成全局唯一、趋势递增的 ID 是订后端Web框架微服务RPC框架异步编程Hyperf Snowflake 分布式 ID 生成器从算法原理到源码级实践指南Hyperf Snowflake 分布式 ID 生成器从算法原理到源码级实践指南 导读 Snowflake雪花算法是 Twitter 提出的分布式全局唯一后端微服务Thunderbolt 重试预算机制详解408/429/5xx 自动重试如何保护你的 AI 对话Thunderbolt 重试预算机制详解408/429/5xx 自动重试如何保护你的 AI 对话 当你在使用 Thunderbolt 这类 AI 桌面应用时人工智能大模型AI 应用本地部署前端后端桌面应用AI Agent上一篇华硕笔记本终极优化指南如何用G-Helper提升性能与续航下一篇解锁NVIDIA显卡隐藏性能Profile Inspector深度调优实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表