基于 Go-Zero 微服务框架构建的高性能短链接生成系统,集成 Bloom Filter、Redis 缓存、Singleflight、分布式锁、Prometheus 监控等生产级特性。
- 短链接生成与解析(支持永久有效、7 天失效、自定义过期时间)
- 管理员手动禁用短链
- Bloom Filter 内存级拦截无效请求,DB 无效查询降低 100%
- Singleflight 合并热点并发查询,防止缓存击穿
- Redis 分布式锁保证转链幂等,防止并发重复发号
- Redis 缓存分层(sqlc 自动缓存 + 手动缓存)
- 请求超时控制、并发限流、优雅关闭
- Prometheus 指标暴露 + OpenTelemetry 链路追踪 + 结构化日志
- MD5 去重 + 62 进制编码 + 违禁词过滤
- RESTful API + gRPC 双协议支持
- Go stdlib 自研压测器,验证 22000+ QPS / P99 < 17ms
| 组件 | 技术选型 | 说明 |
|---|---|---|
| Web 框架 | Go-Zero v1.9.0 | 微服务框架 |
| 通信协议 | gRPC + HTTP | 双协议支持 |
| 数据库 | MySQL | 持久化存储,6 索引 + 2 唯一键 |
| 缓存 | Redis | 序列号生成、分布式锁、sqlc 缓存 |
| 服务发现 | Etcd | 服务注册与发现 |
| 布隆过滤器 | bits-and-blooms/bloom/v3 | 内存级无效请求拦截 |
| 请求合并 | go-zero syncx.SingleFlight | 防止缓存击穿 |
| 分布式锁 | go-zero redis.RedisLock | 转链幂等保护 |
| 监控 | Prometheus + Grafana | 四层全链路指标监控 |
| 链路追踪 | OpenTelemetry + Jaeger | 分布式追踪 |
| 压测工具 | Go stdlib net/http | 自研压测器 |
┌──────────────────────────────────────────────┐
│ Client │
└──────────────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ API Gateway (Go-Zero) :8888 │
│ │
│ ┌─────────┐ ┌──────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐ │
│ │ CORS │→│ 限流(500) │→│ Bloom Filter │→│Singleflt │→│ sqlc Cache│ │
│ │ 中间件 │ │ 信号量 │ │ 拦截无效请求 │ │ 合并查询 │ │ Redis缓存 │ │
│ └─────────┘ └──────────┘ └───────────────┘ └──────────┘ └─────┬─────┘ │
│ │ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │ │
│ │/convert │ │/{shortUrl}│ │/debug/stats │ ▼ │
│ │ 转链接口 │ │ 跳转接口 │ │ 指标端点 │ ┌──────────────┐│
│ │ +Redis锁 │ │ +Bloom │ │ │ │ MySQL ││
│ └──────────┘ └───────────┘ └──────────────┘ │ (持久化) ││
│ └──────────────┘│
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Prometheus :9101 /metrics │ OpenTelemetry → Jaeger │ 结构化日志 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│ gRPC
▼
┌─────────────────────┐
│ RPC Service │
│ (Transform) │
└─────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ MySQL │ │ Redis │ │ Etcd │
│ 持久化 │ │ 序列号 │ │ 服务发现 │
└──────────┘ └──────────┘ └──────────┘
短链跳转的完整查询链路,逐层过滤无效请求:
请求进入
│
├─① 限流中间件 ──── 超过 500 并发 → 503 Server Busy
│
├─② Bloom Filter ── 不存在 → 直接返回 404(0 DB 查询)
│
├─③ Singleflight ── 合并相同短链的并发请求(1 次 DB 查询)
│
├─④ Redis 缓存 ──── 命中 → 直接返回(0 DB 查询)
│
├─⑤ MySQL 查询 ──── 缓存未命中才查库
│
└─⑥ 状态校验 ────── 禁用→403 / 过期→410 / 正常→302 跳转
.
├── api/ # API 网关层
│ ├── etc/ # 配置文件 (Bloom/Prometheus/Telemetry/限流)
│ ├── frontend/ # 前端页面
│ ├── internal/
│ │ ├── config/ # 配置定义
│ │ ├── handler/ # HTTP 处理器 + Debug 端点
│ │ ├── logic/ # 业务逻辑 (Show + Bloom/Singleflight, Convert + Redis锁)
│ │ ├── svc/ # 服务上下文 (Bloom 初始化)
│ │ ├── stats/ # atomic 计数器 (压测埋点)
│ │ └── types/ # 类型定义
│ ├── model/ # 数据模型 (带 DB 查询埋点)
│ ├── pkg/ # 工具包 (62 进制编码、MD5)
│ ├── sequence/ # 发号器 (Redis/MySQL 双实现)
│ └── shorturl.go # 入口 (限流中间件 + 优雅关闭)
├── rpc/ # RPC 服务层
│ ├── transform/ # 短链接转换服务
│ └── proto/ # Protobuf 定义
├── benchmark/ # 压测工具
│ ├── seed/ # 数据灌入 + URL 生成
│ ├── loadgen/ # Go stdlib 自研压测器
│ └── results/ # 压测报告
├── docker/ # 监控部署
│ ├── docker-compose.yml # Prometheus + Grafana
│ ├── prometheus/prometheus.yml
│ └── grafana/dashboard.json # 预置 Dashboard
└── go.mod
- Go 1.24.1+
- Docker(用于 Redis、Etcd、Prometheus、Grafana)
# Redis
docker run -d --name redis -p 6379:6379 redis:latest
# Etcd
docker run -d --name etcd -p 2379:2379 \
-e ALLOW_NONE_AUTHENTICATION=yes \
bitnami/etcd:latest
# Prometheus + Grafana(监控)
cd docker
docker compose up -dCREATE DATABASE IF NOT EXISTS gozero;
USE gozero;
CREATE TABLE `short_url_map` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`create_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`create_by` VARCHAR(64) NOT NULL DEFAULT '',
`is_del` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`status` TINYINT UNSIGNED NOT NULL DEFAULT '1' COMMENT '1启用 2禁用',
`lurl` VARCHAR(2048) DEFAULT NULL,
`md5` char(32) DEFAULT NULL,
`surl` VARCHAR(11) DEFAULT NULL,
`expire_at` DATETIME DEFAULT NULL COMMENT 'NULL=永久有效',
`disabled_at` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `idx_is_del` (`is_del`),
INDEX `idx_status` (`status`),
INDEX `idx_expire_at` (`expire_at`),
UNIQUE KEY `uniq_lurl` (`md5`),
UNIQUE KEY `uniq_surl` (`surl`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `sequence` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`stub` VARCHAR(1) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_uniq_stub` (`stub`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;编辑 api/etc/shorturl-api.yaml,修改数据库密码、Redis 地址等:
ShortUrlDB:
DSN: "root:你的密码@tcp(127.0.0.1:3306)/gozero?parseTime=true"
Redis:
Host: "127.0.0.1:6379"
BloomFilterEnabled: true # Bloom Filter 开关
Timeout: 3000 # 请求超时 3s
MaxConns: 500 # 最大连接数
ShutdownGracePeriod: 5s # 优雅关闭宽限期# 启动 RPC 服务
go run rpc/transform/transform.go -f rpc/etc/transform.yaml
# 启动 API 网关
go run api/shorturl.go -f api/etc/shorturl-api.yaml| 服务 | 地址 |
|---|---|
| 前端页面 | http://localhost:8888 |
| API 接口 | http://localhost:8888 |
| Prometheus 指标 | http://localhost:9101/metrics |
| Prometheus UI | http://localhost:9090 |
| Grafana | http://localhost:3000 (admin/admin) |
POST /convert
Content-Type: application/json
{
"longUrl": "https://github.com",
"expireType": "permanent"
}expireType 支持:permanent(永久) / 7d(7天) / custom(自定义,需传 expireAt)
GET /{shortId}
# 正常 → 302 跳转
# 过期 → 410 Gone
# 禁用 → 403 Forbidden
# 不存在 → 404 Not FoundPOST /admin/disable
Content-Type: application/json
{ "shortUrl": "abc123" }GET /debug/stats # 查看统计计数器
POST /debug/reset # 重置计数器返回 totalRequest、bloomBlocked、dbQuery、cacheHit 四个指标。
- 数据量:1 万条短链
- 压测器:Go stdlib 自研 (net/http + goroutine)
- 机器:20 核 Windows / GOMAXPROCS=20
| 轮次 | Bloom | DB 查询 | BloomBlocked | 降幅 |
|---|---|---|---|---|
| 基线 | OFF | 16,398 | 0 | - |
| 优化 | ON | 6,398 | 99,853 | 无效查询 100% |
Bloom Filter 完美拦截全部 50% 无效请求,有效请求零误判。
| 场景 | QPS | P50 | P90 | P99 | DB 查询 |
|---|---|---|---|---|---|
| 缓存命中热点 | 35,654 | 4.7ms | 9.3ms | 16.6ms | 0 |
| 混合 50% 无效 | 18,425 | 8.4ms | 25.2ms | 42.5ms | 0 |
| 全无效请求 | 39,459 | 2.6ms | 11.9ms | 24.4ms | 0 |
# 灌入测试数据
go run benchmark/seed/main.go -mode seed -count 10000
# 压测(混合模式)
go run benchmark/loadgen/main.go -mode mixed -ratio 0.5 -c 200 -d 30s
# 清理测试数据
go run benchmark/seed/main.go -mode cleanup| 层级 | 指标类别 | 关键指标 |
|---|---|---|
| HTTP 入口 | 请求指标 | QPS、P99 延迟、5xx 错误率 |
| 中间件 | Redis/MySQL 连接池 | 连接池命中率、等待耗时 |
| Go Runtime | 运行时 | goroutine 数、堆内存、GC 耗时 |
| OS 进程 | 系统资源 | CPU 使用率、RSS 内存、FD 使用率 |
预置 Dashboard 已包含 12 个面板,导入方式:
# 方式一:Docker 启动后通过 API 自动导入(已在 docker-compose 中配置)
cd docker && docker compose up -d
# 方式二:手动导入
# Grafana → Dashboards → Import → 上传 docker/grafana/dashboard.json# QPS
sum(rate(http_server_requests_code_total[1m]))
# P99 延迟
histogram_quantile(0.99, sum(rate(http_server_requests_duration_ms_bucket[1m])) by (le))
# GC 平均耗时
rate(go_gc_duration_seconds_sum[1m]) / rate(go_gc_duration_seconds_count[1m])
# goroutine 数量
go_goroutines
MIT License

