短地址服务的设计与实现
有不少网站可以提供长链转短链的服务,例如新浪短地址服务、bitly。背后的原理其实是将长链和短链进行一一映射。换而言之,通过各家短链服务商的锻炼和长链是一一对应的。其中的关键点在于如何根据长链生成短链,以及当用户访问短链后怎么处理。
如何生成短地址
比较直观想到的解法是使用哈希函数,但是无论怎样构造哈希函数,都存在哈希碰撞的问题。再次想到碰撞之后可以在hash字符串后面补充其他字符串来实现,但是需要补充多少位呢?这种方案显然不是那么方便。联想到全局唯一,以及数据存储我们很容易想到数据库的自增id,其实这样也是可以的,这里为了实现简单(主要是天然的过期时间),采用了redis来存储数据,其key设计如下:
globalId:全局自增id,用于保证短链的唯一性
shortToLong:短地址到长地址的映射,key为短地址,可以很方便通过短地址找到长地址
longToShortInfo:长链到短链的映射,如果这个键存在值就无需重复生成了.
1 | const Redis = require('ioredis'); |
其实短地址的核心就是将一个地址对应了一个整数,之所以将id转化为base62编码是因为base62将每个短链对应一个62进制的数(数字10个,大小写字母一共52)。这样单个位能表示的信息量就更多了,意味着我们可以用更少的位数表示更大的整数。
如何解析短连接
当收短链转长链的请求的时候去redis中查找短链对应的长链,并且向浏览器发送302跳转。注意不使用301的原因是301是永久重定向,浏览器会缓存(虽然301符合HTTP语义并且对服务器的压力更小),这样我们就没法追踪用户了。
进一步优化
上面的代码的问题是我们的发号器是一个单点。如果要做成分布式就需要多个节点保持同步加1,这个以CAP理论来看本身是不可能真正做到的,况且存在比较大的数据冗余。其实这个问题的解决非常简单,我们可以退一步考虑,我们是否可以实现两个发号器,一个发单号,一个发双号,这样就变单点为多点了?依次类推,我们可以实现1000个逻辑发号器,分别发尾号为0到999的号。每发一个号,每个发号器加1000,而不是加1。这些发号器独立工作,互不干扰即可。而且在实现上,也可以先是逻辑的,真的压力变大了,再拆分成独立的物理机器单元。1000个节点,估计对人类来说应该够用了。如果你真的还想更多,理论上也是可以的。