我们经常在线上分享信息。无论是文章、视频还是产品,网址(URLs)是访问这些内容的入口。然而,长网址分享起来可能会很麻烦,尤其是在像社交媒体或消息应用这样的平台上,这些平台有字数限制。这时像 bit.ly 和 TinyURL 这样的网址就派上用场了。
它们有助于将长链接缩短并使其更易读。这些缩短的链接不仅看起来更简洁,还使跨平台分享内容变得轻松自如。
现在你知道了这个秘密,想象全球每天有数百万人在缩短网址链接。这给底层系统带来了巨大的压力。而这正是系统架构设计大显身手的地方。
从这个关于系统设计的系列开始,我们的目标是剖析每个系统所涉及的功能性、架构、挑战、扩展性、系统API、性能和资源估算。我们将提供必要的洞察力,帮助你在实际情况中解决类似的问题。就像编程不仅仅是写代码一样,设计也意味着培养直觉并形成有条理的思考方式。因为没有清晰的设计思路,你无法写出好代码。
如果这还不行,请读到最后,文章末尾有一个特别惊喜,可能让你的系统设计技能大幅提升!
通过这一系列,我们希望提高你的设计感,并帮助你成为一个技术娴熟、有好奇心的工程师,能构建大规模系统。
如果你对这些概念完全不了解,或者这是你第一次听说它们,并且想要了解系统设计的基本模块的基础,我们为你准备好了相关内容。
我们正在做一个关于系统设计101入门系列的歌单——这是链接
如果你对系统设计不太了解,这里有一些很酷的东西你可以了解下。
如果你完全不了解这些概念,或者第一次听说这些概念,并希望首先掌握系统设计的基础知识,我们帮到你。在继续看下面的文章之前,你可以先看看下面的视频。
我们准备聊一聊系统设计方面的内容。
我们把它分成了7个模块
1. 功能性和非功能性需求
2. 资源估算 — 服务器、带宽、存储
3. 构建块的深入探讨为什么和怎么做
4. 系统API接口
5. 组件及其工作流程图
6. 编码方案及相关计算
7. 非功能性需求的映射
该系统的实际功能将包括以下几点。不了解需求的话,真的很难开始设计,是吧?
这些标准说明了系统应该如何表现,而不是它应该完成的任务。
在这部分,我们将通过一些数学计算来计算我们需要多少资源来支持我们的系统。这很重要,因为当我们在建造或开发某个系统时,我们需要预估它可以增长到什么规模,并提前规划存储、带宽、服务器等资源需求,等等。
我们来拆解一下:
存储估算:
由于条目保存期限为5年,且每月大约有2亿条目,因此总条目数将约为120亿。
因为每个条目是500字节,也就是说,总存储量估计为6 TB。
我们正在估算每秒会有多少个URL(统一资源定位符)。
这以后会帮助我们算带宽。
根据上述估计,我们预计每月会有20亿次重定向请求量,对应200万次网址缩短请求量。
我们每月处理2亿个URL缩短和200亿次重定向。
我们可以基于这个基准来计算我们系统的每秒查询数(QPS)。按照平均每月的天数,一个月大约有2,592,000秒。
采用1:100的比例进行重定向,每秒的URL重定向次数:
带宽(Bandwidth)= 数据流量 × 平均响应大小(Average Response Size)
请求缩短:我们预计每秒会有77个新网址。
这相当于每秒总的输入数据量将是这样的:大约308 Kbps。
重定向请求:预计的重定向速率为每秒7.7K URL,总数据输出量为30.8 M bps (比特每秒)。
入站带宽=308 Kbps
出站带宽 = 30.8 Mbps
在计算这个系统所需的大概服务器数量之前,让我们考虑一个小例子:
假设:
当我们说15个服务器可以满足5亿用户的需求时,从数学的角度来看,这似乎不太对。大型服务通常会使用更多的服务器,因为仅仅计算每秒的请求数量远远不能反映出服务器的全部需求。
此外,这种计算假设每个请求都是由一个单独的服务器处理的,但实际上,请求可能需要经过多种类型的服务器,包括网页服务器、应用服务器和存储服务器。
因此,来计算所需的服务器数目,我们将考虑以下两个因素:
一个更实际的数字是12,500台服务器。
我们需要估算一下内存需求,因为在我们想缓存一些频繁访问的重定向请求时就会用到内存。假设20%的重定向请求产生了80%的流量,且这些请求中存在80-20的分布。
由于我们只会缓存这些每日重定向请求的20%,所需的总内存大约为66GB。
那么,66GB的缓存容量就挺好的。
到了宣布的时候了。
如果你喜欢这些内容,你一定会超级喜欢我在YouTube频道上的内容。
我们为粉丝提供一系列专属福利,包括虚拟视频合作项目、会员专属投票、专为会员提供的高质量内容等,更多精彩内容即将推出,敬请期待。
在本文的背景下,我们的独家视频将引导您了解系统设计的所有重要方面,深入探讨各个组件,提供额外的数学概念理解,分享实用技巧,并提供一些实用策略,帮助您提升系统设计技能。
我是谁啊?
在过去7年里,我领导了涉及AWS、Java、Spring Boot、Kafka、ELK栈、Splunk、Apache Mesos等项目的实施,通过优化系统,提升了成本效益,实现了高达30%的成本节约,通过性能调优,延迟降低了50%,同时使系统能够处理更多的交易。
所以,我完全可以肯定你可以依赖我的专业知识。
除此之外,我们还将提供专门针对Java线程和并发常见面试问题的视频,详细的Spring Boot项目计划,以及许多项目灵感。所有这些特别内容都是专为你准备的哦!
支持我们频道的同时,加入我们日益壮大的社区,享受独家内容。
要立即加入我们,成为有价值的一员,请点击链接。
完成估算之后,我们就能识别出设计中的关键组件。
为了展示我们服务的功能性,我们可以使用 REST APIs 来实现以下功能特性:
我们可以按照以下定义创建短的新的网址。
插入成功后返回缩短的URL给用户。否则,系统会返回相应的错误代码给用户。
要将短网址重定向,该 REST API 的定义将是:
成功的重定向后将用户带到与之相关的该原始 URL。
同样,要删除短网址,REST API 的定义将如下所示。
删除成功后,系统会显示消息“URL已成功移除”
我们来看看我们需要存的是什么数据
用户数据:
短链接:
短链接: 通常只有7个字符的唯一短链接。
原始长链接: 原始的长链接。
为什么需要水平扩展?
对于像网址缩短这样的服务来说,水平扩展非常重要,因为它能够处理不断增长的流量和数据,而不会让系统超负荷。
NoSQL 数据库会是一个很好的选择,因为我们预计系统中会有大量的 读取 请求。此外,除了与创建 URL 的用户相关的详情外,存储的记录是独立的,它们之间没有其他联系。
MongoDB 挺不错的一个选择,因为:
一个 NoSQL 数据库非常适合我们的系统,因为它可以处理大量的读取而不变慢。它可以处理不同类型的数据,且扩容容易。此外,即使很多人同时访问,它也能快速找到我们需要的信息。
2.短链接生成器 :
我们的短链接生成器分为两部分:
序列生成器:
在 URL 缩短服务中,生成独一无二的缩短 URL 的唯一 ID 至关重要。这通常通过递增计数器或运用分布式算法跨多个服务器生成 ID 并避免冲突来实现。这确保了即使在高并发和快速的 URL 缩短请求下,每个缩短的 URL 都会获得一个唯一的 ID,避免冲突并确保服务的完整性。
Base-58 编码:
生成唯一的ID虽然重要,但创建易于阅读和分享的短URL对于用户体验来说同样重要。这时,Base-58编码就派上用场了。Base-58编码类似于Base-64编码,但是去除了0(零)、O(大写字母O)、I(大写字母i)和l(小写字母L)这样的字符,这样可以避免混淆并提高可读性。
3. 负载平衡
为了保证高可用性,我们将会用到。
为什么需要两者?
全球服务器负载均衡对于多个地理位置分散的数据中心或区域之间的传入流量分配至关重要。随着全球在线服务的普及以及用户对低延迟体验需求的增加,全球负载均衡对于在不同地区保持一致性能至关重要。它使公司能够有效地为全球各地的客户提供服务,并通过将流量从出现问题或中断的地区转移出去确保服务的高可用性。
局部负载均衡关注在一个数据中心或网络内部分配流量,以优化资源利用率、加快响应时间并避免单个服务器过载。
4. 缓存:Memcached 可以为我们的 读密集型 应用提供理想的缓存解决方案。在每个数据中心可以实现一个缓存层来处理来自附近的请求。我们之前也讨论过缓存存储的需求。
5. 速率限制: 每个用户将拥有一个唯一的密钥:api_dev_key
,该密钥将限制你在一段特定的时间内可以缩短和重定向链接的任务的数量。
我们来探索系统每个部分是如何一起工作来实现它们的功能。
url_key
存储在数据库中以便将来使用。url_key
及其记录将被删除;否则,会显示一个错误消息。下面是一张图解,如图所示:
编码在网址缩短过程中将原始数据,如字母和符号,转换为一种编码格式,这种编码格式更适合在互联网上发送。这种转换过程保证了创建的短网址对于URL来说是安全的,并且在使用或分享时不会出现问题。
有很多不同的编码方式:
我们系统采用Base58编码以提高可读性,并避免因视觉相似字符引起的混淆。
Base58 编码 (来源链接)
我们来看看如何将一个数从58进制转换为10进制,以及反过来怎么做。
将Base 58转Base 10:
十进制转五十八进制:
我们快速算一下最佳短网址长度。
长度为5的URL会生成58⁵ = 约6亿5600万个URL 长度为6的URL会生成58⁶ = 约38亿个URL
我们的系统生成12亿个URL(存储利用率考虑),使用6个base58字符可以生成大约38亿个URL。因此,每个生成的短链接将包含6个字符。
短链接的序列生成从哪里开始?
让我们来分解一下。当你给我们一个长长的网址需要缩短时,我们会用到一个叫SUG(短网址生成器)的工具。它由两部分构成:序列生成器和base 58编码器。
现在,当我们得到一个长链接时,我们首先通过序列器生成一串数字。然后将这些数字转换成base 58编码,这样就得到了一个由6个字符组成的短链接。但是有个问题:我们从哪里开始计数,以便正好得到6个字符的链接?这正是我们需要解决的问题。
通过逆向计算发现,要达到至少6个字符的base-58,相应的base-10数值大约是10亿。
这个数是:十进制数:1000000000
1000000000 % 58 = 18 17241379 % 58 = 9 297265 % 58 = 15 5125 % 58 = 21 88 % 58 = 30 1 % 58 = 1 以58为底的等效表示: [1][30][21][15][9][18] : 2XNGAK
所以,长度为6的 base 58 序列对应的最小 base 10 数字是 10亿(即 1,000,000,000)。短链接生成器将从 10亿 开始为任何用户生成短链接。
所以我们现在能理解,我们的设计能满足功能需求了。
那我们该怎么满足这些非功能性的需求呢?
让我们聊聊并做个总结。
MongoDB,在读取操作方面具有低延迟、高吞吐量
通过从分配给每个服务器的ID池中随机选择一个未使用过的唯一ID,这种方法不同于按顺序分配ID,从而降低了预测后续短网址的可能性。
此外,使用如SHA-256这样的加密哈希函数对原始URL进行哈希,然后将该哈希值编码成一个较短的表示会更佳。
这篇文章到此结束。
谢谢阅读。
如果你喜欢这篇文章的话,请多点几下 👏。
这让我有动力去创作更多类似的内容。请分享给你觉得这篇文章可能帮到的人。
联系我 — 瓦尔莎·达斯 |LinkedIn
如果你对此感到安心,我在软件工程、职业发展、核心Java、系统设计或面试准备方面提供个性化指导,让我们在这里连接 here。
请放心,我全心全意投入其中。我真心热爱解决复杂问题,提供量身定制的解决方案,并与来自各种背景的人互动。
订阅我的YouTube频道 — Code With Ease — By Varsha,在那里,我们聊聊Java、数据结构和算法等等话题。
点击这里订阅我的Medium账号,每次我发布新文章时,你都会收到通知。
学习快乐,一起成长。