业务场景:
短信平台发送带有跳转链接的短信时,会存在一个问题,有的时候链接会特别长,而且可能会有一些特殊字符,影响点:
- 这样手机的短信会将链接会切开来,直接点击无法跳转.
- 复制粘贴时浏览器可能无法将其直接跳转,会将其默认为搜索条件而不是链接地址来跳转.
正是以上的这些原因(只是一小部分的原因),就衍生了短连接的生成这个需求
短链接的生成方式:
- 加密方式:
最简单的办法就是通过MD5方式来加密,也是目前网上最多的一种实现方式,我也是借鉴了其方法,不过亲测高并发下无法支持其生成的唯一性,可能引起短连接地址不唯一,导致bug.
解决办法下面再讲.先看看我们的加密方式,代码如下:
import java.security.MessageDigest;
import java.util.Map;
import java.util.Random;
public class URLUtils {
/**
* 根据MD5加密方式生成短链接
*@author DingYongJun
*@date 2020/8/18
*@return List<Map<String, Object>>
*/
public static String getShortURL(String URL){
// 可以自定义生成 MD5 加密字符传前的混合 KEY
String key = "QDGYJ";
// 要使用生成 URL 的字符
String[] chars = new String[] { "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", "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"
};
// 对传入网址进行 MD5 加密
String hex = md5ByHex(key + URL);
String[] resUrl = new String[4];
for (int i = 0; i < 4; i++) {
// 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算
String sTempSubString = hex.substring(i * 8, i * 8 + 8);
// 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用long ,则会越界
long lHexLong = 0x3FFFFFFF & Long.parseLong(sTempSubString, 16);
String outChars = "";
for (int j = 0; j < 6; j++) {
// 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引
long index = 0x0000003D & lHexLong;
// 把取得的字符相加
outChars += chars[(int) index];
// 每次循环按位右移 5 位
lHexLong = lHexLong >> 5;
}
// 把字符串存入对应索引的输出数组
resUrl[i] = outChars;
}
Random random=new Random();
int j=random.nextInt(4);
//随机取一个作为短链
return resUrl[j];
}
/**
* MD5加密(32位大写)
*@author DingYongJun
*@date 2020/8/18
*@return List<Map<String, Object>>
*/
public static String md5ByHex(String src) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] b = src.getBytes();
md.reset();
md.update(b);
byte[] hash = md.digest();
String hs = "";
String stmp = "";
for (int i = 0; i < hash.length; i++) {
stmp = Integer.toHexString(hash[i] & 0xFF);
if (stmp.length() == 1)
hs = hs + "0" + stmp;
else {
hs = hs + stmp;
}
}
return hs.toUpperCase();
} catch (Exception e) {
return "";
}
}
}
以上是借鉴网上其他博主的代码,在此不做过多赘述,有兴趣研究其具体的生成方式的可以使用断点的方式来进去了解.
-
问题所在:
大家可以很轻易的发现一些问题,比如每次传的url一毛一样的时候,我们只生成四个短连接和随机数,当并发量大到一定的程度时,就会发生不唯一性的问题. -
解决办法:
为了不使短连接地址太长(不超过12位,否则和长链接本质上区别不大了).我们可以在业务层调用两次getShortURL方法.
具体代码如下:
Date date = new Date();
String URL = paramsMap.get("url").toString();
String shortUrl = URLUtils.getShortURL(URL);
//为了保证数据的唯一性,再加个时间戳压缩成短链接,两个拼接在一起,确保数据的唯一性
String timeStamp = String.valueOf(date.getTime());
String shotTime = URLUtils.getShortURL(timeStamp);
String duanlianjie = shortUrl+shotTime;
解释:
为了保证数据的唯一性,再加个时间戳压缩成短链接,两个拼接在一起,确保数据的唯一性,系统时间是一直在变化的,所以在一定程度上就保证了这个短链接的唯一性!
可能出现的问题:
这个在高并发的情况也是可能会出现重复的情况,但是这个保证了同样的长链接不会生成重复的短链接.又因为短信平台不会同一时间多次调用.基本上满足了我的业务需求.
解决办法:
在此我就不具体实现解决办法了,只提供一个大概思路给大家.
- 1.在getShortURL加上同步锁Synchronized ,保证每次只能有一个线程可以调用该方法,虽然在一定程度上可能会损失性能,但是会确保短链接的唯一性!
- 2.可以加上其他的算法来和时间戳拼接到一起,然后再调用生成方法.如:(雪花算法等).有兴趣的可以自己去研究一下.
自此全部结束,以上都是个人所言,如有不对,尽请指出,大家一起交流进步。谢谢!
有需要源码的,或者有不理解,欢迎关注微信公众号来讨论,留言会回复的哦~
!
欢迎微信搜索关注:《全员格子》!!!