短网址系统可能是最常见的分布式系统设计问题之一了,本身从业务需求上说,读远多过写,而且数据结构确定且简单,数据量小,还易于使用缓存,因此本身难度在分布式系统的问题里面算是比较低的。另外,这个系统本身 “分布式” 的特性也比较弱,而且从组件图的角度来说,没有多少是 “可画的” ,因此之前也就没有介绍它。不过后来我改变想法了,我觉得还是可以总结总结,特别是可以把一些相关的特殊需求考虑进去。
短网址服务就像是 bit.ly 这样的,给一个长长的 URL,它给你吐出一个较短的 URL,往后访问这个 URL 就可以做到 302 重定向到原来那个长 URL 了。
- 图中上半部分是写的部分,无论是 API 直接调用还是通过某一个 UI 去调用,Write API 会进行鉴权操作。另外,如果源 URL 已经是经过短网址服务处理过的,就需要返回失败,否则就陷入了一个递归服务的窘境。
- 其中写部分的 Cache 是用来防止一些过度的访问,比如由于某种原因,短时间内对某一个特定 URL 来生成短网址的请求特别多,那么就可以通过它来发现并阻止。
- Key Generator 用来生成短网址中变化的部分(key),这里面根据不同的需求有几种方法:
- 如果允许按序,那么最简单的方法是使用数据库的 sequence,为了高可用,可以配置多个数据库,step 相同,但起点分散开,比如数据库 A 生成 ID 序列为 1、3、5……,数据库 B 则是 2、4、6……
- 拿到数据库生成的 ID 以后,可以将这个十进制数转成 [a-zA-Z0-9_-] 这样的 64 进制数。
- 如果要求 key 无序,那么可以再根据上述结果加一个算法上的小处理,保证 ID 到最终 key 的一对一映射即可,最简单的方法是单个数或者字符的映射,比如 a->3、b->M(当然,这种方法相对也比较好猜)。
- 如果需要自定义 ID,那么这个 Key Generator 可以接受一个自定义 key 去数据库里面找,找不到就可以用,否则就意味着冲突出现了。
- Key 得到以后,生成的相对路径和原 URL 需要写入 URL DB 中。
- 这里面有一个问题,就是如果两次请求的长 URL 相同,系统应该给出同样的短 URL 还是不同的短 URL?或者说,应该考虑去重吗?一般说来,不应该去重,应为根据短 URL 可以进行许多收费和数据分析,这两个相同的长 URL 来自于不同的用户,如果这里合并去重了就丢失了和用户对应的这部分信息。
- 短 URL 生成以后,这里我还画了一个 Syncer,用来将生成的新映射同步到其它地区的节点去。因为短网址的读的服务如果在本地,那么显然响应速度是更快的,而且也可以减轻中心节点的负担。
- 不同地区的用户,在使用读服务的时候,通过带有缓存的 Read API 来进行,但是具体的地址是根据 DNS 来做均衡,优先使用本地的读服务。
- 关于读服务,还有一个常见问题是,HTTP 状态码应该是 301 还是 302?一般应该使用 302,因为 301 是永久重定向,很明显我们不希望它是永久重定向,而应该是临时重定向,因为永久重定向会丢失很多后续的访问,和前面提到的去重问题,一样不利于收费和数据分析。
这是《常见分布式系统设计图解》系列文章中的一篇,如果你感兴趣,请参阅汇总(目录)寻找你其它感兴趣的内容。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》
这里面有一个问题,就是如果两次请求的长 URL 相同,系统应该给出同样的短 URL 还是不同的短 URL?或者说,应该考虑去重吗?一般说来,不应该去重,应为根据短 URL 可以进行许多收费和数据分析,这两个相同的长 URL 来自于不同的用户,如果这里合并去重了就丢失了和用户对应的这部分信息。
不知道这个不去重的理由,能否再详细解释下呢? 因为其实可以去重,也不影响在去重之前对用户数据的收集,而且如果不去重的话,不是会有很对对应相同长 URL 的短 URL 生成吗? 这是否是一种存储和计算的浪费呢
这要看收集什么数据?
比如某用户使用了这个短网址服务,生成了短 URL,那么一段时间以后,他可以来查看,到底这个短网址的访问情况如何。如果去重了那么这个信息就丢失了,因为用户关心的是他自己生成的链接。
至于存储浪费,就这一些开销而言,实在有些牵强。