短链服务(TinyURL)
微博和Twitter都有140字数的限制,如果分享一个长网址,很容易就超出限制,发布出去。短网址服务可以把一个长网址变成短网址,方便在社交网络上传播。
https://soulmachine.gitbooks.io/system-design/content/cn/
https://cloud.tencent.com/developer/article/1706838
# 短链长度——7
先说结论:实际生产过程中可以采用长度不超过7的字符串,由大小写字母加数字共62个字母组成的短链组合,下面列出推导过程:
- 存储采用整形:因为有很多字符是不常见的,不利于传播,所以短链一般只使用大小写字母和数字进行存储,所以每一个字符最多有62(26*2+10)种可能。用char来储存的话过于奢侈,所有可以考虑用62进制整形进行存储。
- 存储最多64位整形:当前(2021年11月之前)互联网上的网页总数大概是 60亿(参考 http://www.worldwidewebsize.com (opens new window)),60亿介于 (2^32, 2^33)之间,远小于2^64的上限值,那么用一个64位整数足够了,就可以存储。
- 展示最长11长度的字符串:64位的整形,转化为62进制的长度为
log62(264−1)=10.7
,62进制的每个数字一一映射到62个大小写+数字字符中,所以用11个长度的字符串就可以展示64为的整形短链。 - 展示7长度的字符串合适:用长度为7的字符串可以表示35万亿(62^7=3521614606208)个链接,远大于现在的60亿网页总数。为什么不是6长度,因为6长度只有600多亿个链接,相对于现在60亿链接的长度可以在可预见的未来不被满足使用。
# 长链映射短链——1:N
一个长链应该映射多个短链,判断哪个短链点击的比较多,这样就可以容易进行数据分析。
# 计算短网址——UUID生成器
对于一个长链,如何计算得到和他映射的短链呢?
这里需要的是每次长链到短链的映射都必须生成一个唯一的短链,结合上述**「短链长度」**讨论可以理解为每一次映射都生成一个唯一的整形。
这里需要考虑的是:1. 每个长链可以映射多次得到不一样的结果、2. 每一次映射得到的结果都不一样、3. 结果应该是一个长度可配置的整形、映射过程高效。
如果采用hash映射的话就有哈希冲突的问题,需要消耗额外的成本解决哈希冲突。
可以采用 「分布式ID生成器」 (opens new window)来解决这一问题。
# 如何存储——分布式KV
结合上述**「长链映射多个短链」**,可以设计长链和短链是一对多的映射关系,所有以短链为主键存储长链是一个正确的设计。
使用RDS进行存储可以解决这个问题,但是使用分布式KV数据库来存储这个量级的数据应该是一种更好的选择。
# 重定向状态码——302
一次短链的请求流程:客户端-->发出短链接请求--> 302跳转到--->长连接
为什么重定向请求要采用302呢:
- 301: 代表永久重定向. 也就是第一次拿到请求重定向以后, 下次浏览器再次请求短链接的时候, 不会真正的请求短链接服务器, 而是从浏览器本地的缓存拿到长链接. 这样一来, 通过浏览器本地缓存可以减少服务器的压力, 但是也会带来新的问题, 我们如果想要统计这个入口链接可以带来多少的连接量. 使用301返回, 在server层就无法统计访问量. 所以一般不使用301
- 302: 代表临时重定向, 每次断连请求都会请求短链服务器, 除非在响应头标识了cache control expire ,这样浏览器才会缓存, 这样便于server统计点击数。虽然使用302给短链服务器增加一些压力, 但是数据异常重要的今天, 这点资源还是值得的. 所以,推荐使用302
# 预防攻击——限制IP、长链缓存
如果一些别有用心的黑客,短时间内向TinyURL服务器发送大量的请求,会迅速耗光短链,怎么办呢?
- 首先,限制IP的单日请求总数,超过阈值则直接拒绝服务。
- 用一台Redis作为缓存服务器,存储的不是 短链->长链,而是 长链->短链,仅存储一天以内的数据,用LRU机制进行淘汰。这样,如果黑客大量发同一个长网址过来,直接从缓存服务器里返回短网址即可,他就无法耗光我们的ID了。