https://mp.weixin.qq.com/s/k4KuNyIjs2Oxk4bJa03d0A
文章目录
一、需求分析
短链生成及访问需求
短链应用授权管理需求
非功能性需求
二、概要设计
短链服务用例图
整体部署模型
领域设计
短链生成技术方案选择
4.1 单项散列函数生成短 URL
4.2 预生成短 URL
4.3 基于混淆的自增短 URL
三、详细设计
- 混淆自增算法详解
1.1 混淆加密算法设计
1.2 恢复混淆解密算法设计
1.3 算法量级支撑
重定向响应码
高并发挑战方案
API授权校验
四、Web管理后台
五、项目源码
六、思考
图片
一、需求分析
- 短链生成及访问需求
短 URL 生成器,也称作短链接生成器,就是将一个比较长的 URL 生成一个比较短的URL,当浏览器通过短 URL 生成器访问这个短 URL 的时候,重定向访问到原始的长 URL目标服务器,访问时序图如下:
图片
用户访问短URL时序描述:
由应用调用短链服务生成短 URL,并将该短URL 展示给用户
用户在浏览器中点击该短 URL 的时候,请求发送到短 URL 生成器
短URL 生成器返回 HTTP 重定向响应,将用户请求重定向到最初的原始长 URL
浏览器访问长 URL 服务器,完成请求服务
- 短链应用授权管理需求
短链管理,短链系统管理员可以在管理后台做以下操作:
查看已生成的短链列表以及短链的访问次数统计
直接在线输入长URL生成短URL
管理短链系统的授权秘钥(只有已授权的应用才可通过HTTP请求生成短链)
秘钥使用时序图如下:
图片
秘钥使用流程描述:
由管理员在管理后台创建应用秘钥
将秘钥配到应用程序中
应用程序携带秘钥加密后的Token信息请求短链服务生成短URL
短链服务接收到请求时校验Token是否合法,合法则返回相应的短URL
- 非功能性需求
系统需要保持高可用,不因单一服务器宕机而引起服务失效,即需保证服务是支持伸缩的
短 URL 应该是不可猜测的,即不能猜测某个短 URL 是否存在,也不能猜测短 URL 可能对应的长 URL 地址内容
图片
二、概要设计
概要设计阶段主要关注短链服务的用例、部署模型、领域设计及短链生成方案的选择
- 短链服务用例图
图片
用户可以访问短URL,短链服务会将请求进行重定向
应用程序可以通过短链服务为长URL生成唯一的短URL
应用程序可以存储,统计分析短URL的相关数据,比如访问次数
管理员可以在Web后台检索、查看短链的生成及使用情况
管理员可以在Web后台管理授权访问的应用秘钥
- 整体部署模型
短链服务的业务逻辑比较简单,相对比较有挑战的就是高并发的短URL的访问请求如何处理。高并发访问主要通过负载均衡与缓存解决。具体的架构图如下:
图片
- 领域设计
领域设计这块分为两部分,一部分是短链生成记录,另一部分是应用授权
图片
- 短链生成技术方案选择
短 URL 生成器的设计核心就是短 URL 的生成,即长 URL 通过某种函数,计算得到n个字符的短 URL。短 URL 有几种不同的生成算法。由于Base64后面两位的“+”和“/”在 URL 中会被编码为“%2B”以及“%2F”,需要进行再编码,因此不考虑后两位符号,下文皆以Base62编码([A-Z],[a-z],[0-9])作为描述
4.1 单项散列函数生成短 URL
散列函数可以将任意长度的输入(又叫做预映射, pre-image),通过执行复杂的运算,变成固定长度的输出又叫散列值, hash value)。在短URL生成中,散列函数可用于将长URL映射到短URL,从而达到缩短URL的效果。
通常的设计方案:
将长URL作为散列函数的输入
执行散列函数(MD5 或者 SHA256),将长URL转换为固定长度的哈希值
将哈希值进行进一步处理,进行Base62编码,防止冲突
将编码后的字符串截取N个字符作为短URL
具体流程如图:
图片
优点:生成速度快
缺点:存在哈希碰撞,相同的哈希值对应不同的长URL,需要进行特殊处理以避免这一问题。虽然哈希碰撞的概率极低,但是 Base62 编码后再截断的 N 个字符有可能会冲突的概率就很高了。所以在生成的时候,需要先校验该短 URL 是否已经映射为其他的长 URL,如果是,那么需要重新计算(换单向散列算法,或者换 Base62 编码截断位置)。重新计算得到的短 URL 依然可能冲突,需要再重新计算
这样的冲突处理需要多次到存储中查找 URL,无法保证短链服务的性能要求,不考虑该方案
4.2 预生成短 URL
预生成短URL是指提前生成一些短URL并保存到数据库中,当用户需要缩短长URL时,直接从数据库中获取一个未被使用过的短URL,而不是现场生成新的短URL。
通常的设计方案:
采用随机算法离线预生成一批短URL,为了避免短URL冲突,每次生成的时候都需要检查该URL是否已存在
将预生成的短URL存到数据库
从数据库的短URL池中取一部分放到缓存中
当用户需要缩短一个长URL时,缓存中获取一个短URL
当缓存中的短URL快用完时,需从数据库再加载一部分到缓存中
当数据库中的预生成短URL快用完时,需要生成更多的短URL并添加到数据库中
具体流程如图:
图片
优点:由于生成URL是离线计算,不会对服务器性能产生过大的负担,使用的时候读取短URL速度快
缺点:预生成短 URL 会生成大量未被使用的短URL,浪费了存储空间和资源。此外,如果预生成的短URL数量不足,用户可能需要等待新的短URL被生成才能完成操作。越到后期随机算法生成的URL产生冲突的概率会越大,导致需要对比的次数越多
由于需要提前占用大量的存储空间,所以暂不考虑该方案
4.3 基于混淆的自增短 URL
自增短 URL 一种免冲突的算法是用自增长自然数来实现,即维持一个自增长的二进制自然数,然后将该自然数进行 Base62 编码即可得到一系列的短 URL。这样生成的的短 URL 必然唯一,通常采用自增序列号的方式进行生成。原理是将每一个长链接映射到一个唯一的自增序列号,然后将自增序列号转换为指定长度的短链接
比如:
自然数 1 的常规Base62 编码是字符“B”
就可以用 http://1.cn/B 作为短 URL。
弊端:但是这种算法将导致短URL是可猜测的,只要知道了其中一个短 URL,就可以猜测下一个短 URL
为了解决上述问题,基于上面的方式在生成时加入随机打乱编码Base62等混淆方法,可以增加被预测的概率,增强短URL的安全性
通常的设计方案:
初始化一个计数器,用于记录已经生成的短URL数量
当用户输入长URL时,将计数器自增1,并将自增后的值前面补0,比如自然数1变成0001
然后将该值倒转,也就是0001变成1000
为了防止短URL被预测,对转换后的Base62编码进行打乱或加密,以作混淆
将倒转后的值转换为混淆加密后的Base62编码,得到短URL
具体流程如图:
图片
优点:编码保证绝对唯一,不会出现碰撞冲突,生成速度较快,不需要提前占用存储空间
缺点:该方法需要维护一个计数器,如果生成短URL的并发量较大,性能瓶颈取决于计数器,计数器常见的方案:
Redis原子自增
数据库自增主键
我们就是采用了基于混淆的自增短URL,计数器避免引入过多的中间件,因此选择了数据库自增主键
图片
三、详细设计
在详细设计阶段,主要考虑重定向码、高并发场景应对方案、混淆加解密算法的解析及API授权校验的流程
- 混淆自增算法详解
标准Base64编码表如下:
图片
其中“+”和“/”在 URL 中会被编码为“%2B”以及“%2F”,需要进行再编码,因此直接使用标准 Base64 编码进行短URL 编码并不合适,所以,我们需要针对 URL 场景对 Base64 编码进行改造,Base64 编码表中的 62,63 进行编码移除,更新为Base62编码
1.1 混淆加密算法设计
图片
将标准编码随机打乱 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
举例:打乱成:s9LFkgy5RovixI1aOf8UhdY3r4DMplQZJXPqebE0WSjBn7wVzmN2Gc6THCAKhaut
6位长度标准编码与打乱后编码的对应关系
计数器自增Id 标准Base62编码 标准Base62编码(6位字符) 打乱后Base62编码 打乱Base62编码(6位字符)
6 G AAAAAG y sssssy
66 BE AAAABE 9k ssss9k
100 Bm AAAABm 9E ssss9E
可以看出,虽然打乱了,但还顺序性还是很明显
将前面补0再倒转,由于6位长度最大11位,为了避免倒转后超过该数值,因此补到10位
计数器自增Id 打乱后Base62编码(6位字符) 前面补0到10位 倒转数字 倒转后的打乱Base62编码(6位字符)
6 sssssy 0000000006 6000000000 yPFrgP
66 ssss9k 0000000066 6600000000 5xWCQH
100 ssss9E 0000000100 10000000 ssSKph
1.2 恢复混淆解密算法设计
图片
将请求收到的短链Key根据打乱后的Base62编码转成十进制数,补0到10位,然后倒转就得到原来的短链Id
短链Key 解析后的十进制数 前面补0到10位 倒转数字
yPFrgP 6000000000 6000000000 6
5xWCQH 6600000000 6600000000 66
ssSKph 10000000 0010000000 100
1.3 算法量级支撑
短链长度 最大支持数 数据量级
5 99999999 亿
6 9999999999 百亿
7 999999999999 万亿
- 重定向响应码
满足短 URL 重定向要求的 HTTP 重定向响应码有 301 和 302 两种
301 表示永久重定向,即浏览器一旦访问过该短 URL,就将重定向的原始长 URL 缓存在本地,此后不再请求短 URL 生成器,直接根据缓存在浏览器(HTTP 客户端)的长 URL 路径进行访问
302 表示临时重定向,每次访问短 URL 都需要访问短 URL 生成器
一般来说,使用 301 状态码可以降低短链服务器的负载压力,但无法统计短 URL 的使用情况,而短链服务的架构设计完全可以承受这些负载压力,因此短链服务使用 302 状态码构造重定向响应
- 高并发挑战方案
系统调用可以分成两种情况:
应用请求生成短URL的过程
用户访问短URL,通过短链跳转到长URL的过程
情况1:请求生成短URL不会太频繁,因为是手动点击生成的,因此第一点并发量会比较低,生成URL的性能瓶颈取决于计数器
情况2:由于是直接触达客户,一个短链可能被成千上万的客户访问,上述部署模型中也提到,主要通过负载均衡与缓存解决,在此的缓存选择了本地内存缓存,减少与分布式缓存网络请求的链路耗时,用户访问短链的泳道图如下:
图片
具体流程:
用户点击短URL,请求短链服务
短链服务解密短链Key生成短链的自增Id
通过自增Id到缓存中匹配是否有对应的长URL
缓存匹配不到则根据主键Id查询数据库
数据库中如果查不到长URL,则返回HTTP 404响应
数据库中如果查到长URL,则更新缓存,再更新数据库的访问次数,然后重定向到长URL
- API授权校验
授权校验主要做了三重校验:
Body携带的时间戳timestamp必须在60秒内
Body携带的应用Code(app_code)必须是数据库中已存在的
Headers携带的Token必须有效
Token的加密:由时间戳 + 应用秘钥 取大写Md5,具体值为:
$”timestamp={timestampStr}&&app_secret={appSecretStr}”.ToMd5().ToUpper()
具体的授权流程如下:
图片
图片
四、Web管理后台
登录页
图片
短链列表页
图片
在线生成页
图片
授权应用页
图片
图片
图片
图片
五、项目源码
这是一个基于.NET开源的短链生成及监控系统,它包含了在线生成短链、短链跳转长链、支持短链访问次数以及Web监控页面,可以帮助我们更容易地生成短链、监控短链!
https://github.com/Bryan-Cyf/SuperShortLink
欢迎大家提出PR,共同讨论进步