Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >搭建短链接平台详细分析及具体代码实现

搭建短链接平台详细分析及具体代码实现

原创
作者头像
Mintimate
修改于 2021-08-12 10:14:57
修改于 2021-08-12 10:14:57
3.9K0
举报
文章被收录于专栏:Mintimate's BlogMintimate's Blog

作者:Mintimate

博客:https://www.mintimate.cn

Mintimate's Blog,只为与你分享

封面嗷
封面嗷

短链接

什么是短链接

短链接,又称缩略网址服务、缩址、短址、短网址、缩略网址、网址缩短、缩短网址、URL缩短等,指的是一种互联网上的技术与服务。此服务可以提供短URL以代替原来可能较长的URL,将长的URL地址缩短。

用户访问缩短后的URL时,通常将会重定向到原来的URL。

为什么用短链接

使用短链接,主要的场景有:

  • Twitter、微博等平台,消息字数限制,使用短链接对原有链接缩短
  • 隐藏Get、PATH参数。
  • ……

实例演示

有些小伙伴可能还是没有概念,这里举个腾讯云自带的短链接。比如腾讯云服务器限时秒杀活动的链接是:

代码语言:txt
AI代码解释
复制
https://cloud.tencent.com/act/cps/redirect?redirect=1077&cps_key=&from=console

而腾讯云给的短链接:

代码语言:txt
AI代码解释
复制
https://curl.qcloud.com/XnaFxKqr

可以看到,链接有效地缩短了。同时,已经看不到PATHGet参数。用户访问短链接,会自动301/302跳转到原链接:

腾讯重定向
腾讯重定向

实现思路

其实实现的思路很简单,我们生成一个短链接,大概的思路是传入原链接,在后台进行处理后,得到一个唯一识别码,一同存入数据库,最后再把这个唯一识别码回显给用户

生成短链接
生成短链接

得到短链接后,用户发给其他用户进行访问时,后台根据这个识别码,再进行数据库查询,最后重定向到原链接即可

解析短链接
解析短链接

所以,其实实现很简单,要点:

  • 生成唯一识别码,对应链接,且识别码要短。
  • 后台301/302重定向跳转

使用Java作为后台API服务,处理上面两点很简单:

  • 雪花ID转换为六十二进制,得到短的识别码。
  • 使用RedirectView设置响应头,并重定向链接。

本文以Java(Springboot)为例,其他编程语言可以按图索骥。

唯一识别码

每次后台接收前台的响应(即:长链接),则生成一个识别码存储到数据库,已备后续调取重定向。

这个识别码最好与时间戳有关,同时,如果有多个服务器同时组网,这个识别码最好还要加上机械识别码

综上,我们可以使用雪花ID,但是雪花ID作为一个Long类型,转换为int类型有19位,肯定是太长了,所以,我们还需要转码为六十二进制

雪花ID

雪花算法(Snowflake)是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs或snowflakes。这种算法由Twitter创建,并用于推文的ID。Discord和Instagram等其他公司采用了修改后的版本。

一个雪花ID:

  • 前41位是时间戳
  • 之后10位代表计算机ID
  • 其余12位代表每台机器上生成ID的序列号
雪花ID组成
雪花ID组成

参考代码:

代码语言:txt
AI代码解释
复制
/**
 * Twitter的SnowFlake算法,使用SnowFlake算法生成一个整数,然后转化为62进制变成一个短地址URL
 *
 * https://github.com/beyondfengyu/SnowFlake
 */
public class SnowFlakeShortUrl {

    /**
     * 起始的时间戳
     */
    private final static long START_TIMESTAMP = 1480166465631L;

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 12;   //序列号占用的位数
    private final static long MACHINE_BIT = 5;     //机器标识占用的位数
    private final static long DATA_CENTER_BIT = 5; //数据中心占用的位数

    /**
     * 每一部分的最大值
     */
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;

    private long dataCenterId;  //数据中心
    private long machineId;     //机器标识
    private long sequence = 0L; //序列号
    private long lastTimeStamp = -1L;  //上一次时间戳

    private long getNextMill() {
        long mill = getNewTimeStamp();
        while (mill <= lastTimeStamp) {
            mill = getNewTimeStamp();
        }
        return mill;
    }

    private long getNewTimeStamp() {
        return System.currentTimeMillis();
    }

    /**
     * 根据指定的数据中心ID和机器标志ID生成指定的序列号
     *
     * @param dataCenterId 数据中心ID
     * @param machineId    机器标志ID
     */
    public SnowFlakeShortUrl(long dataCenterId, long machineId) {
        if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {
            throw new IllegalArgumentException("DtaCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0!");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("MachineId can't be greater than MAX_MACHINE_NUM or less than 0!");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    /**
     * 产生下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long currTimeStamp = getNewTimeStamp();
        if (currTimeStamp < lastTimeStamp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currTimeStamp == lastTimeStamp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currTimeStamp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        lastTimeStamp = currTimeStamp;

        return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //时间戳部分
                | dataCenterId << DATA_CENTER_LEFT       //数据中心部分
                | machineId << MACHINE_LEFT             //机器标识部分
                | sequence;                             //序列号部分
    }
    
    public static void main(String[] args) {
        SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(2, 3);

        for (int i = 0; i < (1 << 4); i++) {
            //10进制
            System.out.println(snowFlake.nextId());
        }
    }
}

当然,如果你用使用Mybatis Plus,可以引用Mybatis Plus的IdWorker.getId方法,生成雪花ID。生成后的Long类型,我们使用十进制展开,应该是一个17-19位的数字。

六十二进制

因为雪花ID通过十进制展开是一个17-19位的数字,如果直接用来当作短链接,太长了点,我们需要对其缩短。

为了保证唯一,且可对照。我们转换为六十二进制。原因很简单:六十二进制使用A-Z、a-z和0-9组成。

把十进制,转换为六十二进制,能有效减短长度。

根据Wiki-Base62,十进制中0-61分别对应0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz。所以,我们编写编码和解码:

代码语言:txt
AI代码解释
复制
/**
     * 初始化 62 进制数据,索引位置代表转换字符的数值 0-61,比如 A代表1,z代表61
     */
    private static String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    /**
     * 进制转换比率
     */
    private static int SCALE = 62;

    /**
     * 匹配字符串只包含数字和大小写字母
     */
    private static String REGEX = "^[0-9a-zA-Z]+$";

    /**
     * 十进制数字转为62进制字符串
     *
     * @param val 十进制数字
     * @return 62进制字符串
     */
    public static String encode10To62(long val)
    {
        if (val < 0)
        {
            throw new IllegalArgumentException("this is an Invalid parameter:" + val);
        }
        StringBuilder sb = new StringBuilder();
        int remainder;
        while (Math.abs(val) > SCALE - 1)
        {
            //从最后一位开始进制转换,取转换后的值,最后反转字符串
            remainder = Long.valueOf(val % SCALE).intValue();
            sb.append(CHARS.charAt(remainder));
            val = val / SCALE;
        }
        //获取最高位
        sb.append(CHARS.charAt(Long.valueOf(val).intValue()));
        return sb.reverse().toString();
    }

    /**
     * 十进制数字转为62进制字符串
     *
     * @param val 62进制字符串
     * @return 十进制数字
     */
    public static long decode62To10(String val)
    {
        if (val == null)
        {
            throw new NumberFormatException("null");
        }
        if (!val.matches(REGEX))
        {
            throw new IllegalArgumentException("this is an Invalid parameter:" + val);
        }
        String tmp = val.replace("^0*", "");

        long result = 0;
        int index = 0;
        int length = tmp.length();
        for (int i = 0; i < length; i++)
        {
            index = CHARS.indexOf(tmp.charAt(i));
            result += (long)(index * Math.pow(SCALE, length - i - 1));
        }
        return result;
    }

再测试一下:

代码语言:txt
AI代码解释
复制
//Test
public static void main(String[] args) {
    Long snow = IdWorker.getId();
    System.out.println(snow);
    String str = encode10To62(snow);
    System.out.println(str);
    Long g = decode62To10(str);
    System.out.println(g);
}

输出:

代码语言:txt
AI代码解释
复制
1425664925648310274
1hJYkVByV0M
1425664925648310274

响应头

重定向链接,响应头很重要。Nginx内可以使用配置直接跳转301/302,比如强制HTTPS:

代码语言:txt
AI代码解释
复制
if ($server_port !~ 443){
    rewrite ^(/.*)$ https://$host$1 permanent;
}

而我们搭建短链接平台,也利用301或者302进行重定向:

雪花ID组成
雪花ID组成

301/302

301和302都是重定向,那它们的区别是什么呢?

  • 301:永久重定向,在请求的URL已被移除时使用,响应的location首部中应包含资源现在所处的URL
  • 302:临时重定向,和永久重定向类似,客户端应用location给出URL临时定位资源,将来的请求仍为原来的URL。

实际场景里,301在跳转后,浏览器会记住这个跳转,后续请求,不再请求原地址,而是直接请求新地址;所以301一般用于网站域名的迁移,强制网站https等,而302一般是网站维护,需要临时跳转到非维护页面等情况。

那我们搭建短链接平台,需要什么重定向呢?我认为是都可以。使用301重定向,可以减少服务器负载,而使用302重定向,可以方便我们统计链接实际调取次数。

Java内,进行301/302的跳转,其实很简单,使用类RedirectView,其中的HttpStatus即可:

代码语言:txt
AI代码解释
复制
# RedirectView类
RedirectView redirectView = new RedirectView(fullURL);
# 301跳转
redirectView.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
# 302跳转
redirectView.setStatusCode(HttpStatus.MOVED_TEMPORARILY);

实际上,看HttpStatus的源码,可以看到这里枚举了很多HTTP的响应头:

HttpStatus枚举
HttpStatus枚举

Maven部署(代码实现)

最后,我们看看实际部署和代码实现。只是随便提供思路,代码可能有逻辑不严谨地方嗷。

本次使用MariaDB作为数据库,使用Mybatis Plus对数据库进行操作,Springboot提供框架并方便打包。

依赖包

首先,我们创建一个工程,其中Lombok是为了方便实体类生成Set/Get方法:

代码语言:txt
AI代码解释
复制
<dependencies>
<!-- Springboot-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
<!-- MariaDB驱动-->
    <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
        <scope>runtime</scope>
    </dependency>
<!-- MybatisPlus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.0</version>
    </dependency>
<!-- lombok插件-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
<!-- Test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

实体类

我们看看短链接实体类:

代码语言:txt
AI代码解释
复制
@Data
@NoArgsConstructor
public class ShortUrl {
    @TableId
    private Long id;
    private String baseUrl;
    private String suffixUrl;
    private String fullUrl;
    private String shortCode;
    @TableField(fill = FieldFill.INSERT)
    private Integer totalClickCount;
    @TableField(fill = FieldFill.INSERT)
    private Date expirationDate;
    
    public ShortUrl(String baseUrl, String suffixUrl, String fullUrl) {
        this.baseUrl = baseUrl;
        this.suffixUrl = suffixUrl;
        this.fullUrl = fullUrl;
    }
}

其中:

  • baseUrl:用户提供的原链接域名,如:tool.mintimate.cn
  • suffixUrl:用户提供链接的参数,如:/user?login=yes
  • fullUrl:用户提供的原链接,如:https://tool.mintimate.cn/curl
  • shortCode:生成的短链接。
  • totalClickCount:统计点击次数(Hander自动设置默认值)
  • expirationDate:失效时间(Hander自动设置默认值)

短链接处理

首先,做一个控制器,用来接收用户请求:

代码语言:txt
AI代码解释
复制
// 接收原链接,返回短链接
@ResponseBody
@PostMapping(value = "/add")
public ShortUrl encodeURL(@RequestParam(value = "UserURL") String UserURL){
    String Domain = DomainUntil.getDomain(UserURL);
    if (Domain==null){
        return null;
    }
    return shortUrlService.saveURL(UserURL);
}

之后,看看业务层,我们需要对域名进行加工,先得到一个雪花ID,再对其转至六十二进制,并回显:

代码语言:txt
AI代码解释
复制
@Resource
ShortUrlMapper shortUrlMapper;
@Override
public ShortUrl saveURL(String UserURL) {
    // 创建新对象
    ShortUrl shortUrl=new ShortUrl(DomainUntil.getTopDomain(UserURL),DomainUntil.getFile(UserURL),UserURL);
    // 使用Mybatis Plus,提前拿到对象的雪花ID
    Long ID= IdWorker.getId(shortUrl);
    // 转六十二进制
    String Short_URL_CODE = DecimalAndSixtyBinary.encode10To62(ID);
    shortUrl.setShortCode(Short_URL_CODE);
    int code=shortUrlMapper.insert(shortUrl);
    return shortUrl;
}

这个时候,我们使用Postman来测试一下:

测试成功
测试成功

可以看到,测试成功。

短链接重定向

短链接重定向,就很简单了。我们写一个请求即可:

代码语言:txt
AI代码解释
复制
@ResponseBody
@RequestMapping(value = "/{UrlID}")
public RedirectView reverseURL(@PathVariable(value = "UrlID") String UrlID) {
    // 接收请求参数为String
    String fullURL=shortUrlService.findURL(UrlID);
    if (fullURL==null){
        // 定义的404页面
        RedirectView redirectView = new RedirectView("/error/404");
        redirectView.setStatusCode(HttpStatus.NOT_FOUND);
        return redirectView;
    }
    else {
        RedirectView redirectView = new RedirectView(fullURL);
        redirectView.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
        return redirectView;
    }
}

其中,shortUrlServicefindURL就是简单的JDBC查询,不具体实现。

Demo

我根据上述思路,初略搭建一个Demo,并部署在了腾讯云轻量应用服务器(不得不说,个人开发者,使用轻量应用服务器实在是太方便了,而且性价比极高):

  • 前端:基于Vue,使用element ui和Bootstrap
  • 后端:Springboot
在线演示
在线演示

我们可以在Linux/macOS上使用curl测试一下,比如直接用腾讯云轻量应用服务器的Linux远程终端:

代码语言:txt
AI代码解释
复制
curl -I "https://curl.mintimate.ml/1Hjsg8wDe8i"
在线演示
在线演示

我设置的响应头位301永久重定向,主要是不想做数据统计,如果想做数据统计,建议使用302重定向。

完善思路

可以看到,文章实现的有些粗糙,提供以下完善思路:

  • 限制单IP一段时间的请求频率:目前我是使用前端Vue进行控制,但是最好后端也进行控制。
  • 数据库优化:目前使用的是MariaDB,如果要更好的体验,或者响应数据量大,使用Redis会更好。(如果不知道怎么部署Redis,可以使用腾讯云的Redis
  • Cron定时任务:使用雪花ID转六十二进制,在链接长度上,还是有点长,但是安全性应该是很高的;如果降低安全性,并进一步缩短长度,可以创建Cron定时线程,无效旧的短链接
  • 原链接合法性检测:我代码内,只是判断URL是否合法,对其具体内容是否合法没用检测,建议可以在设计算法,检测网站内容。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
如何设计一个短链接系统
短链接是一种将长URL地址转换为较短、易于记忆的链接的技术。它通过使用特定的算法或服务将长链接压缩成更短的形式,以便在限制字符长度或需要更简洁的场景下使用。
柯柏技术笔记
2024/01/10
8660
如何设计一个短链接系统
面试官说:你来设计一个短链接生成系统吧
相信大家在生活中,特别是最近的双十一活动期间,会收到很多短信,而那些短信都有两个特征,第一个是几乎都是垃圾短信,这个特点此处可以忽略不计,第二个特点是**链接很短**,比如下面这个:
秦怀杂货店
2021/12/04
6240
短链接的实现
生活中,经常会在手机短信的广告中出现,因为短信服务本身对短信的长度有限制,如果使用一个非常长的链接,几百字符很快就能用完,关键信息的字符数被挤压,影响了服务方的广告价值同时也影响了消费者的观感,通过短链可以解决这个问题。
时光潜流
2023/10/22
5820
短链接的实现
如何实现一个短链接服务 | 短链接生成原理
短链接,通俗来说,就是将长的URL网址,通过程序计算等方式,转换为简短的网址字符串。
梦溪
2021/08/09
19.4K3
短链接的设计与实现
短链接的实现在生活中比较常见,比如我们接受到的广告短信,短信会包含他们的活动链接。
梁规晓
2020/11/05
2.1K0
短链接的设计与实现
框架篇:分布式全局唯一ID
每一次HTTP请求,数据库的事务的执行,我们追踪代码执行的过程中,需要一个唯一值和这些业务操作相关联,对于单机的系统,可以用数据库的自增ID或者时间戳加一个在本机递增值,即可实现唯一值。但在分布式,又该如何实现唯一性的ID
潜行前行
2021/07/23
7140
东半球最接地气的短链接系统设计
今天下午,烟哥和同事在厕所里排队等坑的时候(人多坑少)。想象一下一个场景,我正在一边排队,一边拿着手机撩妹。前面一个同事,拿着手机短信转过头来和我聊天。
Java3y
2019/11/12
6610
东半球最接地气的短链接系统设计
短链接生成太无聊?试试看长链接生成,URL地址变成乐谱音符🎵
用短链接替换较长的原始 URL,使得用户在访问网页或资源时可以使用更短、更便于记忆和分享的链接,也方便隐藏Get请求。
Mintimate
2023/09/23
6402
短链接生成太无聊?试试看长链接生成,URL地址变成乐谱音符🎵
小知识科普:随处可见的短ID和短网址
早上收到快递小哥的短信说快递被放在了A地的丰巢快递柜,然鹅这个快递柜我并不知道在哪里。
帅地
2019/11/22
1.7K0
小知识科普:随处可见的短ID和短网址
重发和重定向有什么区别与重定向应用
第一次,客户端request A,服务器响应,并response回来,告诉浏览器,你应该去B。这个时候IE可以看到地址变了,而且历史的回退按钮也亮了。重定向可以访问自己web应用以外的资源。在重定向的过程中,传输的信息会被丢失。
王小明_HIT
2020/09/29
8010
重发和重定向有什么区别与重定向应用
Java系列之雪花算法和原理
SnowFlake 算法:是 Twitter 开源的分布式 id 生成算法。 核心思想:使用一个 64 bit 的 long 型的数字作为全局唯一 id。 首先了解一下雪花ID的结构:从网上盗用一张;
沁溪源
2020/11/24
1.2K0
Java系列之雪花算法和原理
字节二面:100Wqps短链系统,如何设计?
这段时间,在整理知识星球中面试专栏时看到这么一个字节跳动的二面真题:100Wqps短链系统,怎么设计?
码猿技术专栏
2023/05/01
4.5K1
字节二面:100Wqps短链系统,如何设计?
剖析短链接工具开发原理与源码讲解
微博和Twitter都有140字数的限制,如果分享一个长网址,很容易就超出限制,同时长链接也占用了太多的字符空间,无法编辑更多的内容。另外,如国内微信、淘宝等等很多平台都是无法互通,平台之间都或多或少存在相互屏蔽的行为。同时,还有一个比较重要的因素,在我们日常网络营销中,当营销活动推出后,却很难去追踪用户与效果,基于这些种种的因素,才最终导致了如今短链接的盛行。
用户9229846
2021/11/29
1.2K1
剖析短链接工具开发原理与源码讲解
短链接服务Octopus的实现与源码开放
半年前(2020-06)左右,疫情触底反弹,公司的业务量不断提升,运营部门为了方便短信、模板消息推送等渠道的投放,提出了一个把长链接压缩为短链接的功能需求。当时为了快速推广,使用了一些比较知名的第三方短链压缩平台,存在一些问题:
Throwable
2020/12/29
1K0
雪花算法:分布式唯一ID生成利器
无论是在分布式系统中的ID生成,还是在业务系统中请求流水号这一类唯一编号的生成,都是软件开发人员经常会面临的一场景。而雪花算法便是这些场景的一个解决方案。
程序新视界
2022/05/06
1.2K0
雪花算法:分布式唯一ID生成利器
一口气说出 9种 分布式ID生成方式,面试官有点懵了
前两天粉丝给我留言吐槽最近面试:“四哥,年前我在公司受点委屈一冲动就裸辞了,然后现在疫情严重两个多月还没找到工作,接了几个视频面试也都没下文。好多面试官问完一个问题,紧接着说还会其他解决方法吗?能干活解决bug不就行了吗?那还得会多少种方法?”
用户4172423
2020/02/26
9860
短链接原理分析
1. 什么是短链接 顾名思义,短链接即是长度较短的网址。通过短链接技术,我们可以将长度较长的链接压缩成较短的链接。并通过跳转的方式,将用户请求由短链接重定向到长链接上去。短链接主要用在诸如微博,BBS
芋道源码
2018/12/18
3.4K0
一步步带你了解ID发号器是什么、为什么、如何做!
上一篇文章《面试必备:如何将一个长URL转换为一个短URL?》中谈到如何将长地址URL转换为短地址URL,其中谈到了一个比较理想的解决方案就是使用发号器生成一个唯一的整数ID,然后转换为62进制,作为短地址URL。
Java后端技术
2018/08/09
1.4K0
一步步带你了解ID发号器是什么、为什么、如何做!
短链接算法收集与分析
一般来说,第三步是我们比较头疼的,如何将一个长的URL字符串,映射成一个较短的字符串呢。我总结了三种办法:
大江小浪
2018/07/25
1.7K0
「System Design」设计一个短链接系统
短链接系统可以把比较长的 URL 网址转换成简短的网址字符串,短链接的优势是方便传播。适合在一些对字符串长度有要求的场景中使用,比如短信,微博等,比如
全球技术精选
2022/09/05
4370
「System Design」设计一个短链接系统
推荐阅读
相关推荐
如何设计一个短链接系统
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档