所谓的短域名服务,就是处理我们平常经常收到的那种带有短链的短信中的短链的。它具有四个基本的功能:a. 用户点击短域名,服务端接收到请求之后,将其重定向到对应的长域名中;b. 其他的服务通过RPC请求,传一个短域名code,服务端将对应的长域名信息返回;c. 调用方传一个长域名过来,将其转换为短域名,并且返回;d. 记录每个短域名每个小时的点击量数据,并且提供查询接口;
首先,短域名服务需要实现的两个基本的功能分别是:a. 接收一个长域名信息,返回一个标记短域名的code;b. 如果调用方是其他服务,那么其传过来一个短域名code,就需要将相应的长域名信息返回给调用方;c. 客户端直接调用,请求参数就是一个短域名,而服务器则需要将短域名转换为长域名,然后通过一定的方式将客户端页面重定向到长域名。其实这个题目需要做的就是存储长域名和短域名的映射关系,而最简单的映射关系就是“id->url”的这种映射关系,而且id也是唯一的,也就保证了这种映射关系的唯一性。使用id作为映射我们有如下几个点需要注意:
调用方肯定不能直接传一个id过来,因为我们的调用方有一部分直接是用户,可能有人通过id自增的特性,恶意刷我们的接口。这里我们可以对id进行编码,因为url中只直接支持a-zA-Z0-9
这总共62个字符,其他的字符则需要编码,我们可以将id通过base62的方式进行编码,从而得到一个字符串,而且base62编码有一个好处就是,十进制的1亿,编码之后也只有5个字符,因而base62编码能够支持大量的短码,而不用过于担心短码不够的问题;
如果直接采用按顺序a-zA-Z0-9
的base62编码,可能用户根据这个顺序顺推,而恶意刷我们的短码,造成我们的服务器宕机,因而这里我们在使用base62的时候,需要对原始字符进行乱序,然后依据这个乱序来编码和解码操作;
在使用短码查询对应的长域名的时候,我们一般是将数据存储在数据库和redis中,一般的,为了提升效率,查询的时候都会到redis中查询,然后再到数据库查,然后持久化到redis中,当然一般的也都直接能从redis中读取到。但是这里存在一个问题是,如果用户恶意的刷一些无效的短码请求,这些请求都会先去redis中查,redis中没有,然后会去数据库查,从而导致整个服务宕机。这个问题我们有一个解决方案就是,对短码计算一个checksum,比如很简单的,将最后一个字符作为存储checksum的字符,然后将前面几个字符的整型值相加,而最后一个字符用base62编码有62个值,因而前面几个字符的加和然后对62取余,然后经过base62编码的值作为checksum。这样每次接收到查询请求的时候,将code计算一下checksum,判断其与最后一个字符是否相同,不相同的就可以过滤掉了。如下是这种思路下短码的编码方式:
|--------id base62编码--------|---校验和---|
a s d x K 9 1 M
这里还存在一个问题是,如果后台服务器需要对短码进行不兼容的升级,比如这次可以支持8个字符,下个版本可以支持10个字符,这样我们可以做一些优化,或者支持一些新的功能,那么我们就需要对短码加版本号,这里存在一个问题是,这个版本号是直接编码到短码中还是根据短码与长域名的映射关系而存储在表征这个映射关系model的某个字段中。如果只是简单的存储在映射关系的model中,有可能新版本与旧版本生成的短码结果是一样的,此时我们就不知道这个短码是什么版本的了。因而我们将版本号存储在生成的短码中。如下是改进后的短码的编码方式:
|--------id base62编码--------|---版本---|---校验和---|
a s d x K 9 1 M
生成短码还有一个特点是,需要能够生成尽量多的短码,上述改进后的生成方式,我们相当于有两个固定字符分别作为版本和校验和,也就是只剩下6个字符给我们用于标记“id”与“长链接”的映射关系了,虽然也不少,但我们可以更优化一些。我们注意到,版本是很少发生变化的,而校验和是通过对某个数字取余计算得到的,这个数字我们可以缩小一些,也就是说我们可以将版本和校验和压缩到一个字符中。base62表示的62个状态,转成二进制之后有6位,因而我们可以随意使用的只有5位(当然,我们可以更优一点,就是使用6位,只要对应的十进制值不是63和64即可),我们可以将这5位的前两位用于表示版本号,后三位用于存储校验和,也就是说目前,我们最多支持4个版本,而计算得到的校验和是0~7中的某个值,然后将这整个值经过base62编码之后,就得到了短码的最后一个字符。这种处理方式相对于前一个也有一个好处是,对于同一个版本的短码,其每个字符都是不一样的,而前一种编码方式,倒数第二个字符始终是同一个字符,比如版本1。如下是这种短码的编码方式:
|--------id base62编码--------|---校验和+版本---| |--版本--|--校验和--|
a s d x K 9 1 M 0 1 1 0 1
这里我们还需要讨论的一个问题是,用于进行base62编码的id应该如何生成。生成唯一的id很简单,但是如何高效率的生成id则需要做额外的处理。这里我们有如下几种方案:
结合上述的讨论,我们采用的实现方案主要有如下几个部分:
a-zA-Z0-9
数组对生成的id进行编码,生成一个最长7位的字符串;0b111
进行&
操作即可),然后将取余后的结果加上当前版本号(第一个版本值为0),将其结果经过base62编码之后,作为最后一个字符,放到生成的短码最后一位;
|