Files
Qiniu/technical/xinfra-preview/cachecloud-overview.md
T

378 lines
14 KiB
Markdown
Raw Normal View History

2026-07-04 20:23:48 +08:00
---
tags: [cachecloud, redis, infra, self-hosted]
create time: 2026-07-04 12:00
---
# CacheCloud Redis 管理台
## 概述
CacheCloud(搜狐开源)是一个 **Redis 私有云平台**,支持 Standalone、Sentinel、Cluster 三种架构的一站式高效管理。xinfra 中所有需要 Redis 服务的场景都通过 CacheCloud 进行**统一的实例申请和管理**——开发同学不再需要手动安装部署 Redis,只需在平台上提交工单即可获取可用实例。
核心目标:**降低大规模 Redis 运维成本,提升资源管控能力和利用率**。平台提供快速搭建/迁移、运维管理、弹性伸缩、统计监控、客户端整合接入等功能。
---
## 系统架构
### 整体拓扑
```mermaid
graph TB
subgraph "用户层"
Dev[开发同学 / 运维人员]
end
subgraph "接入层 (Nginx 双机房)"
NG1[Nginx 机房 A]
NG2[Nginx 机房 B]
VIP[Virtual IP → 双向漂移]
VIP --> NG1
VIP --> NG2
end
subgraph "应用层 (Spring Boot)"
CC[CacheCloud Web Server]
DB[(MySQL 元数据)]
AGT_Mgr[Agent 管理器]
ALERT[报警组件<br/>邮件 / 微信 / HTTP]
CUSTOM[自定义扩展模块]
end
subgraph "执行层 (Agent 代理)"
AGT1[Agent 宿主机 A<br/>Redis Standalone / Sentinel]
AGT2[Agent 宿主机 B<br/>Redis Cluster Shard 1]
AGT3[Agent 宿主机 C<br/>Redis Cluster Shard 2]
end
Dev -->|cc.company.com| VIP
VIP --> NG1 & NG2
NG1 & NG2 --> CC
CC <--> DB
CC --> AGT_Mgr
CC --> ALERT
CC --> CUSTOM
AGT_Mgr -->|SSH + 心跳| AGT1 & AGT2 & AGT3
```
> **关键设计决策**:CacheCloud 采用 **Agent 模式**而非直接 SSH 连接 Redis 实例。每个宿主机部署一个 Agent 进程,负责该机器上所有 Redis 实例的生命周期管理(启停、升级、备份恢复)。这样做的好处是 Agent 可复用 SSH 连接、缓存本地状态,大幅减少管理平台到 Redis 主机的网络开销。
### 核心组件
| 组件 | 职责 |
|------|------|
| **Web UI / REST API** | 自助申请实例、配置修改、工单审批、监控大屏 |
| **Agent 代理** | 部署在每个宿主机上,负责 Redis 进程生命周期管理、状态上报、远程命令执行 |
| **元数据库 (MySQL)** | 存储实例拓扑、配置模板、账号权限、工单流转记录 |
| **报警模块** | 内置邮件和微信报警,同时暴露 HTTP 接口供任意语言集成 |
| **扩展模块** | 登录组件 (`LoginComponent`)、报警组件 (`EmailComponent`/`WeChatComponent`) 均可自行实现 |
---
## 实例类型与选型指南
### 三种架构对比
| 维度 | Standalone | Sentinel (Master-Slave) | Cluster |
|------|-----------|------------------------|---------|
| **高可用** | ❌ 单机,无 Failover | ✅ 主从 + Sentinel 自动故障转移 | ✅ 多分片,自动槽迁移 |
| **横向扩展** | ❌ 受单机容量限制 | ❌ 受单机容量限制 | ✅ 新增节点即扩容 |
| **复杂度** | 简单 | 中等 | 较高 |
| **推荐场景** | 测试环境、低频读写缓存 | 生产环境常规业务、内存需求 ≤ 6GB | 大数据量、高并发、内存需求 > 6GB |
> **建议**:并不是 Cluster 越好。如果当前或未来所需内存不超过 6GB 且要求高可用,选择 Sentinel 就足够了。
### 选型决策树
```mermaid
flowchart TD
Start{是否需要高可用?}
Start -->|否: 测试/灰度| Standalone[Standalone]
Start -->|是| NeedScale{是否需要横向扩展?}
NeedScale -->|否≤6GB| Sentinel[Sentinel 主从]
NeedScale -->|是>6GB| Cluster[Redis Cluster]
```
---
## 客户端接入方式
CacheCloud 提供了多种客户端 SDK 及 REST API,覆盖 Java、Python 等主流语言。
### REST API(最通用)
通过简单 HTTP 请求即可获取实例连接信息,适用于任何语言:
```
GET http://{domain}/cache/client/redis/{appType}/{appId}.json?clientVersion={version}
```
**参数说明**:
| 参数 | 含义 | 枚举值 |
|------|------|--------|
| `appType` | 实例类型 | `cluster` / `sentinel` / `standalone` |
| `appId` | 应用 ID | 平台分配的数字 ID |
**响应示例**:
```json
{
"message": "client is up to date, Cheers!",
"shardNum": 10,
"appId": 10192,
"status": 1,
"shardInfo": "10.10.xx.xx:6390,10.10.xx.xx:6382 10.10.xx.xx:6387,10.10.xx.xx:6379 ..."
}
```
> `shardInfo` 字段以空格分隔每个分片,逗号分隔同一个分片的主从地址。解析后即可直连。
### Java — cachecloud-client-redis(Jedis 封装)
```java
@Configuration
public class RedisConfiguration {
@Bean(destroyMethod = "close")
public PipelineCluster pipelineCluster(@Value("${cachecloud.demo.appId}") long appId) {
return ClientBuilder.redisCluster(appId).build();
}
@Bean(destroyMethod = "destroy")
public JedisSentinelPool jedisSentinelPool(@Value("${cachecloud.demo.appId}") long appId) {
return ClientBuilder.redisSentinel(appId).build();
}
@Bean(destroyMethod = "destroy")
public JedisPool jedisPool(@Value("${cachecloud.demo.appId}") long appId) {
return ClientBuilder.redisStandalone(appId).build();
}
}
```
**使用方式**:
```java
@Autowired private PipelineCluster pipelineCluster;
public String get(String key) {
return pipelineCluster.get(key);
}
```
> 所有 `...Pool.getResource()` 使用后必须调用 `jedis.close()` —— 它并非真正关闭连接,而是将连接归还给连接池(内部判断连接是否损坏后决定 `returnResource` 还是 `returnBrokenResource`)。
### Java — cachecloud-client-lettuce(Lettuce 封装)
适合需要异步/响应式场景的应用:
```java
@Bean(destroyMethod = "shutdown")
public RedisClusterClient redisClusterClient(long appId, String password) {
return LettuceClientBuilder.redisCluster(appId, password).build();
}
@Bean(destroyMethod = "close")
public StatefulRedisClusterConnection<String, String> clusterConnection(RedisClusterClient client) {
StatefulRedisClusterConnection<String, String> conn = client.connect();
conn.setReadFrom(ReadFrom.REPLICA_PREFERRED); // 读操作优先走从节点
return conn;
}
```
### Python 接入(REST API 动态拉取连接信息)
```python
import requests
from rediscluster import RedisCluster
app_id = 10192
url = f'http://cc.company.com/cache/client/redis/cluster/{app_id}.json'
resp = requests.get(url).json()
startup_nodes = [
dict(zip(['host', 'port'], addr.split(':')))
for shard in resp['shardInfo'].split(' ')
for addr in shard.split(',')
]
rc = RedisCluster(startup_nodes=startup_nodes, password='your-pass')
```
### 跨机房部署(Cross-Room)
对于容灾要求高的业务,CacheCloud 支持**跨机房双活**:同一业务在两个机房分别部署应用实例,客户端 SDK 自动做双写双读和机房切换。原理是两个 `PipelineCluster` 实例被包装进一个 `RedisCrossRoomClient`。
---
## 系统功能全景
### 用户端功能
| 功能 | 说明 |
|------|------|
| **应用管理** | 查看统计信息、实例列表、应用拓扑、连接信息 |
| **监控面板** | 命令曲线、延迟监控、日报统计 |
| **命令执行** | 在线执行 Redis 命令用于排查 |
| **键值分析** | 分析 bigkey、hotkey 分布 |
### 运维端功能
| 功能分类 | 具体能力 |
|----------|---------|
| **数据统计** | 全局统计、client 统计、server 统计 |
| **工单审批** | 实例申请、配置修改、数据清理等操作需管理员审批 |
| **应用运维** | 应用维度的启停、配置下发、日志查看 |
| **实例运维** | 实例维度的启停、配置查询、数据清理 |
| **数据迁移** | 跨实例数据迁移工具 |
| **诊断工具** | 慢查询分析、连接数诊断等 |
| **模板管理** | 按规格预设 Redis 配置模板(maxmemory、持久化策略等) |
| **任务流** | 编排运维操作流程(如批量升级) |
### 报警配置
CacheCloud 内置的报警覆盖了 Redis 机器级别和实例级别的重要指标,支持的报警渠道:
- **邮件报警** — 默认实现,也可替换为 HTTP 回调
- **微信报警** — 企业微信/钉钉 webhook 风格
报警可通过 HTTP 接口自定义,格式如下:
```
POST www.xxx.com/emailAlert?title=xx&content=xx&receiver=x&cc=x
POST www.xxx.com/weChatAlert?title=xx&message=xx&weChatList=xx
```
---
## 基础运维实践
### maxmemory-policy 策略选择
| 策略 | 行为 | 适用场景 |
|------|------|---------|
| `volatile-lru` | 删除有过期时间的 key,LRU 淘汰 | 默认策略,有 TTL 的缓存 |
| `allkeys-lru` | 对所有 key 做 LRU 淘汰 | 纯缓存场景,不设 TTL |
| `volatile-ttl` | 删除即将过期的 key | 希望按过期时间优先淘汰 |
| `noeviction` | 不淘汰,写操作返回错误 | 需要绝对保证数据完整性 |
> **注意**:修改的配置会对应用的所有节点生效,因为所有节点的配置是统一的。
### Jedis 连接池调优参考
```java
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(DEFAULT_MAX_TOTAL * 5); // 根据实际 QPS 调整
poolConfig.setMaxIdle(DEFAULT_MAX_IDLE * 3);
poolConfig.setMinIdle(DEFAULT_MIN_IDLE * 2);
poolConfig.setMaxWaitMillis(3000); // 连接耗尽时最多等待 3s
poolConfig.setJmxEnabled(true); // 开启 JMX 便于观察
poolConfig.setTestWhileIdle(true); // 空闲时定期检查连接有效性
poolConfig.setTimeBetweenEvictionRunsMillis(60000); // 每分钟检查一次
```
关键字段解读:
| 配置项 | 默认值 | 调优建议 |
|--------|--------|---------|
| `maxTotal` | 8 | 根据峰值连接数估算,通常 × 3~5 |
| `testOnBorrow` | false | 不建议设为 true,会显著增加延迟 |
| `testOnReturn` | false | 一般保持 false,由 `testWhileIdle` 兜底 |
| `whenExhaustedAction` | 1 (阻塞) | 配合 `maxWaitMillis` 使用,避免无限等待 |
---
## 常见陷阱与最佳实践
### 1. Bigkey 的寻找与优化
**什么是 bigkey**:value 所占内存空间较大的 key。字符串类型超过 100KB 即视为 bigkey;非字符串类型(Hash/List/Set/ZSet)则以元素数量过多为准。
**危害**:
- 内存不均匀:Cluster 中造成部分节点内存暴增
- 超时阻塞:Redis 单线程特性下,大 key 操作耗时 > 客户端超时
- 网络拥塞:单次大流量冲击网卡,影响同机其他实例
- 过期删除阻塞:未启用 lazyfree 时阻塞主线程
- 碎片整理冲突:Redis 4.0+ activeDefrag 对超大 key 可能导致周期性延迟
**发现手段**:
| 方法 | 命令 | 特点 |
|------|------|------|
| redis-cli | `redis-cli --bigkeys` | 全量扫描,建议在从节点执行 |
| DEBUG OBJECT | `DEBUG OBJECT key` | 获知序列化长度 `serializedlength` |
| MEMORY USAGE | `MEMORY USAGE key` | 仅 Redis 4.0+,返回精确内存占用 |
| 监控输出缓冲区 | `info clients` 关注 `client_biggest_input_buf` / `client_recent_max_output_buffer` | 间接判断是否存在大 key 读取 |
**优雅删除方案**:
- **String**:直接使用 `DEL`,通常不会阻塞
- **Hash/List/Set/ZSet**:使用 `HSCAN/SSCAN/ZSCAN` 分批获取元素 + `HDEL/SREM/ZREM` 逐个删除,或使用 Redis 4.0+ 的 `UNLINK`(异步删除)
```python
def del_big_hash(r, key):
cursor = 0
while True:
cursor, members = r.hscan(key, cursor, count=100)
if not members:
break
r.hdel(key, *members) # pipeline 批量更高效
if cursor == 0:
break
```
### 2. Hotkey 的处理方向
当某个 key 的访问量远超平均水平时:
- **本地缓存**:在应用侧加一层 Guava/Caffeine 缓存
- **key 拆分**:将一个大 key 拆成多个子 key(如 `user:1001:friends` → `user:1001:friends:1`, `user:1001:friends:2`)
- **读写分离**:读操作通过 Sentinel 路由到从节点
### 3. 实例规格匹配
不要盲目选大规格。建议先通过 Prometheus/Grafana 观察历史 QPS、内存使用和连接数,再确定实例规格。过度配置会在资源看板上体现为浪费。
### 4. 临时 vs 长期实例
- **临时实例**(测试/灰度用)应设置自动回收策略——超出保留期后系统自动销毁
- **长期实例**则需要完善监控、定期巡检配置变更
### 5. 机房就近原则
Redis 对网络延迟非常敏感。跨机房访问比同机房慢数倍,因此应用申请时应填写服务所在机房,确保实例分配到最近的机器。
---
## Redis 版本演进速览
CacheCloud 管理的 Redis 实例可能运行在不同版本,了解各版本的关键特性有助于理解平台行为差异:
| 版本 | 核心新特性 | CacheCloud 相关 |
|------|-----------|----------------|
| **3.x** | 原生 Cluster | 最早支持的 Cluster 形态 |
| **4.0** | Lazyfree 异步删除、AOF/RDB 混合持久化、内存碎片整理 | 支持 UNLINK 删除 bigkey |
| **5.0** | Stream 数据类型、RESP3 协议、Dynamic HZ | 客户端兼容性更好 |
| **6.0** | 多线程 IO、ACL、SSL 加密、协助客户端缓存 | 多线程 IO 提升非 Pipeline 场景 2 倍性能 |
> **启发问题**:为什么 Redis 6.0 的多线程只处理网络 IO 而不涉及命令执行?这背后有什么权衡?
---
## 与 xinfra 的对接方向
进阶目标中提到要「基于 CacheCloud API 实现页面直接创建 Redis Cluster」,这意味着:
1. **阅读 Open API 文档**:梳理可用的创建/配置/查询接口
2. **封装创建流程**:选择规格 → 调用 API → 等待就绪 → 返回连接信息
3. **集成到 xinfra 统一服务管理页面**:让开发者在 xinfra 内即可完成 Redis 实例的全生命周期操作
4. **可选增强**:对接 xinfra 的审批流、用量统计、成本分摊等模块
---
## 关联笔记
- [[technical/xinfra-preview/cloud-dm-overview]] — SQL 审核与 Redis 访问同属数据层基础设施
- [[technical/xinfra-preview/k8s-rke2-fundamentals]] — CacheCloud 的宿主机未来可能迁移至 K8s 部署
- https://github.com/sohutv/cachecloud — CacheCloud GitHub 仓库(含完整 Wiki 和代码)
- https://github.com/antirez/redis — Redis 官方仓库