外观
Redis
TSystems 使用 Redis 7(Alpine 镜像)作为缓存和分布式存储后端,支持限流计数器、会话管理等场景。
全链路概览
.env 环境变量
→ src/utils/ConstantUtils.mts 常量读取
→ src/utils/RedisUtils.mts 功能封装
→ src/app.mts 应用初始化
→ 各中间件/服务使用1
2
3
4
5
2
3
4
5
生产环境部署链路:
.env 环境变量
→ hosts/Shanghai/redis/scripts/deploy.mts 部署脚本
→ hosts/Shanghai/redis/src/docker-compose.yml Docker 编排1
2
3
2
3
本地开发链路:
.env 环境变量
→ scripts/dev.mts SSH 端口转发隧道
→ 本地 `node --watch` 连接本地转发的 Redis 端口1
2
3
2
3
环境变量
在 .env 中定义:
ini
# Redis 部署密码
REDIS_PASSWORD="REDIS_PASSWORD"
REDIS_PORT="1234"
REDIS_HOST="localhost"1
2
3
4
2
3
4
| 变量 | 说明 | 默认值 |
|---|---|---|
REDIS_PASSWORD | Redis 连接密码 | "" |
REDIS_PORT | Redis 服务端口 | 6379 |
REDIS_HOST | Redis 主机地址 | localhost |
常量定义
src/utils/ConstantUtils.mts 从环境变量读取并拼装连接 URL:
typescript
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || "";
export const REDIS_PORT = process.env.REDIS_PORT || "6379";
export const REDIS_HOST = process.env.REDIS_HOST || "localhost";
export const REDIS_URL = `redis://${REDIS_HOST}:${REDIS_PORT}`;1
2
3
4
2
3
4
REDIS_URL 仅包含 host 和 port,不包含密码。密码通过 REDIS_PASSWORD 单独传入客户端。
服务部署
生产环境使用 Docker 部署 Redis 7 Alpine 容器,部署脚本位于 hosts/Shanghai/redis/scripts/deploy.mts。
部署流程
typescript
import { sshConfig } from "#hosts/Shanghai/nginx/scripts/config";
import { deployRedis } from "#scripts/utils/use-deploy";
import { REDIS_PASSWORD, REDIS_PORT } from "#utils/ConstantUtils";
await deployRedis({
sshConfig,
remotePath: "/etc/redis",
localPath,
password: REDIS_PASSWORD,
port: REDIS_PORT,
});1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
部署命令:
bash
npm run deploy:redis:shanghai:ali1
Docker Compose 配置
hosts/Shanghai/redis/src/docker-compose.yml:
yaml
services:
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
ports:
- "${REDIS_PORT}:6379"
volumes:
- ./redis_data:/data1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
关键配置:
- 持久化:
--appendonly yes启用 AOF 持久化,数据映射到redis_data:/data - 内存限制:
--maxmemory 512mb,配合allkeys-lru淘汰策略 - 密码认证:
--requirepass ${REDIS_PASSWORD}
本地开发 SSH 隧道
当 REDIS_HOST 不是 localhost 或 127.0.0.1 时,scripts/dev.mts 自动建立 SSH 端口转发隧道:
typescript
if (REDIS_HOST !== "localhost" && REDIS_HOST !== "127.0.0.1") {
addTask("sshTunnel", async () => {
const { promise } = runCommandInChildProcess({
appName: "sshTunnel",
command: "ssh",
args: `-L ${REDIS_PORT}:localhost:6379 -p ${SSH_PORT} ${SSH_USERNAME}@${SSH_HOST} -N`,
env: process.env,
});
// ...
});
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
将远程服务器的 6379 端口转发到本地 ${REDIS_PORT} 端口,使本地开发环境可以连接远程 Redis 实例。
功能封装
src/utils/RedisUtils.mts 封装了 Redis 连接管理和常用操作。
连接管理
typescript
import { createClient } from "redis";
import { REDIS_URL, REDIS_PASSWORD } from "#utils/ConstantUtils";
// 懒加载单例
export const getRedisClient = (): RedisClientType => {
if (!redisClient) {
redisClient = createClient({
url: REDIS_URL,
password: REDIS_PASSWORD || undefined,
});
redisClient.on("error", (err) => logger.error("Redis Client Error", err));
redisClient.on("connect", () => logger.info("Redis Client Connected"));
}
return redisClient;
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
连接初始化
typescript
export const connectRedis = async (): Promise<void> => {
const client = getRedisClient();
if (!client.isOpen) {
await client.connect();
}
};1
2
3
4
5
6
2
3
4
5
6
常用操作
| 函数 | 说明 | 签名 |
|---|---|---|
redisSet | 写入键值,支持过期时间 | (key, value, expireSeconds?) |
redisGet | 读取键值 | (key) => string | null |
redisDel | 删除键 | (key) |
redisExists | 检查键是否存在 | (key) => boolean |
应用初始化
在 src/app.mts 中,应用启动时初始化 Redis 连接:
typescript
// 初始化 Redis 连接
await connectRedis();1
2
2
连接成功后,各模块可直接通过 getRedisClient() 获取 Redis 客户端实例进行操作。
使用场景
分布式限流
src/middlewares/RateLimitMiddleware.mts 使用 rate-limit-redis 将限流计数器存储在 Redis 中,实现多实例共享限流:
typescript
store: new RedisStore({
sendCommand: (...args: string[]) => getRedisClient().sendCommand(args),
}),1
2
3
2
3
详见 中间件文档。