基于Java设计一个短链接生成系统

目录
  • 引言
  • 短链接的原理
  • 短链接怎么设计的

引言

相信大家在生活中,特别是最近的双十一活动期间,会收到很多短信,而那些短信都有两个特征,第一个是几乎都是垃圾短信,这个特点此处可以忽略不计,第二个特点是链接很短,比如下面这个:

我们知道,短信有些是有字数限制的,直接放一个带满各种参数的链接,不合适,另外一点是,不想暴露参数。好处无非以下:

  • 太长的链接容易被限制长度
  • 短链接看着简洁,长链接看着容易懵
  • 安全,不想暴露参数
  • 可以统一链接转换,当然也可以实现统计点击次数等操作

那背后的原理是什么呢?怎么实现的?让你实现这样的系统,你会怎么设计呢?【来自于某鹅场面试官】

短链接的原理

短链接展示的逻辑

这里最重要的知识点是重定向,先复习一下http的状态码:

分类 含义
1** 服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

那么以 3 开头的状态码都是关于重定向的:

  • 300:多种选择,可以在多个位置存在
  • 301:永久重定向,浏览器会缓存,自动重定向到新的地址
  • 302:临时重定向,客户端还是会继续使用旧的URL
  • 303:查看其他的地址,类似于301
  • 304:未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。
  • 305:需要使用代理才能访问到资源
  • 306:废弃的状态码
  • 307:临时重定向,使用Get请求重定向

整个跳转的流程:

1.用户访问短链接,请求到达服务器

2.服务器将短链接装换成为长链接,然后给浏览器返回重定向的状态码301/302

301永久重定向会导致浏览器缓存重定向地址,短链接系统统计访问次数会不正确

302临时重定向可以解决次数不准的问题,但是每次都会到短链接系统转换,服务器压力会变大。

3.浏览器拿到重定向的状态码,以及真正需要访问的地址,重定向到真正的长链接上。

从下图可以看出,确实链接被302重定向到新的地址上去,返回的头里面有一个字段Location就是所要重定向的地址:

短链接怎么设计的

全局发号器

肯定我们第一点想到的是压缩,像文件压缩那样,压缩之后再解压还原到原来的链接,重定向到原来的链接,但是很不幸的是,这个是行不通的,你有见过什么压缩方式能把这么长的数字直接压缩到这么短么?事实上不可能。就像是Huffman树,也只能对那种重复字符较多的字符串压缩时效率较高,像链接这种,可能带很多参数,而且各种不规则的情况都有,直接压缩算法不现实。

那https://dx.10086.cn/tzHLFw与https://gd.10086.cn/gmccapp/webpage/payPhonemoney/index.html?channel=之间的装换是怎么样的呢?前面路径不变,变化的是后面,也就是tzHLFw与gmccapp/webpage/payPhonemoney/index.html?channel=之间的转换。

实际也很简单,就是数据库里面的一条数据,一个id对应长链接(相当于全局的发号器,全局唯一的ID):

id url
1 gd.10086.cn/gmccapp/web…

这里用到的,也就是我们之前说过的分布式全局唯一ID,如果我们直接用id作为参数,貌似也可以:https://dx.10086.cn/1,访问这个链接时,去数据库查询获得真正的url,再重定向。

单机的唯一ID很简单,用原子类AtomicLong就可以,但是分布式的就不行了,简单点可以用 redis,或者数据库自增,或者可以考虑Zookeeper之类的。

id 转换策略

但是直接用递增的数字,有两个坏处:

  • 数字很大的时候,还是很长
  • 递增的数字,不安全,规律性太强了

明显我们平时看到的链接也不是数字的,一般都是大小写字母加上数字。为了缩短链接的长度,我们必须把id转换掉,比如我们的短链接由a-z,A-Z,0-9组成,相当于62进制的数字,将id转换成为62进制的数字:

public class ShortUrl {

    private static final String BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static String toBase62(long num) {
        StringBuilder result = new StringBuilder();
        do {
            int i = (int) (num % 62);
            result.append(BASE.charAt(i));
            num /= 62;
        } while (num > 0);

        return result.reverse().toString();
    }

    public static long toBase10(String str) {
        long result = 0;
        for (int i = 0; i < str.length(); i++) {
            result = result * 62 + BASE.indexOf(str.charAt(i));
        }
        return result;
    }

    public static void main(String[] args) {
        // tzHLFw
        System.out.println(toBase10("tzHLFw"));
        System.out.println(toBase62(27095455234L));
    }
}

id转 62位的key 或者key装换成为id都已经实现了,不过计算还是比较耗时的,不如加个字段存起来,于是数据库变成了:

id key url
27095455234 tzHLFw gd.10086.cn/gmccapp/web…

但是这样还是很容易被猜出这个id和key的对应关系,要是被遍历访问,那还是很不安全的,如果担心,可以随机将短链接的字符顺序打乱,或者在适当的位置加上一些随机生成的字符,比如第1,4,5 位是随机字符,其他位置不变,只要我们计算的时候,将它对应的关系存到数据库,我们就可以通过连接的key找到对应的url。(值得注意的是,key必须是全局唯一的,如果冲突,必须重新生成)

一般短链接都有过期时间,那么我们也必须在数据库里面加上对应的字段,访问的时候,先判断是否过期,过期则不给予重定向。

性能考虑

如果有很多短链接暴露出去了,数据库里面数据很多,这个时候可以考虑使用缓存优化,生成的时候顺便把缓存写入,然后读取的时候,走缓存即可,因为一般短链接和长链接的关系不会修改,即使修改,也是很低频的事情。

如果系统的id用完了怎么办?这种概率很小,如果真的发生,可以重用旧的已经失效的id号。

如果被人疯狂请求一些不存在的短链接怎么办?其实这就是缓存穿透,缓存穿透是指,缓存和数据库都没有的数据,被大量请求,比如订单号不可能为-1,但是用户请求了大量订单号为-1的数据,由于数据不存在,缓存就也不会存在该数据,所有的请求都会直接穿透到数据库。如果被恶意用户利用,疯狂请求不存在的数据,就会导致数据库压力过大,甚至垮掉。

针对这种情况,一般可以用布隆过滤器过滤掉不存在的数据请求,但是我们这里id本来就是递增且有序的,其实我们范围大致都是已知的,更加容易判断,超出的肯定不存在,或者请求到的时候,缓存里面放一个空对象也是没有问题的。

到此这篇关于基于Java设计一个短链接生成系统的文章就介绍到这了,更多相关Java短链接生成系统内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java Springboot如何基于图片生成下载链接

    现有一些图片在服务器上的链接,在浏览器中打开这些链接是直接显示在浏览器页面的形式. 现在需要生成这些图片的单独下载以及打包下载链接,即在浏览器中打开下载链接后弹出下载框提示下载.由于前端存在跨域问题,所以图片下载由后台接口完成. 首先编写文件下载工具类: import java.net.URL; import java.net.MalformedURLException; import org.apache.commons.io.FileUtils; public class FileDownl

  • Java如何生成带网站链接(URL)的二维码

    QR 码 QR 码最常见的应用便是为网站中一个特定的网页或下载页带来流量.因此,QR码常常会编码URL或网站地址,用户可以通过手机摄像头扫描,并在其浏览器中打开.URL可以直接编码在QR码中. QR(Quick-Response) code是被广泛使用的一种二维码,解码速度快. 它可以存储多用类型 如上图时一个qrcode的基本结构,其中: 位置探测图形.位置探测图形分隔符.定位图形:用于对二维码的定位,对每个QR码来说,位置都是固定存在的,只是大小规格会有所差异; 校正图形:规格确定,校正图形

  • 浅谈Java代码的 微信长链转短链接口使用 post 请求封装Json(实例)

    废话不多说,直接上代码 String longUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + MpUtil.APPID + "&redirect_uri=" + MpUtil.HOMEPAGE + "/nweixinLoginPc.fo%3Frandomcode=" + randomcode + "&response_type=co

  • 基于Java设计一个短链接生成系统

    目录 引言 短链接的原理 短链接怎么设计的 引言 相信大家在生活中,特别是最近的双十一活动期间,会收到很多短信,而那些短信都有两个特征,第一个是几乎都是垃圾短信,这个特点此处可以忽略不计,第二个特点是链接很短,比如下面这个: 我们知道,短信有些是有字数限制的,直接放一个带满各种参数的链接,不合适,另外一点是,不想暴露参数.好处无非以下: 太长的链接容易被限制长度 短链接看着简洁,长链接看着容易懵 安全,不想暴露参数 可以统一链接转换,当然也可以实现统计点击次数等操作 那背后的原理是什么呢?怎么实

  • 基于Java实现一个简单的单词本Android App的实践

    目录 布局设计 代码 AddDanciActivity.java DBOpenHelper.java 本文基于Java实现了一个简单的单词本安卓app,用的是SQLite数据库,包括布局文件.源码及实现图. 布局设计 单词本主界面 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/an

  • 基于Java实现一个复杂关系表达式过滤器

    目录 背景 分析准备 实现方式 写在最后 背景 最近,有一个新需求,需要后台设置一个复杂的关系表达式,根据用户指定ID,解析该用用户是否满足该条件,后台设置类似于禅道的搜索条件 但是不同的是禅道有且仅有两个组,每个组最多三个条件 而我们这边组与关系可能是更复杂的,组中有组,每个条件都是有且或关系的.由于保密原因,原型就不发出来了. 看到这个需求,作为一个后端,第一时间想到的是类似QLEpress这类的表达式框架,只要构建一个表达式,通过解析表达式即可快速对目标用户进行筛选,但是可惜的是前端同学不

  • 基于Java创建一个订单类代码实例

    这篇文章主要介绍了基于Java创建一个订单类代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 需求描述 定义一个类,描述订单信息 订单id 订单所属用户(用户对象) 订单所包含的商品(不定数量个商品对象) 订单总金额 订单应付金额: 总金额500~1000,打折85折 总金额1000~1500,打折80折 总金额1500~2000,打折70折 总金额超过2000,打折65折 在此基础上,还要看用户的vip等级 用户vip等级为:一般会员,

  • 基于QT设计一个春联自动生成器

    目录 1. 前言 2. 实现原理 3. 示例代码 3.1 获取春联接口 3.2 数据解析代码 3.3 token数据存储 1. 前言 春节是中国最隆重的传统节日,一到过年家家户户肯定是要贴春联:在春节前夕,会用大红纸张,加上浓墨书写祝福词语,在春节当天贴于门框两边,寓意着一年吉祥如意,还会将福字倒转贴于门上,有"福到临门"的意思.为了方便找到合适的春联句子,我这里就采用现成的自然语言处理接口实现了一个自动生成春联的软件,输入提示词就可以完成春联的生成,方便写春联时参考. 下面是实现的效

  • 基于Java制作一个好玩的打飞机游戏

    目录 1.效果图 2.项目整体构造 3.主类代码展示 4.飞机类代码展示 5.炮弹类代码展示 6.爆炸类代码展示 1.效果图 2.项目整体构造 3.主类代码展示 public class MyGameFrame  extends  Frame {          Image   planeImg  = GameUtil.getImage("images/plane.png");     Image   bg  = GameUtil.getImage("images/bg.j

  • 基于C#设计一个双色球选号工具

    目录 窗体展示 开始后展示 结束摇色展示 代码 导入的命名空间 初始化私有变量 页面初始化 开始按钮 数字更新 结束按钮 结果显示 窗体展示 开始后展示 结束摇色展示 代码 导入的命名空间 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text;

  • 基于Java编写一个简单的风控组件

    目录 一.背景 1.为什么要做风控 2.为什么要自己写风控 3.其它要求 二.思路 1.风控规则的实现 2.调用方式的实现 三.具体实现 1.风控计数规则实现 2.注解的实现 四.测试一下 1.写法 2.Debug看看 一.背景 1.为什么要做风控 这不得拜产品大佬所赐 目前我们业务有使用到非常多的AI能力,如ocr识别.语音测评等,这些能力往往都比较费钱或者费资源,所以在产品层面也希望我们对用户的能力使用次数做一定的限制,因此风控是必须的! 2.为什么要自己写风控 那么多开源的风控组件,为什么

  • 基于Java制作一个简易的远控终端

    目录 远控终端的本质 Java制作简易的远控 1.环境 2.新建项目 3.新建一个Java类 4.编写程序 5.将项目打包成jar包并生成exe文件 附完整代码 远控终端的本质 1.服务端(攻击者)传输消息 ----> socket连接 ----> 客户端(被攻击者)接收消息 2.客户端执行消息内容(即执行服务端传回来的命令) 3.客户端传输执行结果 ----> socket连接 ----> 服务端显示命令执行结果 Java制作简易的远控 1.环境 环境:IntelliJ IDEA

  • 基于java编写局域网多人聊天室

    由于需要制作网络计算机网络课程设计,并且不想搞网络布线或者局域网路由器配置等等这种完全搞不懂的东西,最后决定使用socket基于java编写一个局域网聊天室: 关于socket以及网络编程的相关知识详见我另一篇文章:Java基于socket编程 程序基于C/S结构,即客户端服务器模式. 服务器: 默认ip为本机ip 需要双方确定一个端口号 可设置最大连接人数 可启动与关闭 界面显示在线用户人以及姓名(本机不在此显示) 客户端: 需要手动设置服务器ip地址(局域网) 手动设置端口号 输入姓名 可连

随机推荐