前言
迫于业务需求和某些限制,不得已需要造一个轮子短链接系统。该类系统目前市面上有开源解决方案YOURLS,不过该系统是PHP版本,考虑后期维护的成本,还是决定自己开发。
目前业务需求有:
- 长链接转换为短链接
- 自定义短链接
- 短链接访问量统计
技术
后端:Egg.js + Oracle + redis
前端:暂无(该系统直接输出API,链接创建等操作在其他系统进行)
实现
长链接转换为短链接
这是短链接系统的基本需求,考虑到具体业务需求,长链接和短链接并非一一对应的关系,也就是同一个长链接每次转换都会生成不同的短链接。
数据库中每个链接有5个关键字段:
-
短链接前缀(我司目前有不止一个域名)
-
短链接ID(自增)
-
自定义链接(用户自行填写)
-
hash(根据ID转换得到)
-
PV(链接访问量)
-
STATUS(链接状态,启用/禁用)
在Oracle数据库中创建自增ID,给每一个长链接分配一个ID,然后将ID由10进制转换为62进制得到hash。此时,由于自定义链接的存在,所以需要在数据库中查询该hash值是否和已存在的自定义链接冲突。如果冲突,那么ID继续自增,重新计算hash,直到不再冲突。
注:
-
如果考虑到保密因素,可以将
0-9a-zA-Z顺序打乱。 -
如果考虑到
0、O、o、1、I不易区分,可以把这些字母去掉。 -
如果不是商用服务,使用6位或者8位数字字母随机编码也是可行的。
自定义短链接
自定义短链接需要考虑的问题就是用户自定义的链接和现有的hash冲突。
解决步骤:
-
首先获取当前链接的数字ID,并转换为62进制,比较hash和自定义链接是否一致,在数据库中查询该hash是否和已有的自定义链接冲突。
-
在数据库中搜索该自定义链接是否和现有hash冲突,如果冲突,则返回创建失败。
使用redis对链接进行缓存
尽管自定义链接和hash不冲突,都可以作为唯一ID,但是考虑到后期数据统计的唯一性和准确性,还是使用hash作为链接的标识。
在redis中创建哈希表URL_lIST,通过hset将已访问的链接所有数据存入redis,创建哈希表PERSONALIZED_URL_LIST,将自定义链接和对应的hash存入,利用redis的incr给每一个链接创建一个计数器,用于统计PV。
当用户通过链接http://xxx.xxx/abc访问时,首先以在URL_LIST中查询是否存在hash值为abc的信息(情况1),如果没有,那么在PERSONALIZED_URL_LIST查询是否存在值为abc的个性化链接(情况2),如果没有,那么直接到数据库查询数据。
针对情况2,如果查到数据,那么再到URL_LIST中查询对应的数据。
由于现实环境的复杂性,可能存在的情况是,PERSONALIZED_URL_LIST里存在该数据但是URL_LIST中不存在,那么还需要到数据库里查询。不过这些都是同步操作顺序进行,原则上不需要单独处理。
短链接数据统计
初期考虑到后面有数据可视化的需求,希望直接上ELK,解决数据可视化的同时也解决数据统计的问题。不过由于ELK的知识有限,日志传输到ELK后丢失严重,该方案暂时搁置。
由于短链接系统有多节点多线程,所以需要建设一个独立于短链接系统的短链接统计系统,主要工作就是完成数据统计的定时任务。
前面提到利用redis的incr给每一个链接创建一个计数器,这个计数器维护的是当前链接的增量PV。
定时任务的工作就是定时获取URL_LSIT中存在的链接,查找链接对应的PV增量,如果增量大于零,那么将URL_LIST中存在的该链接的PV值与PV增量相加,将PV增量置零,更新URL_LSIT和数据库中的数据。
TODO
- 基于ELK的数据可视化
- 链接禁用和启用控制
- ...