Bootstrap

短链接业务解决方案(附源码项目)

开源地址

https://github.com/lcy19930619/short-link
一个单节点短链接项目,有需要的拿去改改就行了,如果方便,可以帮忙点点star

什么是短链接

在这里插入图片描述
蓝色部分就是短链接

为什么要用短链接?

因为短信是按照字符去计算条数的,超过70字符就是两条短信,一条短信一毛钱,如果你用的链接比较长,嘿嘿,成本偏高

如何理解短链接的业务流程?

  • 首先,你要有一个待跳转的长链接地址,这个链接长度随你心情,多长都行,参数也是看你安排,可以携带用户唯一标识,比如,加密后的用户id
  • 使用雪花id,生成一个全局唯一的id
  • 使用签名算法,生成一个唯一的短key,长度短一些就行
  • 把这几个数据信息,存储到一张表中
  • 使用短key,和你的短链接跳转域名,拼接成一个短链接
  • 将这个短链接,发送给用户
  • 用户点击短链接,产生get请求,后端服务器拿到这个链接的key,去查询数据库,从而获得长链接地址
  • 利用重定向技术,实现从短链接向长链接跳转

301 302 如何选择

  • 如果你的业务需要跟踪用户点击,并且需要记录用户的多次点击情况,那么别犹豫,直接用301
  • 如果你仅仅只需要一个重定向,那就302,准没错

注:301、302 仅适用于 get 请求,post 请求需要使用307,在url重定向的过程中,只有 get 请求,301为临时重定向,多次点击依然会访问后端,302为永久重定向,同一个设备的同一个浏览器,仅访问一次后端

遇到的问题

雪花id存在的时钟回拨问题

服务器一般都会配置时间同步服务,但是这个同步时间时候,有个几毫秒的误差,如果并发量不大的情况下,还好,一但并发过高,就可能会导致出现重复的雪花id

数据存储过大问题

短链接数据量可能会很大,比如一次推送的用户比较多,而连接要存储很久,所以存储空间就会比较大

链接的可控性

一旦手抖,写了一个有问题的链接,要能对这一批链接进行处理,比如,重定向到404什么的,及时止血

数据记录

有很多业务需求,需要记录点击次数等要求,因此需要有新的记录表记录数据的点击时间

链接的安全性认证

每当用户点击此链接时,需要验证该短链接是否为当前用户专属,否则会泄露用户隐私

问题解决方案

问题一:雪花id时钟回拨问题

采用超卖思想,提前预支雪花id,单节点模式下,利用定时线程,将生成的雪花id存储到一个 Set 集合中,而后达到一定数量,存储到阻塞队列中,从而解决这个时钟回拨产生重复id问题
示例代码如下:

package net.jlxxw.link.component;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import javax.annotation.PostConstruct;
import net.jlxxw.link.util.SnowflakeIdWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

/**
 * 队列id 生成器,解决雪花id存在的时钟回拨问题
 * @author chunyang.leng
 * @date 2022-11-28 1:12 PM
 */
@Primary
@Component
public class QueueIdCenter implements IdCenter {
    private static final Logger logger = LoggerFactory.getLogger(QueueIdCenter.class);
    /**
     * 长度 20w 的 雪花id
     */
    private static final BlockingQueue<Long> QUEUE = new LinkedBlockingQueue<>(200000);

    @Autowired
    private  SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;


    @PostConstruct
    private void init(){
        threadPoolTaskExecutor.execute(()->{
            while (true) {
                try {
                    initQueue();
                }catch ( Exception e ){
                    logger.error("队列id生成器出现未知异常",e);
                }finally {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ignored) {

                    }
                }
            }
        });

    }



    /**
     * 获取id
     *
     * @return
     */
    @Override
    public Long getId() {
        try {
            return QUEUE.take();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    private void initQueue(){
        int size = 100000;
        if (QUEUE.size() < size){
            logger.info("初始化队列开始");
            Set<Long> set = new HashSet<>(size);
            while (set.size() < size){
                try {
                    long id = snowflakeIdWorker.nextId();
                    set.add(id);
                }catch (Exception ignored) {

                }
            }
            QUEUE.addAll(set);
            logger.info("初始化队列结束");
        }
    }
}

问题二:存储

短链接表中存储了雪花id字段值,必要时刻可以根据此字段进行分库分表,降低MySQL存储压力,也可以使用mongodb进行存储,同为b tree存储,mongo压缩性能比MySQL能好一些

问题三:止血

链接存储时增加一个字段,标识数据是否启用

问题四:数据记录

新建数据表,存储多条记录即可

问题五:隐私

生成短链接时,就要存储用户id,在重定向时,将短链接参数添加到url参数中,业务接口通过Http header中的 referer,获取短链接key,去查询并判断是否和当前用户相同

;