文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

从0到1剖析并编码实现短链系统

2024-12-02 07:54

关注

对于服务端研发,关于前端访问时的长短转换,其实只要知道有30X重定向基本也就可以了。

相较于重定向,我更关注的,是短链生成方式选型、存储选型、系统性能应对等方面的方案和设计。

Part one 短链系统分析

短链系统的最根本能力:是可以根据长链计算得到短链,以方便外部访问:

也可以根据短链映射到长链,寻找真实服务地址提供服务:

条条大路通罗马,系统方案有很多,但采取哪种最合适,还需要和存储策略以及访问性能联合起来一起看~

Part two 实现方案分析

Hash是最容易想到的实现策略之一,那么,Hash方式有哪些优缺点,我们又该怎么改进呢?

Hash策略关键点解析

首先,如果用hash方式来生成短链,那么短链是没法通过hash码反解出长链的,因此,必须存储短链和长链的关联关系;

其次,长链的长度一般又很长,不便于索引的构建,需要再生成一个长链的固定唯一短串来辅助存储和查询。( 如32位MD5压缩,加密算法一般不利于压缩,而压缩算法一般不可逆);

再次,hash难免会有冲突,需要对原始长链尾部拼接一个或多个固定串来消除冲突,因此,在访问长链时同样需要裁剪固定串。

Hash策略的存储设计

如果用mysql进行结构化存储比较简单:

id | 短链 | 长链MD5 | 长链 | 时间戳

并且需要给 短链 和 长链MD5 构建索引,供查询时使用。

如果用redis等非结构化kv存储,则需要存储多个关系用于查询:

长链MD5 -> 短链 |

短链 -> 长链MD5 |

长链MD5 -> 长链

改进-- 自增ID+高位进制法

如果说长链的MD5标识的生成和存储是不可缺少的,那么,可优化的点,就只能从短链 -> 长链MD5的转化这里来下手了。

有没有办法既可以省掉短链 -> 长链MD5的存储呢?

如果我们让唯一标识和短链之间可以通过计算相互编码解码,是不是就可以了!?既省了一部分存储,而且也省掉了查询存储的时间耗时,本地简单计算总归要比查询外部存储要快的多。

经常使用进制转换,可以知道,进制越高所占位数越少。

16进制转换示例

这个时候,用MD5应该就不太合适了,不好参与计算,因此,我们改用纯数字的分布式ID来代替MD5串(一般公司应该都有现成的分布式ID生成服务吧)。

利用进制转换虽然可以很方便编码成短链,但有时候,我们不希望出现 短链被轻松解码,导致服务端可被遍历,因此,需要考虑对进制转换进行加密处理。

编码实现带加密的进制转换

首先,需要选择高位进制:我们启用 0-9 a-z A-Z 全部的数字和字母,一共62位,即支持62进制转换

private static final String DIGITAL_STRING = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
//byte数组
private static final byte[] DIGITAL;
static {
DIGITAL = DIGITAL_STRING.getBytes(StandardCharsets.US_ASCII);
}

从10进制向62进制转换的编码实现:

public static String encode(long 分布式id) {
//内部复制
long value = id;
//创建短链所需长度的空间,这里可以限制短链长度为最多12位,最少6位
ByteBuffer buf = ByteBuffer.allocate(12);
//最多执行12次转换
for (int i = 0; i < 12; i++) {
//分布式ID对62取模
int mod = (int) (value % 62);
//加密, 再取模
int pos = (mod + (OFFSET << i)) % 62;
//根据模值从数组中获取对应的值,加入结果集中
buf.put(DIGITAL[pos]);
//求余数,用余数继续参加后续模操作,直到余数为0 或 短链长度达到要求
value = value / 62;
if (value==0 && i >= 6) {
break;
}
}
//从ByteBuffer中获取结果集合
byte[] result=new byte[buf.position()];
buf.rewind();
buf.get(result);
//反转顺序
ArrayUtils.reverse(result);
return new String(result);
}

重点关注(mod + (OFFSET << i)) % 62 这个操作,其目的是在模值上加上一个偏移量(偏移大小和所处位置有关,非固定偏移) ,用来防止被直接解码。

//将短链串解码为分布式ID
public static long decode(String code) {
long value = 0;
byte[] buf = code.getBytes();
int length = buf.length;
//循环次数为短链字符串长度
for (int i = 0; i < length; i++) {
byte digital = buf[i];
//当前字符对应的下标
int index = Arrays.binarySearch(DIGITAL, digital);
//当前下标需要减掉加密时增加的部分(同样和所处位置有关)
index = index - (OFFSET << (length - i - 1));
//因为减掉的有可能是一个非常大的值,再把index对62取余,如果任然小于0 ,就加上62(62进制内负变正)
index = index % 62;
if (index < 0) {
index = index + 62;
}
//10进制复原原值
value = value * 62 + index;
}
return value;
}

Part three 总结

每一种技术方案,都是通过不断论证和尝试才可以最终决定的。我们在学习一个技术架构时,最好可以从它的发展历程,每个瓶颈点的解决来进行整体把握,会对我们处理问题时候的入手角度和思考方式的锻炼起到很好的作用。

本文转载自微信公众号「Coder的技术之路」,可以通过以下二维码关注。转载本文请联系Coder的技术之路公众号。

来源:Coder的技术之路内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯