码迷,mamicode.com
首页 > 编程语言 > 详细

Java 域名短链接服务原理及解决方案

时间:2019-07-17 18:57:57      阅读:534      评论:0      收藏:0      [点我收藏+]

标签:sub   ack   stat   解决方案   收藏   set   ase   ati   nfa   

一、背景

       现在在各种圈的产品各种推广地址,由于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 域名短链接服务原理及解决方案

标签:sub   ack   stat   解决方案   收藏   set   ase   ati   nfa   

原文地址:https://www.cnblogs.com/xuzhujack/p/11202364.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!