Java 网址短链接服务原理及解决方案
一、背景
现在在各种圈的产品各种推广地址,由于URL地址过长,不美观、不方便收藏、发布、传播以及各种发文字数限制等问题,微信、微博都在使用短链接技术。最近由于使用的三方的生成、解析短链接服务开始限制使用以及准备收费、不方便统计分析、不方便流控等问题,决定自建一个短地址服务。
二、原理
比如,http://a.b.com/15uOVS 这个短地址
第1步,浏览器请求这个地址
第2步,通过DNS后到短地址服务端,还原这个短地址对应的原始长地址。
第3步,请求http 301 或302到原始的长地址上
第4步,浏览器拿到原始长地址的响应response
三、实现
短地址服务的核心是短地址和长地址的转化映射算法。
最简单的算法是把原来的长地址做MD5摘要记为key,长地址记为value。把key value放入服务端缓存中比如redis中。
反向解析时通过URL解决出key来,比如上面的短地址key = 15uOVS 。然后通过key去缓存中获取原始长地址value实现URL地址还原。
MD5摘要有几个明显的问题:
1、短地址的长度受限,比如MD5后的数据长度是32位,需要进行分段循环处理,使短地址足够短
2、MD5的哈希碰撞问题,有一定的概率重复,解决此问题,需要不断的提升算法的复杂度,有些得不偿失
当然不止MD5实现算法比较多,大家可以自行谷歌。
笔者的实现原理如下:
1、新建一张表,主要字段包括自增ID【这里有个小技巧,可以给自增ID设置一个漂亮的起步值,使短地址码随机性更好一些,不建议从0开始自增,比如alter table mapping_info AUTO_INCREMENT=1112345612;】,原始长地址URL等,做一个自增的10进制长整型短地址key,然后把key转化成62进制,映射到0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz这62个字符上,做成6位的短链接码,如开头的例子15uOVS,【 6位62进制数,对应的号码空间为62的6次方,约等于568亿,即可以生成586亿的短网址,基本能满足需求】。
2、把短地址码及长地址,通过key value的形式放入redis和数据库中。
3、写一个过滤器ShortUrlMappingFilter 过滤器(为什么是过滤器,因为过滤器是对请求进行过滤转发的,不明白自己谷歌)的执行order设为第一个执行,即值最小。
注: 拦截器、过滤器、监听器的执行顺序 ---> 监听器 > 过滤器 > 拦截器 > controller执行 > 拦截器 > 过滤器 > 监听器
相关核心代码如下:
(一) 、ShortUrlMappingFilter过滤器如下:
/**
* @author xuzhujack
**/
public class ShortUrlMappingFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger(ShortUrlMappingFilter.class);
private static final int shortUrlKeyLength = 6;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("ShortUrlMappingFilter init ...........");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
LOGGER.info("ShortUrlMappingFilter url mapping start......");
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
BeanFactory factory = WebApplicationContextUtils
.getRequiredWebApplicationContext(request.getServletContext());
ShortUrlService shortUrlService = (ShortUrlService) factory.getBean("shortUrlService");
if(request.getRequestURI().substring(1).trim().length()== shortUrlKeyLength){
ShortUrlDto query = new ShortUrlDto();
query.setId(ShortUrlUtils.base62Decode(request.getRequestURI().substring(1).trim()));
ShortUrl shortUrl = shortUrlService.queryShortUrl(query);
if (shortUrl == null) {
LOGGER.error("ShortUrlMappingFilter shortUrl is null ...........dto is {}", query);
throw new ServletException();
} else {
String srcUrl = shortUrl.getSrcUrl();
LOGGER.info("ShortUrlMappingFilter shortUrl is not null .shortUrl is {}...........dto is {}", shortUrl, query);
response.sendRedirect(srcUrl);
}
}else{
filterChain.doFilter(request, response);
LOGGER.info("非短链接正常后续的doFilter..........");
}
}
@Override
public void destroy() {
}
}
(二)、 ShortUrlMappingFilter在项目的启动主类中增加如下配置:
@Bean
public FilterRegistrationBean contextFilterRegistrationBean2() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new ShortUrlMappingFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setName("ShortUrlMappingFilter");
registrationBean.setOrder(0);
return registrationBean;
}
(三)、62进制和10进制的相互转换
/**
* 将数字转为62进制
* @param num Long 型数字
* @return 62进制字符串
*/ public static String base62Encode(long num) {
String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
StringBuilder sb = new StringBuilder();
int remainder = 0;
int scale = 62;
while (num > scale - 1) {
remainder = Long.valueOf(num % scale).intValue();
sb.append(chars.charAt(remainder));
num = num / scale;
}
sb.append(chars.charAt(Long.valueOf(num).intValue()));
String value = sb.reverse().toString();
return StringUtils.leftPad(value, 6, '0');
} /**
* 62进制字符串转为数字
* @param str 编码后的62进制字符串
* @return 解码后的 10 进制字符串
*/
public static long base62Decode(String str) {
String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int scale = 62;
str = str.replace("^0*", "");
long num = 0;
int index = 0;
for (int i = 0; i < str.length(); i++) {
index = chars.indexOf(str.charAt(i));
num += (long) (index * (Math.pow(scale, str.length() - i - 1)));
}
return num;
}
Java 网址短链接服务原理及解决方案的更多相关文章
- 用PHP和Python生成短链接服务的字符串ID
假设你想做一个像微博短链接那样的短链接服务,短链接服务生成的URL都非常短例如: http://t.cn/E70Piib, 我们应该都能想到链接中的E70Piib对应的就是存储长链接地址的数据记录的I ...
- 短链接服务Octopus的实现与源码开放
前提 半年前(2020-06)左右,疫情触底反弹,公司的业务量不断提升,运营部门为了方便短信.模板消息推送等渠道的投放,提出了一个把长链接压缩为短链接的功能需求.当时为了快速推广,使用了一些比较知名的 ...
- 百度 谷歌 Twitter,这么多短链接服务(Short Url)究竟哪家强?
一.短链接是什么 url=HPqdQ5VR3vA39x7ZWoWyNzwWnsDhTbh66BTpdzsJLroBDzFRm4JV-G818Zc027uZrwe7zxtxnD4H2FUahftpUK& ...
- Java生成短链接
为什么要生成短链接 微博之所以要是使用短链接,主要是因为微博只允许发140 字,如果链接地址太长的话,那么发送的字数将大大减少. 那么使用短链接的作用有哪些呢?1.字数,2.统计,3.监控,4.过滤 ...
- 使用plv8+hashids生成短链接服务
有写过一个集成npm plv8 以及shortid生成短链接id服务,实际上我们可以集成触发器自动生成url对应的短链接地址,hashids也是一个不错的选择. 以下是一个别人写的一个博客实现可以参考 ...
- 生成短链接的URL
假设你想做一个像微博短链接那样的短链接服务,短链接服务生成的URL都非常短例如: http://t.cn/E70Piib, 我们应该都能想到链接中的E70Piib对应的就是存储长链接地址的数据记录的I ...
- 长链接转换成短链接(iOS版本)
首先需要将字符串使用md5加密,添加NSString的md5的类别方法如下 .h文件 #import <CommonCrypto/CommonDigest.h> @interface NS ...
- 新浪微博API生成短链接
通过新浪微博API,生成短链接,支持一次性转多个长链接 什么是短链接 短链接,通俗来说,就是将长的URL网址,通过程序计算等方式,转换为简短的网址字符串. 短链接服务 国内各大微博都推出了自己的短链接 ...
- 微博短链接的生成算法(Java版本)
最近看到微博的短链接真是很火啊,新浪.腾讯.搜狐等微博网站都加入了短链接的功能.之所以要是使用短链接,主要是因为微博只允许发140 字,如果链接地址太长的话,那么发送的字数将大大减少.短链接的主要职责 ...
随机推荐
- js保留的关键字
js保留的关键字 break else new var case finally return void catch for switch while continue function this w ...
- java 简单的冒泡
import java.util.Arrays; public class mao { public static void main(String[] args) { int [] array={1 ...
- Codeforces Round #594 (Div. 1) Ivan the Fool and the Probability Theory
题意:给你一个NxM的图,让你求有多少符合 “一个格子最多只有一个同颜色邻居”的图? 题解:首先我们可以分析一维,很容易就可以知道这是一个斐波那契计数 因为dp[1][m]可以是dp[1][m-1]添 ...
- 技术沙龙|原来落地AI应用是这么回事儿!
目前人工智能已经迈入应用落地之年,作为备受关注的话题,在重磅政策的加持下市场规模迅速扩大并渗透到各行各业的形势越发鲜明.在此背景下,作为国内不容忽视的创新企业之一,京东AI依托于NeuHub平台对数据 ...
- 从Evernote大批顶尖高管离职,看处于漩涡中的笔记应用未来前景
无论是巨头,还是独角兽,甚至是小而美的某些企业,在发生高管离职.裁员等情况时,总会引起业界的广泛关注.究其原因,就在于高管离职.裁员等往往意味着企业内部发生了动荡,甚至还会直接反映出所在行业的发展趋势 ...
- 3D打印前途光明,它需要怎样的进化?
在很长一段时间内,笔者都认为3D打印只会存在于科幻场景内,众多的科技大佬在前几年也和我保持相当一致的看法,代工大王郭台铭曾口出狂言:如果3D打印能够普及,我就把"郭"字倒过来写,时 ...
- Java中包的基本管理与编译
在写程序的过程中,总会出现代码编译过关,但是项目偏偏报错的情况,遇到几种情况,都在此一一记录,希望以后少走弯路. 1.添加jsp文件的时候,会报错 Multiple annotations found ...
- slideshare文档下载
if [ x"$1" = x1 ]; then for i in {1..46}; do url_i="https://image.slidesharecdn.com/b ...
- 2.Jenkins结合k8s完成Jenkins slave功能
1.构建镜像 下载基础镜像,这里使用openvz的包,下载地址为:https://wiki.openvz.org/Download/template/precreated,下载centos7的镜像 下 ...
- JavaSE--[转]加密和签名的区别
转载 http://blog.csdn.net/u012467492/article/details/52034835 私钥用来签名的,公钥用来验签的.公钥加密私钥解密是秘送,私钥加密公钥解密是签名 ...