长链接转成短链接的原理和实现详解

一、为什么要设计短链接,短链接有什么好处?

1、链接变短,在对内容长度有限制的平台发文,可编辑的文字就变多了。

比如:微博,限定了只能发 140 个字,如果一串长链直接怼上去,其他可编辑的内容就所剩无几了,用短链的话,链接长度大大减少,自然可编辑的文字多了不少。

 

2、我们经常需要将链接转成二维码的形式分享给他人,如果是长链的话二维码密集难识别,短链就不存在这个问题了。

3、链接太长在有些平台上无法自动识别为超链接。

二、短链接实现的原理。

1、请求流程:

首先,我们先看看当当的短链接http://localhost:8060/1J0vEa
它是由两个部分组成
http://localhost:8060短链接系统的域名地址或者IP+端口
1J0vEa:请求参数
请求 http://localhost:8060/1J0vEa 地址后,返回状态如下所示

 2、请求短链接后,浏览器发生了什么?

上图所示短链接系统,返回的状态可以为301或者302。
301代表什么?
301代表的是永久重定向。什么意思呢?
对于GET请求, 301跳转会默认被浏览器cache。也就是说,用户第一次访问某个短链接后,如果服务器返回301状态码,则这个用户在后续多次访问同一短链接地址,浏览器会直接请求跳转地址,而不会再去短链接系统上取!

这么做优点很明显,降低了服务器压力,但是无法统计到短链接地址的点击次数。

302代表什么?
302代表的是临时定向。什么意思呢?
对于GET请求, 302跳转默认不会被浏览器缓存,除非在HTTP响应中通过 Cache-Control 或 Expires 暗示浏览器缓存。因此,用户每次访问同一短链接地址,浏览器都会去短链接系统上取。

这么做的优点是,能够统计到短地址被点击的次数了。但是服务器的压力变大了。

三、短链接实现的方法。

1、MurmurHash 非加密型哈希函数 把链接转成一串数字。

public static String hashToBase62(String str) {
   int i = MurmurHash.hash32(str);
   long num = i < 0 ? Integer.MAX_VALUE - (long) i : i;
   return convertDecToBase62(num);
}

2、由于我们的短链接是由 a-z、A-Z 和 0-9 共 62 个字符可以选择。因此,我们可以由长链接经过hashToBase62方法转成的十进制的数字,转换为一个62进制的数,例如:http://www.cnblogs.com/rjzheng/p/11827426.html 转成 1J0vEa

private static char[] CHARS = new char[]{
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
	};
	private static int SIZE = CHARS.length;

	private static String convertDecToBase62(long num) {
		StringBuilder sb = new StringBuilder();
		while (num > 0) {
			int i = (int) (num % SIZE);
			sb.append(CHARS[i]);
			num /= SIZE;
		}
		return sb.reverse().toString();
	}

3、短链接生成方法。

@Override
	public String saveUrlMap(String shortURL, String longURL, String originalURL) {
		//保留长度为1的短链接
		if (shortURL.length() == 1) {
			longURL += DUPLICATE;
			shortURL = saveUrlMap(HashUtils.hashToBase62(longURL), longURL, originalURL);
		}
		//在布隆过滤器中查找是否存在
		else if (FILTER.contains(shortURL)) {
			//存在,从Redis中查找是否有缓存
			String redisLongURL = redisTemplate.opsForValue().get(shortURL);
			if (redisLongURL != null && originalURL.equals(redisLongURL)) {
				//Redis有缓存,重置过期时间
				redisTemplate.expire(shortURL, TIMEOUT, TimeUnit.MINUTES);
				return shortURL;
			}
			//没有缓存,在长链接后加上指定字符串,重新hash
			longURL += DUPLICATE;
			shortURL = saveUrlMap(HashUtils.hashToBase62(longURL), longURL, originalURL);
		} else {
			//不存在,直接存入数据库
			try {
				urlMapper.saveUrlMap(new UrlMap(shortURL, originalURL));
				FILTER.add(shortURL);
				//添加缓存
				redisTemplate.opsForValue().set(shortURL, originalURL, TIMEOUT, TimeUnit.MINUTES);
			} catch (Exception e) {
				if (e instanceof DuplicateKeyException) {
					//数据库已经存在此短链接,则可能是布隆过滤器误判,在长链接后加上指定字符串,重新hash
					longURL += DUPLICATE;
					shortURL = saveUrlMap(HashUtils.hashToBase62(longURL), longURL, originalURL);
				} else {
					throw e;
				}
			}
		}
		return shortURL;
	}

四、代码。

实现的代码在github:https://github.com/526606178/shortUrl

本文参考了:

https://www.cnblogs.com/rjzheng/p/11827426.html

https://github.com/zzyybs/bestJavaer/commit/cc48eb0680671e8bb362782c78ed708f93f729d8#diff-7dcf1a9faa4713d5be6358d58e41fdc70b8f399b058d1ac50ea025ba99301cd8

  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,让我来给您详细解释一下merge join的原理和实例。 一、原理 merge join是一种常用的连接方式,它的原理是通过将两个表按照连接字段进行排序,然后逐个匹配相同值的行,将这些行合并成一个结果集。这个过程中需要使用到两个指针,一个指向左表,一个指向右表,每次比较两个指针所指向的值,如果相等,则将这两个值合并成一个结果,并将两个指针都指向下一个值;如果不相等,则将值较小的指针指向下一个值。这个过程重复进行,直到其中一个表的所有值都被处理完毕,或者两个表的值都被处理完毕。 二、案例 假设有两个表A和B,它们的结构如下: 表A: | id | name | age | |----|------|-----| | 1 | Tom | 20 | | 2 | Jack | 25 | | 3 | Lucy | 22 | 表B: | id | gender | city | |----|--------|------| | 1 | male | Beijing | | 2 | male | Shanghai | | 4 | female | Guangzhou | 我们要使用merge join将这两个表连接起来,连接字段是id。操作步骤如下: 1. 对表A和表B按照id进行排序,排序结果如下: 表A排序后: | id | name | age | |----|------|-----| | 1 | Tom | 20 | | 2 | Jack | 25 | | 3 | Lucy | 22 | 表B排序后: | id | gender | city | |----|--------|------| | 1 | male | Beijing | | 2 | male | Shanghai | | 4 | female | Guangzhou | 2. 从两个表的排序结果中分别取出第一个值进行比较,这里id都是1,所以将这两个值合并成一个结果,结果为: | id | name | age | gender | city | |----|------|-----|--------|------| | 1 | Tom | 20 | male | Beijing | 然后将指针指向下一个值。 3. 取出表A的第二个值2,和表B的第二个值2进行比较,这里也可以合并成一个结果,结果为: | id | name | age | gender | city | |----|------|-----|--------|------| | 2 | Jack | 25 | male | Shanghai | 然后将指针指向下一个值。 4. 取出表A的第三个值3,和表B的第三个值4进行比较,这里不相等,所以将值较小的指针指向下一个值,即将表A的指针指向下一个值。 5. 表A的所有值都被处理完毕,连接结束。 最后,返回连接后的结果集: | id | name | age | gender | city | |----|------|-----|--------|------| | 1 | Tom | 20 | male | Beijing | | 2 | Jack | 25 | male | Shanghai | 以上就是一个简单的merge join的案例和原理详解,希望对您有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值