npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zenweb/cache

v5.6.0

Published

Zenweb Cache module

Readme

@zenweb/cache

基于 Redis 的对象缓存模块,为 zenweb 框架提供缓存能力。

特性

  • 对象自动序列化 / 反序列化(默认 JSON,可自定义)
  • 大对象自动压缩存储(默认 gzip,可自定义)
  • 缓存击穿防护(分布式锁 + 排队等待)
  • 路由级缓存中间件
  • 缓存助手(封装 key 生成、获取、更新、删除)

安装

npm i @zenweb/cache

快速开始

import { create } from 'zenweb';
import modCache from '@zenweb/cache';

create()
  .setup(modCache())
  .start();

默认连接 127.0.0.1:6379,可通过环境变量配置:

REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

基本用法

lockGet — 防击穿缓存获取

最常用的方法。缓存存在直接返回,不存在时自动加锁获取数据并写入缓存,保证并发请求只有一个执行数据获取。

import { $cache } from '@zenweb/cache';

const user = await $cache.lockGet('USER:42', () => db.getUser(42));

set / get — 手动管理缓存

// 写入
await $cache.set('KEY', { foo: 'bar' }, 300); // 300 秒过期

// 读取
const data = await $cache.get<{ foo: string }>('KEY');

// 删除
await $cache.del('KEY');

直接输出压缩数据

对于已压缩的缓存数据(如 gzip),可以跳过解压直接输出给浏览器:

const result = await $cache.lockGet('DATA', fetchLargeData, {
  parse: false,
  decompress: false,
});
if (result.compressed) {
  ctx.set('Content-Encoding', 'gzip');
}
ctx.body = result.data;

路由缓存中间件

使用 cached() 中间件自动缓存控制器返回结果,缓存命中时不会执行控制器方法:

import { Get } from 'zenweb';
import { cached } from '@zenweb/cache';

export class UserController {
  @Get(cached())
  async list() {
    return db.getUsers(); // 结果自动缓存,默认 key 为请求路径
  }

  @Get(cached('USER-LIST'))
  async list2() {
    return db.getUsers(); // 指定固定 key
  }

  @Get(cached(ctx => `USER:${ctx.params.id}`))
  async detail(ctx: Context) {
    return db.getUser(ctx.params.id); // 动态 key
  }
}

lockGet 选项

await $cache.lockGet('KEY', fetch, {
  noWait: true,       // 缓存不存在时立即返回 undefined,后台异步获取
  localStore: {},     // 请求级本地缓存,避免同一请求重复读取 Redis
  preRefresh: 30,     // 剩余 TTL < 30 秒时后台异步刷新
  refresh: true,      // 强制刷新,跳过缓存读取
});

noWait 适用场景: 非关键数据、耗时较长的操作,不希望阻塞请求。

return await $cache.lockGet('STATS', async () => {
  await sleep(3000);
  return getStats();
}, { noWait: true }) || "数据加载中,请稍后重试";

localStore 适用场景: 同一请求内多次访问相同缓存,避免重复网络开销。

const store = {};
// 首次从 Redis 获取并写入 store
const a = await $cache.lockGet('KEY', fetch, { localStore: store });
// 后续从 store 直接返回
const b = await $cache.lockGet('KEY', fetch, { localStore: store });

缓存助手

将缓存的 key 生成、获取、更新、删除封装到一起,适合在 service 层使用:

import { $cacheHelper } from '@zenweb/cache';

const userCache = $cacheHelper('USER', (id: number) => db.getUser(id));

await userCache.get(42);        // key: "USER:42",缓存不存在时自动获取
await userCache.set(user, 42);  // 手动设置
await userCache.del(42);        // 删除

也可以用函数自定义 key 生成规则:

const orderCache = $cacheHelper(
  (userId: number, status: string) => `ORDERS:${userId}:${status}`,
  (userId, status) => db.getOrders(userId, status),
);
await orderCache.get(42, 'active');

自定义序列化与压缩

默认使用 JSON 序列化 + gzip 压缩。成品模块 @zenweb/cache-opt 使用 MessagePack 序列化 + LZ4 压缩,满足大部分场景需求,推荐直接使用:

npm i @zenweb/cache-opt
import { create } from 'zenweb';
import modCache from '@zenweb/cache';
import cacheOpt from '@zenweb/cache-opt';

create()
  .setup(modCache(cacheOpt()))
  .start();

需要自定义 Redis 等配置时,直接传入 SetupOption

app.setup(modCache(cacheOpt({
  redis: { host: '127.0.0.1', port: 6379 },
  set: { ttl: 300 },
})));

如需完全自定义实现(例如 msgpackr + snappy):

import { create } from 'zenweb';
import modCache from '@zenweb/cache';
import { Packr } from 'msgpackr';
import * as snappy from 'snappy';

const SNAPPY_HEADER = Buffer.from('SNAPPY');
const packer = new Packr();

create()
  .setup(modCache({
    serializer: {
      serialize(data) { return packer.pack(data); },
      deserialize(data) { return packer.unpack(data); },
    },
    compressor: {
      isCompressed(data) {
        return SNAPPY_HEADER.equals(data.subarray(0, SNAPPY_HEADER.length));
      },
      async compress(data) {
        const compressed = await snappy.compress(data);
        return Buffer.concat([SNAPPY_HEADER, compressed]);
      },
      async decompress(data) {
        const result = await snappy.uncompress(data.subarray(SNAPPY_HEADER.length));
        return typeof result === 'string' ? Buffer.from(result) : result;
      },
    },
  }))
  .start();

API

Cache 实例方法(通过 $cache 调用)

| 方法 | 说明 | |------|------| | set(key, value, ttl?) | 缓存对象,自动序列化,大对象自动压缩 | | get<T>(key, opt?) | 获取缓存,自动解压反序列化 | | lockGet<T>(key, fetch, opt?) | 防击穿获取,缓存不存在时加锁调用 fetch | | del(key) | 删除缓存 | | ttl(key) | 获取剩余有效期(秒) | | setRaw(key, value, ttl?) | 直接存储 Buffer,不经过序列化 | | getRaw(key) | 直接获取 Buffer,不经过解压反序列化 | | singleRunner(key, run, opt?) | 分布式单例执行器 |

其他导出

| 导出 | 说明 | |------|------| | cached(key?, opt?) | 路由缓存中间件 | | $cacheHelper(key, fetch?, opt?) | 缓存助手工厂 | | Locker | Redis 分布式锁 | | cacheKey(...args) | 缓存 key 生成工具 | | CacheResult | { compressed: boolean, data: Buffer } |

配置项

| 选项 | 默认值 | 说明 | |------|--------|------| | ttl | 60 | 缓存有效期(秒),0 = 永久 | | compressMinLength | 1024 | 最小压缩长度(字节),0 = 不压缩 | | compressStoreRatio | 0.8 | 压缩后需小于原始 × 此比率才存储 | | retryTimeout | 5000 | 获取锁重试超时(毫秒) | | retryDelay | 500 | 重试间隔(毫秒) | | lockTimeout | 10000 | 锁自动释放超时(毫秒) |