news 2026/3/8 5:49:09

2024年毕设系列:从零实现一个高可用的短链服务——技术选型与工程实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2024年毕设系列:从零实现一个高可用的短链服务——技术选型与工程实践


2024年毕设系列:从零实现一个高可用的短链服务——技术选型与工程实践


一、毕设常见痛点:为什么“短链”成了救命稻草

做毕设最怕两件事:老师一句“功能太单薄”,评委一句“代码像作业”。很多同学把商城、博客、聊天室翻来覆去写,最后落个“CRUD 堆砌”的评价。短链服务好在三点:

  1. 场景真实:面试常问,生产常用,简历能写。
  2. 技术纵深:ID 生成、缓存、防刷、302 性能、高可用,每点都能展开。
  3. 体量可控:两周可跑通主流程,四周能打磨到“能上线”的级别。

把短链做成毕设,既能让老师看见“分布式”影子,又不至于被微服务洪流拖垮,性价比极高。


二、需求拆解:高并发302、唯一ID、防滥用

先列三条最硬的核心指标,后面所有选型都围绕它们打分。

  1. 302 重定向 QPS ≥ 3w:校内毕设答辩现场可模拟,用wrk一把梭就能出报告。
  2. ID 必须全局唯一且短:8~62 进制字符串,长度 ≤ 7 位,否则“短”链不短。
  3. 防刷:同一 IP 1min 内 > 60 次请求或 1 天 > 1000 次,直接 429,不废话。

把需求拆成表,后面技术选型直接打分,省得拍脑袋。


三、技术选型对比:Redis vs. DynamoDB,Snowflake vs. HashID

3.1 存储:Redis 还是 DynamoDB?

维度Redis(内存 + RDB)DynamoDB(On-Demand)
延迟0.3 ms5~8 ms
成本内存贵,学校实验室白嫖免费 tier 足够,毕设零元购
运维需自己配主从、哨兵AWS 全托管,毕设省心
数据面代码量少,Lua 脚本即可多,要封装 SDK

结论

  • 实验室有旧服务器可白嫖 → Redis
  • 想直接上线放简历链接 → DynamoDB

下文代码同时给出两套实现,切换只需改一行配置。

3.2 ID 生成:Snowflake vs. HashID

维度Snowflake(64 bit)HashID(salt + 自增)
有序时间有序,可排序依赖 DB 自增,也有序
长度19 位十进制可配置 7 位 62 进制
时钟回拨致命,需 NTP 校准无,依赖 DB 即可
实现复杂度中等,位运算低,调库即可

结论

  • 想炫技、讲位运算 → Snowflake
  • 求稳、两周交差 → HashID

下面代码把两种策略都包成IDGen接口,随时换刀。


四、核心代码:Go 版与 Python 版对照

4.1 Go 版(Redis + Snowflake)

package main import ( "context" "errors" "fmt" "net/http" "strconv" "time" "github.com/go-redis/redis/v8" "github.com/sony/sonyflake" ) var ( rdb *redis.Client sf *sonyflake.Sonyflake ctx = context.Background() ) func init() { rdb = redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"}) sf = sonyflake.NewSonyflake(sonyflake.Settings{}) // 默认本机时钟 } // 1. 生成短码 func createShortURL(longURL string) (string, error) { id, err := sf.NextID() if err != nil乃至于时钟回拨 { return "", fmt.Errorf("snowflake error: %w", err) } code := base62Encode(id) // 7 位 err = rdb.Set(ctx, code, longURL, 24*30*time.Hour).Err() return code, err } // 2. 302 重定向 func redirect(w http.ResponseWriter, r *http.Request) { code := r.URL.Path[1:] longURL, err := rdb.Get(ctx, code).Result() if err == redis.Nil { http.NotFound(w, r) return } if err != nil { http.Error(w, "internal error", 500) return } http.Redirect(w, r, longURL, http.StatusFound) } func main() { http.HandleFunc("/", redirect) http.HandleFunc("/create", func(w http.ResponseWriter, r *http.Request) { long := r.FormValue("url") code, err := createShortURL(long) if err != nil { http.Error(w, err.Error(), 500) return } fmt.Fprintf(w, "https://s.example.com/%s", code) }) http.ListenAndServe(":8080", nil) }

4.2 Python 版(DynamoDB + HashID)

import os, boto3, hashids from flask import Flask, redirect, request, abort app = Flask(__name__) table = boto3.resource('dynamodb', region_name='ap-southeast-1').Table('short_url') hasher = hashids.Hashids(salt=os.getenv("HASH_SALT", "demo"), min_length=7) # 1. 生成短码 @app.route("/create", methods=["POST"]) def create(): long_url = request.form["url"] # 用 DynamoDB 原子计数器 resp = table.update_item( Key={"pk": "counter"}, UpdateExpression="ADD seq :1", ExpressionAttributeValues={":1": 1}, ReturnValues="UPDATED_NEW", ) seq = resp["Attributes"]["seq"] code = hasher.encode(seq) table.put_item(Item={"code": code, "long": long_url}) return f"https://s.example.com/{code}" # 2. 302 重定向 @app.route("/<code>") def short(code): resp = table.get_item(Key={"code": code}).get("Item") if not resp: abort(404) return redirect(resp["long"], code=302) if __name__ == "__main__": app.run(port=8080)

两段代码都保留最简错误处理,方便读者先跑通再逐步加料。


五、压测结果与安全加固

5.1 压测环境

  • 4C8G 笔记本,Docker 限 1G 内存
  • wrk -t4 -c100 -d30s https://s.example.com/xxxxxx
方案QPSP99 延迟备注
Go+Redis3.2w9 msCPU 70%,网卡先顶满
Py+DynamoDB1.1w28 ms受限于 AWS 延迟,可接受

5.2 安全三板斧

  1. 限流uber-go/ratelimit令牌桶,1min 60 个令牌,超了直接 429。
  2. Referer 校验:只允许白名单域名调用/create,防止被当公共图床。
  3. 缓存穿透:Redis 层布隆过滤器,不存在短码直接返回 404,避免打 DB。

六、生产环境避坑指南

  1. Snowflake 时钟回拨
    部署前跑ntpdate,仍回拨就抛异常降级到 HashID,保证链路可用。

  2. 缓存穿透
    空对象也缓存 5min,值写"NIL",客户端见"NIL"直接 404。

  3. DynamoDB 热分区
    短码做code主键,随机性足够,一般无热点;若仍出现,把表开 Adaptive Capacity。

  4. Go map 并发写
    全局计数器别用map,用sync.Map或干脆扔 Redis。


七、把短链做成可插拔组件

整个服务被拆成三块:

  • idgen:接口化,Snowflake/HashID 随换。
  • storage:抽象Get/Set,Redis、DynamoDB、MySQL 都能插。
  • limiter:令牌桶、漏桶、滑动窗口,各实现一个Allow() bool即可。

毕业设计答辩完,把仓库公开,README 里留一张“架构插拔图”,面试官一看就懂:这同学不是堆功能,而是面向接口设计。后续想加“二维码生成”、“自定义域名”、“访问统计”,直接写新插件,不碰老代码。



八、小结与下一步

两周时间,先把 302 跑通;再花一周加布隆过滤器、限流、单元测试;最后一周写压测报告和 PPT,毕业设计稳稳过关。代码已开源在 GitHub,欢迎提 PR 一起把idgenstoragelimiter继续拆成独立子模块,让短链服务成为真正的“毕设脚手架”。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/7 23:25:18

如何让stress-ng在ARM设备上高效运行?嵌入式系统压力测试指南

如何让stress-ng在ARM设备上高效运行&#xff1f;嵌入式系统压力测试指南 【免费下载链接】stress-ng-arm 项目地址: https://gitcode.com/gh_mirrors/st/stress-ng-arm 在嵌入式开发中&#xff0c;你是否遇到过这样的问题&#xff1a;x86架构下编译的stress-ng无法直接…

作者头像 李华
网站建设 2026/3/6 12:59:43

PKGi PS3完全攻略:革命性工具解放双手的PS3游戏管理新方式

PKGi PS3完全攻略&#xff1a;革命性工具解放双手的PS3游戏管理新方式 【免费下载链接】pkgi-ps3 A PlayStation 3 package download tool 项目地址: https://gitcode.com/gh_mirrors/pk/pkgi-ps3 作为PS3玩家&#xff0c;你是否还在为游戏安装包在电脑和主机间的来回传…

作者头像 李华
网站建设 2026/3/4 13:59:01

自动化签到解决方案:qd-templates的技术实现与应用指南

自动化签到解决方案&#xff1a;qd-templates的技术实现与应用指南 【免费下载链接】qd-templates 基于开源新版签到框架站发布的公共har模板库&#xff0c;整理自用 qiandao 框架可用的各种网站和App的 Har 模板&#xff0c;仅供学习参考。 项目地址: https://gitcode.com/g…

作者头像 李华
网站建设 2026/3/7 16:27:10

5步破解企业数据孤岛:零代码实现多系统数据自动化整合

5步破解企业数据孤岛&#xff1a;零代码实现多系统数据自动化整合 【免费下载链接】n8n n8n 是一个工作流自动化平台&#xff0c;它结合了代码的灵活性和无代码的高效性。支持 400 集成、原生 AI 功能以及公平开源许可&#xff0c;n8n 能让你在完全掌控数据和部署的前提下&…

作者头像 李华
网站建设 2026/3/7 16:31:13

3D纹理处理效率提升方案:TexTools插件全解析

3D纹理处理效率提升方案&#xff1a;TexTools插件全解析 【免费下载链接】TexTools-Blender TexTools is a UV and Texture tool set for 3dsMax created several years ago. This open repository will port in time several of the UV tools to Blender in python. For more …

作者头像 李华