开发者社区> 咖啡机(K.F.J)> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Node.js躬行记(6)——自制短链系统

简介:   短链顾名思义是一种很短的地址,应用广泛,例如页面中有一张二维码图片,包含的是一个原始地址(如下所示),如果二维码中的链接需要修改,那么就得发代码替换掉。
+关注继续查看

  短链顾名思义是一种很短的地址,应用广泛,例如页面中有一张二维码图片,包含的是一个原始地址(如下所示),如果二维码中的链接需要修改,那么就得发代码替换掉。

  但如果二维码图包含的是一条短链,那么只要修改短链中的映射关系,就能不发代码了。当然了,前提是有一套短链系统维护着他们之间的关系,下图是列表和新增的界面。


97.png


98.png


  前端界面的代码省略了,直接看短链用Node.js实现的后端代码。


一、MySQL


  在 web_short_chain 表中,主键 id 是一个自增的整数,short 字段存储着短链中的 key,也就是 http://t.cn/4fYKXF 中的 4fYKXF 之类的数据,并且是全表唯一的,目前还未对其建索引。


CREATE TABLE `web_short_chain` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `short` varchar(10) COLLATE utf8mb4_bin NOT NULL COMMENT '短链地址中的key',
  `url` varchar(200) COLLATE utf8mb4_bin NOT NULL COMMENT '原始地址',
  `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `mtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态',
  PRIMARY KEY (`id`),
  UNIQUE KEY `short_UNIQUE` (`short`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='短链存储'


二、计算 short 的值


  需要两步才能将原始地址映射成短链地址,第一步是使用 MurmurHash(么么哈希)算法,由Austin Appleby在2008年发明,可将原始地址转换成一个哈希值,算法如下(最新版本 MurmurHash3)。

function MurmurHashV3(key, seed) {
  if (typeof key === "string") key = createBuffer(key);
  var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i;
  remainder = key.length & 3; // key.length % 4
  bytes = key.length - remainder;
  h1 = seed;
  c1 = 0xcc9e2d51;
  c2 = 0x1b873593;
  i = 0;
  while (i < bytes) {
    k1 =
      (key[i] & 0xff) |
      ((key[++i] & 0xff) << 8) |
      ((key[++i] & 0xff) << 16) |
      ((key[++i] & 0xff) << 24);
    ++i;
    k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
    k1 = (k1 << 15) | (k1 >>> 17);
    k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
    h1 ^= k1;
    h1 = (h1 << 13) | (h1 >>> 19);
    h1b = ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
    h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
  }
  k1 = 0;
  switch (remainder) {
    case 3:
      k1 ^= (key[i + 2] & 0xff) << 16;
    case 2:
      k1 ^= (key[i + 1] & 0xff) << 8;
    case 1:
      k1 ^= key[i] & 0xff;
      k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
      k1 = (k1 << 15) | (k1 >>> 17);
      k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
      h1 ^= k1;
  }
  h1 ^= key.length;
  h1 ^= h1 >>> 16;
  h1 = ((h1 & 0xffff) * 0x85ebca6b + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
  h1 ^= h1 >>> 13;
  h1 = ((h1 & 0xffff) * 0xc2b2ae35 + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 0xffffffff;
  h1 ^= h1 >>> 16;
  return h1 >>> 0;
}


  在得到一个整型的哈希值后,就得转换成字符,像上面短链中的字符是 6 个,也就是将10进制转换成62进制,如下所示。


function string10to62(n) {
  if (n === 0) {
    return "0";
  }
  var digits = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  var result = "";
  while (n > 0) {
    result = digits[n % digits.length] + result;
    n = parseInt(n / digits.length, 10);
  }
  return result;
}


三、缓存


  在将映射关系存入数据库时,可将其直接存入 redis 缓存中,采用哈希的数据结构,也就是将计算出的 short 作为 key,原始地址作为 value。

  假设每条关系所占空间是50字节,那么2000W条记录大概占用 1G左右,为了节省空间,缓存的超时时间会设为 7 天。

  每次在访问短链时,首先从缓存中读取,若有,就直接跳转;若无,则查询数据库,再将映射关系存入缓存中。


//读取redis
let url = await services.common.redisShortChainGet(short);
ctx.status = 302;     //临时跳转
if(url) {
  ctx.redirect(getCompleteUrl(url, querystring));
  return;
}
//缓存中不存在,则读取数据库
const data = await services.common.getOneShortChain({ short });
if(!data) {
  ctx.body = "短链不存在";
  return;
}
//将数据库中读取的短链缓存起来
await services.common.redisShortChainSet(short, data.url);
ctx.redirect(getCompleteUrl(data.url, querystring));


  网上的一些文章在判断短链是否存在时,会采用布隆过滤器

  它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,长度是 10 亿的布隆过滤器,也只需要 125MB左右的内存空间。

  布隆过滤器的缺点是有一定的误识别率和删除困难,例如下图中的 A 和 E 是存在于布隆过滤器中的,它们的映射位置都设成了 1,而 B 并不存在,但它的映射指向了两个是 1 的位置,从而就造成了误识别。


99.png

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
node.js 个人博客系统
node.js 个人博客系统
22 0
Node.js躬行记(12)——BFF
  BFF字面意思是服务于前端的后端,我的理解就是数据聚合层。我们组在维护一个后台管理系统,会频繁的与数据库交互。   过去为了增删改查会写大量的对应接口,并且还需要在Model、Service、Router三层写不同的代码逻辑,吃力不讨好。   为了节约开发时间,构思通用接口,并付诸于实际项目中。虽然简化了Router和Service部分,但其实就是将该部分的代码迁移到了前端页面中。
52 0
Node.js 文件系统
Node.js 文件系统 Node.js 提供一组类似 UNIX(POSIX)标准的文件操作API。 Node 导入文件系统模块(fs)语法如下所示: var fs = require("fs") 异步和同步 Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
943 0
Node.js学习
《了不起的Node.js:将JavaScript进行到底》(电子工业出版社) 2009年ryan在JavaScript开发者大会宣布了一个名为node.js的新技术,运行在服务器端的JavaScript,“以后开发web应用就只需要一种语言了!!!” node.js快速高效的优点得益于事件轮询技术(event loop),以及google为chrome浏览器设计的V8(JavaScript解释器和虚拟机)。
1245 0
nvm版本控制以及node.js
nvm node.js版本控制工具        下载 nvm 包 地址:https://github.com/coreybutler/nvm-windows/releases  我们选择第一个:nvm-noinstall.
1006 0
node.js学习
之前弄了一个记事本 http://zhang.tdale.cn/ 这个是w3c那里的node介绍 http://www.w3cschool.cn/nodejs/   有必要的话再整理一遍要点将编程看作是一门艺术,而不单单是个技术。
773 0
node.js使用mysql模块的坑
之前用node.js写的订餐系统,很容易挂掉,一直也没想去解决它。今天看了一下,试了试,原因是在连接数据库的时候没有对error事件进行处理,导致程序一直挂在那里,需要重启服务才能正常使用。   没有使用缓存,直接都是操作数据存,所以导致一个页面数据库请求比较多。
938 0
node.js入门学习
什么是Node.js?还服务器端javascript?对于这个概念我在这篇文章不做解释,可以自己去搜索了解下,服务器端js不是新技术,只是最近的node.js的火爆让他爆发了,我会在以后的文章里解释什么是node.js。
883 0
+关注
咖啡机(K.F.J)
每天进步一点点 研磨生活的香甜
350
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载