因为URL缩短服务的瓶颈在于存储读写和短码生成,而非路由分发;使用net/http可精确控制超时、连接数及HTTP细节,避免框架抽象层掩盖关键性能与安全配置。

为什么直接用 net/http 而不用框架?
因为 URL 缩短服务的瓶颈几乎从不在于路由分发,而在于存储读写和短码生成。用 gin 或 echo 带来的抽象层反而可能掩盖连接复用、超时控制、中间件开销等细节。原生 net/http 让你能精确控制 http.Server 的 ReadTimeout、WriteTimeout 和 MaxConnsPerHost,这对抵御慢速攻击和突发流量更直接。
实操建议:
- 禁用 HTTP/2(除非你明确需要):在
http.Server中设置TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))清空默认映射,避免 TLS 握手后协商 HTTP/2 增加延迟 - 用
sync.Pool复用*bytes.Buffer和 JSON 编码器,尤其在返回 302 重定向或 JSON 响应时 - 不要在 handler 里做任何阻塞 I/O —— 比如调用外部 API、同步写日志文件;这些必须异步化或交由 worker 处理
shorten 接口为何必须幂等且支持自定义短码?
用户粘贴同一个长 URL 多次,应该返回相同的短码,否则会污染统计、破坏分享一致性。同时,运营或 SEO 场景常需指定短码(如 /go/webinar),不能只靠哈希或随机生成。
关键实现点:
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 先查库:用长 URL 的 SHA-256 前 8 字节作为 key 查询缓存(Redis)或 DB 索引,命中则直接返回已有短码
- 再写入:若未命中,生成候选短码(推荐 base62 编码自增 ID,非随机),用
SET short:<code>xxxlong_url EX 3600 NX 原子写入 Redis;失败说明冲突,重试(最多 3 次) - 自定义短码需额外校验:检查
short:<code>user_input是否已存在,且user_input不含路径分隔符、空格、控制字符
302 重定向为何比 301 更合适?
301 Moved Permanently 会被浏览器和 CDN 强制缓存,一旦目标 URL 改动或误配,用户将长期看到错误跳转,排查成本极高。而 302 Found 默认不被缓存(除非显式加 Cache-Control),更利于灰度、A/B 测试和紧急回滚。
注意细节:
- 务必设置
Locationheader 为绝对 URL(如https://example.com/abc123),不能是相对路径,否则某些客户端解析异常 - 响应体可以为空,但必须写入状态码:
w.WriteHeader(http.StatusFound),不能只依赖http.Redirect(它会额外写入 HTML body) - 如果用 Nginx 做前置代理,确认其未覆盖
Locationheader 或强制转为 301
短码生成用自增 ID 还是哈希?
哈希(如 MD5(URL))看似简单,但无法避免哈希碰撞,且无法预估 QPS 下的码空间消耗;而自增 ID(如 PostgreSQL 的 SERIAL 或 Redis 的 INCR)天然唯一、有序、可预测,配合 base62 编码后长度可控(1 亿 ID ≈ 6 位短码)。
真实部署建议:
- 用 Redis
INCR做全局 ID 生成器,比数据库INSERT ... RETURNING更快,也避免事务开销 - ID 初始值避开 0 和小数字(如从 100000 开始),防止生成
a、aa等易被猜解或滥用的短码 - 定期归档老数据(如 90 天无访问的短码),但归档前必须确保无缓存未失效 —— Redis 的
EXPIRE时间要与业务 TTL 严格对齐
最麻烦的其实是短码冲突后的降级策略:比如生成 abc123 失败,该追加时间戳、还是换字符集、还是 fallback 到哈希?这个逻辑一旦上线就很难改,得在第一版就定死。


















