
使用 Medusa Cache Redis 模块为 Medusa 应用接入 Redis 缓存存储【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa导读medusajs/cache-redis是 Medusa 框架官方提供的缓存模块它基于ioredis将 Redis 接入 Medusa 的模块系统作为 Medusa 应用的缓存存储cache store。本指南将围绕该模块的安装方式、配置选项、源码级实现原理与测试验证展开帮助你理解ttl、redisUrl、redisOptions、namespace四个核心配置项的作用掌握如何在生产环境中用 Redis 替换默认的内存缓存以及模块底层如何通过 SCAN 管道批量失效缓存键。读完本文你将能够独立完成 Redis 缓存模块的接入、配置调优与故障排查。模块概述Redis Cache 模块位于 packages/modules/cache-redis包名为medusajs/cache-redis其 package.json 中声明了唯一的运行时依赖ioredis: ^5.4.1并以medusajs/framework作为 peer 依赖当前仓库版本为 2.20.1Node.js 要求20。从 模块入口文件 可以看到该模块是一个标准的 Medusa 模块定义import { ModuleExports } from medusajs/framework/types import Loader from ./loaders import { RedisCacheService } from ./services const service RedisCacheService const loaders [Loader] const moduleDefinition: ModuleExports { service, loaders, } export default moduleDefinition export * from ./initialize export * from ./types即模块暴露一个RedisCacheService服务类和一个 loader负责建立 Redis 连接并注入依赖容器并通过initialize工具支持编程式初始化。安装在 Medusa 项目中通过包管理器安装该模块yarn add medusajs/cache-redis安装完成后模块有两种接入方式通过 Medusa 配置推荐在medusa-config.ts中通过模块定义注册见下文配置章节。编程式初始化使用 src/initialize/index.ts 导出的initialize函数import { initialize } from medusajs/cache-redis import { Modules } from medusajs/framework/utils const cacheService await initialize({ // 模块选项 })该函数内部通过MedusaModule.bootstrap以Modules.CACHE作为模块键引导加载模块默认解析路径为medusajs/cache-redis返回实现ICacheService接口的服务实例。配置选项模块的完整配置类型定义在 src/types/index.tsexport type RedisCacheModuleOptions { /** * Time to keep data in cache (in seconds) */ ttl?: number /** * Redis connection string */ redisUrl?: string /** * Redis client options */ redisOptions?: RedisOptions /** * Prefix for event keys * default medusa: */ namespace?: string }配置选项速查表选项类型是否必填默认值说明ttlnumber否30秒数据在缓存中的保留时间单次set调用可覆盖redisUrlstring是无Redis 实例连接字符串例如redis://localhost:6379redisOptionsRedisOptions否{}透传给ioredis的客户端选项namespacestring否medusa缓存键前缀最终以medusa:形式生效其中redisUrl是必填项在 loader 实现 中若未提供redisUrl会直接抛出错误if (!redisUrl) { throw Error( No redisUrl provided in cacheService module options. It is required for the Redis Cache Module. ) }RedisOptions类型来自ioredis涵盖 host、port、password、db、tls 等常用连接参数你可以通过该选项传入ioredis支持的全部客户端配置。在 medusa-config.ts 中注册在 Medusa 应用中通过modules配置项将缓存模块指向 Redis 实现以medusajs/medusa/cache-redis或medusajs/cache-redis为标识均可两者都已在类型声明中注册// medusa-config.ts import { defineConfig } from medusajs/framework/utils export default defineConfig({ modules: { cache: { resolve: medusajs/medusa/cache-redis, options: { ttl: 60, // 默认缓存 60 秒 redisUrl: redis://localhost:6379, redisOptions: { password: your-redis-password, db: 0, }, namespace: medusa, }, }, }, })说明modules配置的键名cache对应Modules.CACHE模块键。Medusa 默认使用medusajs/medusa/cache-inmemory作为缓存模块见 packages/core/utils/src/modules-sdk/definition.ts当需要 Redis 缓存时将其替换为medusajs/medusa/cache-redis即可仓库中同样维护了TEMPORARY_REDIS_MODULE_PACKAGE_NAMES映射同文件第 79-84 行为 event-bus、cache、workflow-engine、locking 等模块统一提供了 Redis 版本包名解析。配置项详解ttl以秒为单位的默认存活时间。若单次写入时显式传入ttl会覆盖该默认值传入0表示不缓存。默认值为 30 秒定义于 src/services/redis-cache.ts 的DEFAULT_CACHE_TIME。namespace用于为缓存键添加前缀避免多实例/多应用共用同一 Redis 时发生键冲突。默认前缀为medusa实际生成的键形如medusa:your-key。需要注意原 README 中默认medusa:的描述对应的是加上分隔符后的完整前缀形态源码中以medusa作为namespace值存储redis-cache.ts 第 5 行键拼接逻辑见下文。redisOptions直接透传给ioredis的Redis构造函数loader 第 19-23 行可用于配置密码、TLS、重试策略等。连接加载流程模块的 loader 负责建立与 Redis 的连接并注入依赖容器完整实现位于 src/loaders/index.tsimport { LoaderOptions } from medusajs/framework/types import { asValue } from medusajs/framework/awilix import Redis from ioredis import { RedisCacheModuleOptions } from ../types export default async ({ container, logger, options }: LoaderOptions): Promisevoid { const { redisUrl, redisOptions } options as RedisCacheModuleOptions if (!redisUrl) { throw Error( No redisUrl provided in cacheService module options. It is required for the Redis Cache Module. ) } const connection new Redis(redisUrl, { // Lazy connect to properly handle connection errors lazyConnect: true, ...(redisOptions ?? {}), }) try { await connection.connect() logger?.info(Connection to Redis in module cache-redis established) } catch (err) { logger?.error( An error occurred while connecting to Redis in module cache-redis: ${err} ) } container.register({ cacheRedisConnection: asValue(connection), }) }关键点lazyConnectioredis默认在创建客户端时即尝试连接这里显式设置为lazyConnect: true将实际连接推迟到显式调用connection.connect()时以正确处理连接错误。错误处理连接失败不会抛出异常中断启动而是通过logger.error记录错误RedisCacheService在后续操作中仍可运行ioredis会进入重连流程。依赖注入连接实例以cacheRedisConnection为键注册进容器供RedisCacheService构造函数通过InjectedDependencies注入使用redis-cache.ts 第 9-11 行。优雅关闭服务通过__hooks.onApplicationShutdown在应用关闭时调用this.redis.disconnect()释放连接redis-cache.ts 第 27-31 行。缓存服务核心 APIRedisCacheServicesrc/services/redis-cache.ts实现ICacheService接口。该接口由框架统一定义于 packages/core/types/src/cache/service.ts共三个方法方法签名作用getgetT(key: string): PromiseT \| null按键读取缓存未命中返回nullsetset(key: string, data: unknown, ttl?: number): Promisevoid写入缓存ttl省略时使用默认值invalidateinvalidate(key: string): Promisevoid删除缓存支持通配模式set写入缓存async set(key: string, data: Recordstring, unknown, ttl: number this.TTL): Promisevoid { if (ttl 0) { return } await this.redis.set( this.getCacheKey(key), JSON.stringify(data), EXPIRY_MODE, // EX即过期时间以秒为单位 ttl ) }写入时使用 Redis 的SET key value EX ttl命令过期模式固定为EX秒。ttl 0时直接跳过写入语义上等同于该值不应被缓存源码注释原文If the ttl is 0 it will act like the value should not be cached at all.。值为对象时会被JSON.stringify序列化存储。get读取缓存async getT(cacheKey: string): PromiseT | null { cacheKey this.getCacheKey(cacheKey) try { const cached await this.redis.get(cacheKey) if (cached) { return JSON.parse(cached) } } catch (err) { await this.redis.unlink(cacheKey) } return null }读取时对命中值执行JSON.parse还原对象。若解析失败例如缓存值被外部应用写成了非 JSON 格式会主动unlink该键清除脏数据并返回null避免异常向上传播导致业务失败——这是一种典型的缓存自愈策略。invalidate批量失效async invalidate(key: string): Promisevoid { const pattern this.getCacheKey(key) let cursor 0 do { const result await this.redis.scan(cursor, MATCH, pattern, COUNT, 100) cursor result[0] const keys result[1] if (keys.length 0) { const deletePipeline this.redis.pipeline() for (const key of keys) { deletePipeline.unlink(key) } await deletePipeline.exec() } } while (cursor ! 0) }失效逻辑值得关注支持模式匹配传入ps:*这类通配符模式即可批量失效一类缓存键如所有 price set 相关缓存。使用SCAN游标遍历而非KEYS避免在键数量大时阻塞 Redis 单线程。每批最多扫描 100 个键COUNT 100命中后通过pipelineunlink批量删除兼顾吞吐与原子性。unlink相比DEL是异步删除在大键场景下不会阻塞服务。键前缀拼接private getCacheKey(key: string) { return this.namespace ? ${this.namespace}:${key} : key }所有读写失效操作都经过getCacheKey最终在 Redis 中存储的键为{namespace}:{原始key}。因此默认配置下写入product:123实际对应 Redis 键medusa:product:123。测试验证模块自带的单元测试位于 src/services/tests/redis-cache.js使用 jest mock 掉底层 Redis 客户端验证服务与客户端方法的调用关系const redisClientMock { set: jest.fn(), get: jest.fn(), } it(Underlying client methods are called, async () { cacheService new RedisCacheService( { cacheRedisConnection: redisClientMock }, {} ) await cacheService.set(test-key, value) expect(redisClientMock.set).toBeCalled() await cacheService.get(test-key) expect(redisClientMock.get).toBeCalled() })运行测试yarn workspace medusajs/cache-redis test # 或 yarn test -- packages/modules/cache-redis测试证明了RedisCacheService是一个薄封装set与get只是将ioredis客户端方法包装上序列化、TTL 与命名空间逻辑这使该服务天然易于 mock 与替换。结合 ICacheService 接口任何实现该接口的缓存后端都可以无缝替换 Redis。与其他缓存模块的对比Medusa 同时维护两个缓存模块模块存储介质适用场景medusajs/cache-inmemoryREADME进程内 JSMap测试、开发环境仅支持ttl一个配置项进程重启数据即丢失medusajs/cache-redis本文模块Redis生产环境支持连接串、客户端选项、命名空间可跨实例共享缓存cache-inmemory的 README 明确建议Recommended for testing and development. For production, use Redis cache module.推荐用于测试与开发生产环境请使用 Redis 缓存模块。选择依据很直观内存缓存不占用额外基础设施、零配置但无法在多个服务实例间共享也不具备持久化能力而 Redis 缓存天然支持分布式共享、键模式失效与精细化 TTL 控制更适合多副本部署的生产环境。两个模块的 README 通过Other caching modules章节互相引用可在 cache-inmemory/README.md 与 cache-redis/README.md 之间互相跳转。常见问题排查启动报错 NoredisUrlprovidedredisUrl是必填项检查medusa-config.ts中cache模块的options.redisUrl是否配置正确。连接失败但应用正常启动loader 采用lazyConnect 日志记录策略连接失败只记logger.error不会中断启动。此时应检查 Redis 服务状态、redisUrl可访问性以及redisOptions中的认证/TLS 参数。缓存键冲突多应用共用 Redis 时通过namespace区分默认前缀为medusa:。缓存不生效确认写入时未传入ttl: 00 表示不缓存且未超过默认 30 秒 TTL。想要更精细的过期控制在业务代码调用cacheService.set(key, data, ttl)时显式传入秒级 TTL覆盖模块默认值。总结medusajs/cache-redis以极简的模块形态为 Medusa 提供了生产级缓存能力四个配置项覆盖了连接、过期、命名空间三大核心诉求SCAN pipeline unlink的失效策略兼顾性能与安全性lazyConnect与启动容错保证了部署弹性ICacheService接口则保证了缓存后端的可替换性。无论你是要在多实例部署中共享缓存还是想利用 Redis 的持久化与监控生态该模块都是 Medusa 生产环境缓存接入的标准答案。【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考