尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

使用 Medusa Cache Redis 模块为 Medusa 应用接入 Redis 缓存存储

使用 Medusa Cache Redis 模块为 Medusa 应用接入 Redis 缓存存储 使用 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),仅供参考
返回列表