给定一个长的URL,你怎么设计一个服务,能够提供短且独一无二的短URL(短链接-短网址)

短网址

如原地址:https://www.baidu.com/abcdefghigklmnopqrstuvwxyz.html

短网址为:https://dwz.cn/wcplVJvy

短网址:顾名思义就是一个长度比较短的url地址。把原来的长的URL通过程序设计等方式转换为短的链接。

百度搜索“短网址”,会发现有很多相关的在线工具,或者公司平台。说明这个短链接还是有很多用武之地的。

  • 在限制文本长度的社交平台里,可以输入更多信息(朋友圈、微博、短信等)
  • 生成的二维码更容易被识别(识别难度低)
  • 可以将带有中文等特殊字符的链接转化为可识别的链接(如:https://www.baidu.com/s?ie=UTF-8&wd= 百度)
  • 推广时容易复制、不易被拦截等。
  • 针对短链接地址做UV/PV/地域访问统计等处理(针对淘宝客单品短链接分析等)。

问题?

如果要你做一个短网址生成工具的话,应该怎么做呢?又要考虑哪些点呢?

  1. 如何给每个长URL生成一个唯一的标识ID?
  2. 怎么样在短时间内产生大量的唯一标识ID?
  3. 在服务器中如何设置URL重定向?
  4. 如何设计可定制化的短链接生成规则?(针对不同的用户/客户去生成)
  5. 删除短链接的策略?
  6. 如何有效的记录点击等统计数据?

解决方案

一、先处理标识ID的唯一性,以及保证快速大量生成。

  • Hash算法

一般情况下,我们把一个不定长度的字符串生成固定长度的值,会用到Hash算法(如MD5)。

但是它会产生hash碰撞啊,当需要转换的链接越多时,产生碰撞的情况越容易发生,这样的话,可能三个不同的页面都用一个短链接来表示了,肯定会产生问题。

  • 用户数据库处理
| 主键id |类型   |初始值     |当前值      |最大值         |步长 | 
|   1    |短网址 |1000000001 |1000000011  |9999999999    |1    |

## 也就是在数据库中设定一个初始值,每次增加步长。保证每次取出的数字不一样

但是这样做可能会有一个问题,就是当高并发的时候对数据库的压力会比较大。生成1w个短链接就会连接数据库1W次。

  • Redis、Zookeeper

redis官网测试读写到10万左右,zookeeper相比较差一些。都可以生成唯一值。
redis利用 incr 命令自增。 zookeeper利用节点的顺序特性也可以实现唯一性。

但是这种方式就显得短网址地址太具有规律性。就是一个部分可自增的地址,容易被人发现猫腻,不安全。

  • 用不重复算法

也就是将10进制转换为62进制。六十二进制是由[a - z, A - Z, 0 - 9] 总共 62 个字母组成的。1位可以表示62个数字,如果短网址唯一识别码是6位的话,也有62^6种显示(568亿种),这种数量一般是足够了。如果不够的话,可以增加唯一识别码的长度。

如原长链接
http://www.xx.com/aasdfadfadfadfasdf/xsdf/asf?agent=sdfwkefjaskdnfkand
先转自增
http://www.xx.com/10000000001
在转62进制
http://www.xx.com/aUKYOB


算法参考:https://segmentfault.com/a/1190000012088345

但是,这种短网址对于非技术人员来说好像不具有特别的辨识度。但是有点计算机知识的话,还是会发现它比较有规律的,仍然不安全。

  • 不重复算法 + 规则插入

就是在上面的基础上,固定位置插入随机数。

(?)a(?)U(?)K(?)Y(?)O(?)B(?)
在转为62进制数后长度固定的基础上,选取一个或多个?号的位置插入随机字符,扰乱视听。只要自己在解析的时候对应处理就好了。 当然这样处理涉及到对字符串的拼接处理。拆得越碎可能效率上会有影响,自己去评估吧。

综上所述,可以结合redis + 转62进制的方式保证唯一和批量生产。

二、客户想要自定义短链接/或者按照业务区分短链接

## 我们可以针对域名做处理,如下域名:
https://dwz.cn/wcplVJvy  

可以分为几部分
https://?.dwz.cn/?/wcplVJvy

第一个问号表示二级域名的名称,虽然可以设置无限个,但是空间商不会让你这么干的,我们可以将有限的二级域名内部使用。好像为了短网址做二级域名的区分意义不是太大。

第二个问号表示访问地址的某一段。可以任意长度,由用户自己定义,作为个性化的一种服务也不错。在访问时,在nginx的域名转发配置中,可以根据正则进行过滤或者其他处理。

注:如果用户想自定义第二个?后的内容,就要做一些和已存在数据的验证处理。或者做一些额外标记,区分用户自定义的或者系统自动生成的。

三、URL重定向

以文章开头的地址为例,过程如下:

  1. DNS解析 https://dwz.cn/wcplVJvy 的IP地址
  2. 服务端获取短网址的唯一标识符 wcplVJvy
  3. 根据唯一标识符去数据库中获取对应的长连接(数据量大可能导致这里查询慢,可以优化下,前置索引或其他?)
  4. 返回302重定向。(也就是在http协议的响应信息中返回新的location 及 Status)
  5. 浏览器跳转到新的url地址

==注==:如果是将 dwz.cn/wcplVJvy的访问地址重写为 https://dwz.cn/wcplVJvy可以参考这个 【nginx重定向】

四、删除短链接的策略

应该有两种方式去处理。

  1. 设定短链接的有效时间,比如一周/一月/一年,定时定期检查过期数据进行删除。
  2. 参考redis的过期删除策略。 LRU或者LFU算法都可以(最近最少使用,最不频繁使用)

五、统计数据的处理

这里统计的数据就可以很丰富了。

1. 短网址的访问来源(就是从哪个网址点击的短网址)
通过短网址服务端 request.getHeader("Referer");获取。
也可以做个中转页,通过js document.referrer来获取。(这种不太好)

2. 页面访问频次等统计

3.IP,PV等
可以通过请求做一些简单的数据统计,
也可以通过短网址服务端的访问日志来获取,如下:

以前写的可供参考的几种针对日志的处理。

利用logstash6.4.2监控access访问日志并切割,使用geoip插件分析ip地址在kibana控制台形成用户热点地图

通过python处理解析accesslog日志文件,kettle抽取数据并做PV/UV的统计实现

利用linux命令行grep|awk在mac本上分析wordpress.log访问日志


最后附上一段项目中的shortUrls的方法实现部分代码:

package com.chl.tools;

import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

@Service("shortUrlService")
public class ShortUrls implements shortUrlService {

    private static Cache<String, String> cache = CacheBuilder.newBuilder()
            // 设置cache的初始大小为100,要合理设置该值
            .initialCapacity(100)
            // 最大缓存数量1万
            .maximumSize(10000)
            // 设置并发数为5,即同一时间最多只能有5个线程往cache执行写入操作
            .concurrencyLevel(5)
            // 设置cache中的数据在写入之后的存活时间为60分钟
            .expireAfterWrite(3600, TimeUnit.SECONDS)
            // 构建cache实例
            .build();

    @Override
    public String getShortUrl(String longUrl) {
        // 如果已经存在直接返回
        ShortUrlDO target = shortUrlDao.getByLongUrl(longUrl);
        if (target != null) {
            return config.getShortDomainPath() + target.getShortId();
        }

        // 这里是通过数据库步长的方式生成唯一标识
        long id = sequenceService.next(SequenceService.SHORT_URL);

        // 转换成32进制
        String shortId = Utils.to32hex(id);

        // 保存db,然后返回
        target = new ShortUrlDO();
        target.setLongUrl(longUrl);
        target.setShortId(shortId);
        target.setStatus(1);

        shortUrlDao.insert(target);

        return config.getShortDomainPath() + shortId;
    }

    @Override
    public String getLongUrl(String shortId) {
        // 加入了本地缓存处理
        String longUrl = cache.getIfPresent(shortId);
        if (longUrl != null) {
            return longUrl;
        }

        // 查询db
        ShortUrlDO target = shortUrlDao.getByShortId(shortId);
        if (target == null) {
            return null;
        } else {
            cache.put(shortId, target.getLongUrl());
        }
        return target.getLongUrl();
    }

}



©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容