短链的思考

3 篇文章 0 订阅

背景

周末在和同学吃饭的时候提到了短链,短链不是我们常说的http长链接、短链接,而是一个短url。在日常生活中其实经常能看到短链,比如收到推广短信里面的链接,为了节省费用,往往都是以短链形式存在。因此,短链是一个非常有意义的服务。

那么,如果要设计一个短链系统,需要考虑哪些东西呢?

需求

作为短链,最基本的要求就是要能跳转、跳转正确了,因此第一个需求,短链需要保证不重复。

再提高些要求,点击短链后会去一个目的地址,这个跳转要快一些,不然用户等待久了也会失去耐心,因此第二个需求是短链跳转要低延迟。

再讲究点,短链的生成也要快,不然生成的慢也影响发放效率。因此,对于一个短链服务,目标大概如下:

  • 短链不存在重复
  • 短链跳转低延迟
  • 短链生成低延迟

实现

1. 短链不重复

说起不重复,我们往往会想到uuid、哈希、雪花算法,这些其实也对应业界常用的方案。

哈希:

生成长链对应的哈希值(可通过64Bit MurmurHash,url签名冲突率非常低),进行编码后即可得到短链。但是需要注意,哈希算法都存在collision问题,我们可通过简单的手段避免。如在db中存储长短链对应关系时,因为短链是唯一的,因此之前若不存在该key,则可直接插入,若之前已存在会出现插入失败,此时可在长串上拼接一个随机数,然后重新进行哈希,可解决问题。

发号器:

大部分公司应该都会有发号器,当收到一个长链转短链的请求时,请求发号器服务,获取一个号,编码后得到短链。

脱离使用场景、规模做服务都是扯淡,因此需要结合使用场景,若公司规模较小,仅仅是为了缩减短信费用,直接使用mysql的自增索引。若使用场景非常广,可考虑各种分布式key-value系统,如可以部署10台Redis,每台分别负责号段尾号为0-9的发号,此时发号器的步长就为10。

2. 短链生成低延迟

如果想要更快的生成短链,最好的方法是在使用时已经事先生成好,因此可以通过预生成短链key的方式,提前生成50个随机的6位字符串放入池子中,当需要生成短链时,直接从池子中取出一个,用完后再补充一个进入池子中。

若生成时,当前预先生成的池子中没有生成好的,可以参考这篇文章中的做法:How to generate a random string of a fixed length in Go? - Stack Overflow

var src = rand.NewSource(time.Now().UnixNano())

func RandStringBytesMaskImprSrc(n int) string {
    b := make([]byte, n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }

    return string(b)
}

 

几个已改进点:

  • 余数改进:若用 rand.Intn() 生成随机字符,rand.Intn() 实际是委托调用的 Rand.Intn() ,最终调用的 Rand.Int31n() 实现,可以换成rand.Int63()  来提升效率。为了不超过letterBytes的索引范围,用余数保证 letterBytes[rand.Int63() % int64(len(letterBytes))]
  • Masking掩码方式:62的二进制是 111110 ,可以只用rand.Int63()返回的最低6位数;为了平均概率,若值大于 len(letterBytes)-1 则舍弃不用
  • Masking掩码改进:rand.Int63() 一次可以生成63个随机数的位,若只用最低6位,有点浪费。实际上一次生成,可以用于 63/6=10 次

其他优化:

对于一个请求,有可能请求中已经携带了业务自定义的短链地址,可以设置一个自定义的正则表达式,匹配该url,若匹配成功则添加到db中。在添加到bd时需要判断db中是否已有此短链,不存在该短链则直接插入db中,否则应该抛出一个固定的错误码。若未匹配该正则表达式,则需要获取短链,按照上述步骤获取即可。

3. 短链跳转

短链的跳转需要在slb上配置特定的path,通过前缀匹配将请求转发到短链服务中。

因此,短链服务可能非常容易遭受非业务请求的攻击,需要加以防范:

  • 添加过滤规则:过滤一定不合法的短链,如path长度为0的,path以非业务前缀开头的...
  • 添加缓存,从缓存中获取短链对应的长链
  • 匹配到预设短链的前缀时,直接拼接返回长链

预设短链prefix,对于一些特定的场景,如B站的稿件,通常都以/bvxxx开头,因此无需请求短链服务生成随机的key,当识别到短链匹配预设的正则时,直接拼出对应长链即可,无需查询。

说到短链跳转,不可避免的会提到301与302。301和302是2个http code,对应301-永久重定向、302临时重定向。

我们应该使用302而不是301,因为301是永久重定向,此时浏览器会直接显示真实地址,我们就无法统计短链接的点击次数等信息,对于一个公司来说数据往往是命脉,因此宁可增加服务的压力,也需要保留统计数据的能力。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值