缓存
AdonisJS 缓存(@adonisjs/cache)是一个基于 bentocache.dev 构建的简单、轻量级封装,用于缓存数据和提升应用程序性能。它提供了一个简洁统一的 API 来与各种缓存驱动交互,如 Redis、DynamoDB、PostgreSQL、内存缓存等。
我们强烈建议您阅读 Bentocache 文档。Bentocache 提供了一些高级的可选概念,在某些情况下非常有用,例如多层缓存、宽限期、标签、超时、防击穿保护等。
安装
使用以下命令安装和配置 @adonisjs/cache 包:
node ace add @adonisjs/cache:::disclosure
使用检测到的包管理器安装
@adonisjs/cache包。在
adonisrc.ts文件中注册以下服务提供者:ts{ providers: [ // ...其他 providers () => import('@adonisjs/cache/cache_provider'), ] }创建
config/cache.ts文件。在
.env文件中为所选缓存驱动定义环境变量。
:::
配置
缓存包的配置文件位于 config/cache.ts。您可以配置默认缓存驱动、驱动列表及其特定配置。
另请参阅:配置模板
import { defineConfig, store, drivers } from '@adonisjs/cache'
const cacheConfig = defineConfig({
default: 'redis',
stores: {
/**
* 仅在 DynamoDB 上缓存数据
*/
dynamodb: store().useL2Layer(drivers.dynamodb({})),
/**
* 使用 Lucid 配置的数据库缓存数据
*/
database: store().useL2Layer(drivers.database({ connectionName: 'default' })),
/**
* 使用内存作为主存储,Redis 作为辅助存储缓存数据。
* 如果您的应用程序运行在多台服务器上,则需要使用总线同步内存缓存。
*/
redis: store()
.useL1Layer(drivers.memory({ maxSize: '100mb' }))
.useL2Layer(drivers.redis({ connectionName: 'main' }))
.useBus(drivers.redisBus({ connectionName: 'main' })),
},
})
export default cacheConfig在上面的代码示例中,我们为每个缓存存储设置了多个层。这称为多层缓存系统。它允许我们首先检查快速的内存缓存(第一层)。如果在那里找不到数据,我们将使用分布式缓存(第二层)。
Redis
要使用 Redis 作为缓存系统,您必须安装并配置 @adonisjs/redis 包。请参阅此处的文档:Redis。
在 config/cache.ts 中,您必须指定 connectionName。此属性应与 config/redis.ts 文件中的 Redis 配置键匹配。
数据库
database 驱动依赖于 @adonisjs/lucid。因此,您必须安装并配置此包才能使用 database 驱动。
在 config/cache.ts 中,您必须指定 connectionName。此属性应对应于 config/database.ts 文件中的数据库配置键。
此外,在配置 database 驱动时,将发布一个迁移文件到您的 database/migrations 目录,您必须运行它来创建存储缓存条目所需的表。
其他驱动
您可以使用其他驱动,如 memory、dynamodb、kysely 和 orchid。
有关更多信息,请参阅缓存驱动。
使用
配置好缓存后,您可以导入 cache 服务与其交互。在以下示例中,我们将用户详情缓存 5 分钟:
import cache from '@adonisjs/cache/services/main'
import router from '@adonisjs/core/services/router'
import User from '#models/user'
router.get('/user/:id', async ({ params }) => {
return cache.getOrSet({
key: `user:${params.id}`,
factory: async () => {
const user = await User.find(params.id)
return user.toJSON()
},
ttl: '5m',
})
})WARNING
如您所见,我们使用 user.toJSON() 序列化用户数据。这是必要的,因为您的数据必须被序列化才能存储在缓存中。像 Lucid 模型或 Date 实例这样的类不能直接存储在 Redis 或数据库等缓存中。
ttl 定义缓存键的生存时间。TTL 过期后,缓存键被视为过期,下一个请求将从工厂方法重新获取数据。
标签
您可以将缓存条目与一个或多个标签关联,以简化失效操作。条目可以分组到多个标签下,并通过单个操作使其失效,而无需管理单个键。
await cache.getOrSet({
key: 'foo',
factory: getFromDb(),
tags: ['tag-1', 'tag-2']
});
await cache.deleteByTag({ tags: ['tag-1'] });命名空间
另一种分组键的方法是使用命名空间。这允许您稍后一次性使所有内容失效:
const users = cache.namespace('users')
users.set({ key: '32', value: { name: 'foo' } })
users.set({ key: '33', value: { name: 'bar' } })
users.clear()宽限期
如果缓存键已过期但仍在宽限期内,您可以使用 grace 选项允许 Bentocache 返回过期数据。这使 Bentocache 的工作方式与 SWR 或 TanStack Query 等库相同。
import cache from '@adonisjs/cache/services/main'
cache.getOrSet({
key: 'slow-api',
factory: async () => {
await sleep(5000)
return 'slow-api-response'
},
ttl: '1h',
grace: '6h',
})在上面的示例中,数据将在 1 小时后被视为过期。但是,在 6 小时宽限期内的下一个请求将返回过期数据,同时在后台从工厂方法重新获取数据并更新缓存。
超时
您可以使用 timeout 选项配置工厂方法在返回过期数据之前允许运行多长时间。默认情况下,Bentocache 设置软超时为 0ms,这意味着我们始终在后台重新获取数据时返回过期数据。
import cache from '@adonisjs/cache/services/main'
cache.getOrSet({
key: 'slow-api',
factory: async () => {
await sleep(5000)
return 'slow-api-response'
},
ttl: '1h',
grace: '6h',
timeout: '200ms',
})在上面的示例中,工厂方法最多允许运行 200ms。如果工厂方法花费超过 200ms,将向用户返回过期数据,但工厂方法将继续在后台运行。
如果您没有定义 grace 期,您仍然可以使用硬超时在一定时间后停止工厂方法运行。
import cache from '@adonisjs/cache/services/main'
cache.getOrSet({
key: 'slow-api',
factory: async () => {
await sleep(5000)
return 'slow-api-response'
},
ttl: '1h',
hardTimeout: '200ms',
})在此示例中,工厂方法将在 200ms 后停止,并抛出错误。
TIP
您可以同时定义 timeout 和 hardTimeout。timeout 是工厂方法在返回过期数据之前允许运行的最长时间,而 hardTimeout 是工厂方法在被停止之前允许运行的最长时间。
缓存服务
从 @adonisjs/cache/services/main 导出的缓存服务是使用 config/cache.ts 中定义的配置创建的 BentoCache 类的单例实例。
您可以将缓存服务导入到应用程序中并使用它与缓存交互:
import cache from '@adonisjs/cache/services/main'
/**
* 不调用 `use` 方法时,您在缓存服务上调用的方法
* 将使用 `config/cache.ts` 中定义的默认存储。
*/
cache.put({ key: 'username', value: 'jul', ttl: '1h' })
/**
* 使用 `use` 方法,您可以切换到 `config/cache.ts` 中定义的
* 其他存储。
*/
cache.use('dynamodb').put({ key: 'username', value: 'jul', ttl: '1h' })您可以在此处找到所有可用方法:BentoCache API。
await cache.namespace('users').set({ key: 'username', value: 'jul' })
await cache.namespace('users').get({ key: 'username' })
await cache.get({ key: 'username' })
await cache.set({ key: 'username', value: 'jul' })
await cache.setForever({ key: 'username', value:'jul' })
await cache.getOrSet({
key: 'username',
factory: async () => fetchUserName(),
ttl: '1h',
})
await cache.has({ key: 'username' })
await cache.missing({ key: 'username' })
await cache.pull({ key: 'username' })
await cache.delete({ key: 'username' })
await cache.deleteMany({ keys: ['products', 'users'] })
await cache.deleteByTag({ tags: ['products', 'users'] })
await cache.clear()Edge 助手
cache 服务在视图中作为 Edge 助手可用。您可以使用它直接在模板中检索缓存值。
<p>
Hello {{ await cache.get('username') }}
</p>Ace 命令
@adonisjs/cache 包还提供了一组 Ace 命令,用于从终端与缓存交互。
cache:clear
清除指定存储的缓存。如果未指定,将清除默认存储。
# 清除默认缓存存储
node ace cache:clear
# 清除特定缓存存储
node ace cache:clear redis
# 清除特定命名空间
node ace cache:clear store --namespace users
# 清除多个特定标签
node ace cache:clear store --tags products --tags userscache:delete
从指定存储中删除特定缓存键。如果未指定,将从默认存储中删除。
# 删除特定缓存键
node ace cache:delete cache-key
# 从特定存储中删除特定缓存键
node ace cache:delete cache-key storecache:prune
某些缓存驱动,如数据库驱动,不会自动删除过期键,因为它们缺乏原生 TTL 支持。您可以使用 cache:prune 命令手动删除过期键。在支持 TTL 的存储上,此命令不会执行任何操作。
# 从默认缓存存储中清理过期键
node ace cache:prune
# 从特定缓存存储中清理过期键
node ace cache:prune store