
深入解析directus/memoryDirectus 多进程环境下的 KV、LRU Cache、Pub/Sub 与分布式限流抽象层【免费下载链接】directusThe flexible backend for all your projects Turn your DB into a headless CMS, admin panels, or apps with a custom UI, instant APIs, auth more.项目地址: https://gitcode.com/GitHub_Trending/di/directusdirectus/memory是 Directus 专为同一 Directus Project 内多个进程之间同步的短暂存储ephemeral storage而设计的核心抽象包。它把 Key-Value 存储、带 LRU 淘汰的缓存、发布订阅消息总线与共享限流器统一封装成少量可互换的类让你能在本地内存与 Redis 之间一键切换后端。读完本文你将掌握该包导出的createKv、createCache、createBus、createLimiter四个工厂函数的完整用法、所有配置项及其在 Redis 底层的实现机制。一、为什么需要一套内存抽象层Directus 存在各种不同场景的短暂存储需求例如进程间需要共享的临时数据、缓存、消息通知与限流计数。这些数据必须能够在多个进程之间同步同时又要避免每个模块各自对接底层存储、导致代码重复与后端切换困难。该包正是为了收敛这套复杂度而生。根据 packages/memory/src/index.ts 的导出定义它对外统一导出四类能力Cache、Bus、Kv与Limiter其中前三个在包说明文档 packages/memory/readme.md 中被明确列出。每个类都提供local单进程本地内存与redis多进程共享 Redis两种后端Cache 还额外支持multiL1 内存 L2 Redis 的多级缓存因此你可以为单进程开发调试和多进程生产部署分别选择最合适的存储后端而业务代码几乎无需改动。从实现角度观察包的基础设施较为统一所有 Redis 实现都围绕namespace键前缀、Gzip 压缩、分布式锁与序列化这四件事展开公共工具位于 packages/memory/src/utils覆盖序列化、压缩、Buffer/Uint8Array/string 互转与命名空间注入等函数。这样设计的价值在于下层细节只实现一次KV、Cache、Bus、Limiter 共享同一套可靠机制。二、Kv简单可靠的键值存储Kv 是整套抽象中最基础的类——一个简单的键值存储key-value store。它的典型用法如下见 packages/memory/readme.mdimport { createKv } from directus/memory; const kv createKv({ type: memory, // 见下文关于 type 取值的说明 }); await kv.set(my-key, my-value);版本一致性提示包文档示例中的后端取值写作memory而当前源码的类型定义KvConfig实际只接受local | rediscreateKv的分发逻辑create.ts也仅对local与redis分支实例化其余取值会抛出Invalid KV configuration。因此按当前仓库源码为准本地内存后端应传type: local。Kv 的公共接口createKv返回的对象实现了统一的Kv接口定义见 packages/memory/src/kv/types/class.ts方法语义如下方法作用getT(key)按 key 读取值key 不存在时返回undefinedset(key, value)保存任意 JavaScript 原始值、普通对象或数组delete(key)删除指定 keyhas(key)判断 key 是否存在increment(key, amount 1)对存储的数值按给定增量自增并返回新值默认每次加 1setMax(key, value)仅当传入值大于当前存储值时写入返回是否写入成功acquireLock(key)获取一把可release/extend的分布式锁usingLock(key, callback)持锁执行回调的便捷封装结束后自动释放clear()清空该 storeRedis 后端仅清空本 namespace 下的键其中get/set/delete/has/increment是基础的存储能力而setMax、acquireLock、usingLock是针对缓存服务共享场景的强化能力例如多个进程并发更新同一指标时可借助它们保证原子性。配置项详解Kv 的配置按后端划分完整定义见 packages/memory/src/kv/types/config.tsKvConfigLocaltype: local参数说明maxKeys?store 中允许的最大 key 数量ttl?过期时间单位为毫秒到期后自动清除 keyKvConfigRedistype: redis参数默认值说明namespace必填所有 key 的前缀用于多实例/多用途隔离compression?true是否启用 Gzip 压缩compressionMinSize?1000触发压缩的字节下限低于约 1KB 的值压缩收益不大the juice isnt worth the squeezelockTimeout?5000请求分布式锁的时长毫秒ttl?无key 过期时间毫秒redis必填ioredis 连接实例可为已有连接Redis 后端的底层机制KvRedis的实现packages/memory/src/kv/lib/redis.ts集中体现了整个包的工程细节数值单独走 Redis 原生命令当set写入的值是number时直接使用 Redis 的SET可叠加PXTTL只有非数值才走序列化 可选压缩 Buffer 写入的路径。这保证了increment、setMax这类数值运算能作用于真实数值。序列化与压缩写入时先用serialize转成二进制若开启压缩且字节数 ≥compressionMinSize则 Gzip 压缩读取时通过头部标记判断是否压缩并自动解压compress.ts 等工具实现。分布式锁基于 Redlock使用sesamecare-oss/redlock内部构造参数为retryDelay: 50、driftFactor: 0.01、retryCount: 100、retryJitter: 20兼顾了重试强度与时钟漂移容忍度。Lua 脚本实现原子操作setMax与release分别是注入到 ioredis 上的自定义命令源码中内嵌了SET_MAX_SCRIPT与RELEASE_SCRIPT两段 Lua——前者实现仅当新值更大才覆盖的原子比较后者实现仅当持有者匹配才释放的安全解锁。删除使用UNLINK相比DEL对大批量 key 的阻塞风险更低clear()则通过scanStream按 namespace 前缀匹配后分批pipeline.unlink清理避免KEYS *在数据量大时阻塞 Redis。三、Cache在 Kv 之上叠加 LRU 淘汰createCache创建的 Cache 类本质上是Kv 类扩展了 LRULeast Recently Used存储的结果依赖 lru-cache。也就是说它在保留 Kv 全部方法语义的同时额外增加了容量上限约束——当 key 数量超过上限时最久未使用的 key 会被自动淘汰从而把内存占用控制在固定水位。基本用法packages/memory/readme.mdimport { createCache } from directus/memory; const cache createCache({ type: memory, maxKeys: 500, }); await cache.set(my-key, my-value);Cache 的配置同样定义在后端相关的联合类型中CacheConfig但它支持三种后端type说明local纯本地内存缓存参数含maxKeys?与ttl?毫秒redis纯 Redis 缓存参数与 KvRedis 对齐namespace、compression、compressionMinSize、ttl、redismulti多级缓存local作为 L1 内存层redis作为 L2 共享层二者配置分别通过local与redis两个字段传入各为该类型的type之外其余字段multi是 Cache 相对 Kv 的最大增量请求先命中进程内 L1未命中才回源 L2 Redis兼顾了单机热数据的访问速度与跨进程的一致性。这一能力也解释了为什么在 API 侧默认内存缓存与 Redis 缓存可以按环境灵活切换——对应的装配逻辑可参考仓库中的 api/src/cache.ts 及其缓存控制中间件 api/src/middleware/cache.ts。四、Bus轻量级的发布订阅抽象Bus 是一个 pub/sub发布订阅抽象用于在 Directus Project 的多个进程之间广播消息。文档对本地类型的定位表述得很直白memory 类型的 bus 只维护本地处理器本身并不比直接共享一套 pub/sub API带来更多价值——也就是说local Bus 的唯一意义在于让代码在不同部署形态下使用统一的 API 形态真正的跨进程价值体现在 Redis 后端。基本用法packages/memory/readme.mdimport { Redis } from ioredis; import { createBus } from directus/memory; const bus createBus({ type: redis, redis: new Redis(), namespace: directus, });配置类型BusConfig为local | redis两分支type配置要求说明local无额外必填项仅面向单进程实例本地处理器直连redisnamespace必填、redis必填可选compression/compressionMinSize消息经 Redis 通道广播支持 Gzip 压缩key 统一加 namespace 前缀与 Kv/Cache 一样Bus 的 Redis 后端也复用了压缩与 namespace 基础设施。在 Directus 中这类跨进程事件通道与消息同步逻辑被进一步组织在api/src/bus等模块见 api/src/bus/index.ts对外提供与进程无关的订阅能力。五、Limiter多进程共享的基础限流器Limiter 是一个共享的基础限流器用来在多进程之间统一追踪某段时间内允许的操作额度。其核心配置是两个必填数值points时间窗内允许的点数/次数与duration时间窗长度。基本用法packages/memory/readme.mdimport { createLimiter } from directus/memory; const limiter createLimiter({ type: memory, points: 10, duration: 5, });即每 5 个单位时间内最多放行 10 次操作超出的请求会被判定为超限。当前包配置类型LimiterConfig同样分为local与redis两分支且二者都要求同时提供points与durationtype附加必填适用场景local无仅面向单进程实例redisnamespace、redis多进程共享同一份额度计数配套的额度消耗逻辑被抽在 packages/memory/src/limiter/utils/consume.ts 中含独立测试可复用于不同的调用方。从仓库结构可以推断这类限流能力会被上层 API 的限流装配例如 api/src/rate-limiter.ts 及 api/src/middleware 下的限流中间件按单机/分布式需求选用。六、源码结构导览与使用建议如果你希望深入阅读或二次扩展建议按以下路径组织阅读公共接口与分发packages/memory/src/index.ts顶层导出→ 各域的lib/create.ts工厂分发与types/config.ts配置联合类型单进程后端kv/lib/local.ts、cache/lib/local.ts、bus/lib/local.ts、limiter/lib/local.tsRedis 后端kv/lib/redis.ts、cache/lib/redis.ts、bus/lib/redis.ts、limiter/lib/redis.ts公共工具packages/memory/src/utils涵盖serialize、compress、withNamespace、Buffer/Uint8Array 互转等针对选 local 还是 redis给出如下工程判断这与包设计意图一致单进程开发与测试选local含 Cache 的纯内存模式即可零外部依赖、延迟最低多进程或分布式部署选redis以 namespace 隔离不同用途的键并共享状态读多写少、进程内热数据命中率高的缓存优先考虑 Cache 的multi两级模式用 L1 内存承接热点读、用 L2 Redis 兜底一致性。七、注意事项小结单位约定所有ttl、lockTimeout均以毫秒计而 Limiter 的duration与points属业务自定义单位需调用方约定统一。文档与源码的取值差异readme 示例中的type: memory是历史/教学式写法当前 源码类型定义 实际使用local以源码为准可避免工厂函数抛出配置错误。压缩阈值权衡compressionMinSize默认 1000 字节是包作者明确的取舍结论——小值不值得为压缩付出 CPU 时间。锁的适用边界分布式锁由 Redlock 提供适用于短临界区长任务请通过extend续期或拆分持有时长。综上directus/memory以少而统一的抽象把 Directus 在短暂存储、缓存、消息与限流上的多进程诉求收敛到同一套 API 之下让开发者在一个配置文件字段间切换存储后端即可完成从单进程原型到多进程生产环境的平滑演进。【免费下载链接】directusThe flexible backend for all your projects Turn your DB into a headless CMS, admin panels, or apps with a custom UI, instant APIs, auth more.项目地址: https://gitcode.com/GitHub_Trending/di/directus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考