设计Pastebin
设计Pastebin
让我们设计一个类似于web服务的Pastebin,用户可以在其中存储纯文本。该服务的用户将输入一段文本,并获得一个随机生成的URL来访问它。类似服务:pastebin.com,chopapp.com。难度等级:简单
1. Pastebin是什么?
类似于Pastebin的服务允许用户通过网络(通常是Internet)存储纯文本或图像,并生成访问上传数据的唯一url。这类服务还用于通过网络快速共享数据,因为用户只需要传递URL就可以让其他用户看到它。
如果你以前没有使用过pastebin.com,请尝试创建一个新的“Paste”在那里,花一些时间通过不同的选项他们的服务提供。这将对你理解本章有很大帮助。
2. 系统的需求和目标
我们的Pastebin服务应满足以下要求:
功能需求:
- 用户应该能够上传或“Past”他们的数据,并获得一个唯一的URL来访问它。
- 用户只能上传文本。
- 数据和链接将在指定的时间范围后自动过期;用户也应该能够指定过期时间。
- 用户应该有选择地为他们的粘
Past
择一个自定义别名。
非功能性需求:
- 系统应高度可靠,上传的任何数据都不应丢失。
- 系统应该具有高可用性。这是必需的,因为如果我们的服务关闭,用户将无法访问他们的
Past
。- 用户应该能够以最小的延迟实时访问他们的
Past
。Past
链接不应该是可猜测的(不可预测的)。
扩展要求:
- 分析,例如,一个粘贴被访问了多少次?
- 其他服务也应该可以通过REST api访问我们的服务。
3.一些设计方面的考虑
Pastebin与URL Shortening服务共享一些需求,但还有一些额外的设计考虑我们应该记住。
用户一次可以粘贴的文本量的上限是多少? 我们可以限制用户的paste大小不超过10MB,以防止滥用该服务。
我们应该对自定义url设置大小限制吗? 由于我们的服务支持自定义URL,用户可以选择他们喜欢的任何URL,但提供自定义URL不是必须的。但是,对自定义URL施加大小限制是合理的(通常也是可取的),这样我们就有一个一致的URL数据库。
4. 容量估计和约束条件
我们的服务需要大量阅读;与创建新的paste
相比,会有更多的读请求。我们可以假设读写的比例为5:1。
流量估算:Pastebin服务预计不会有类似于Twitter或Facebook的流量,让我们在这里假设我们每天有100万个新粘贴添加到我们的系统。这样我们每天就有500万的阅读量。
每秒新Paste
次数:
Paste
每秒读的次数:
存储容量估算:用户最多可以上传10MB的数据;通常类似于Pastebin的服务
用于共享源代码、配置或日志。这样的文本并不大,所以让我们假设每个粘贴平均包含10KB。
按照这个速度,我们每天将存储10GB的数据。
带宽估计:对于写请求,我们期望每秒12个新Paste
,导致每秒120KB的输入。
对于读请求,我们期望每秒58个请求。因此,发送给用户的总数据出口为0.6 MB/s。
返回:(字符串)
成功的插入将返回可以访问Paste
的URL,否则将返回错误代码。
类似地,我们可以使用检索和删除Paste api:
getPaste(api_dev_key, api_paste_key)
其中“api_paste_key”是一个字符串,表示要检索的粘贴的粘贴密钥。这个API将返回粘贴的文本数据。
deletePaste(api_dev_key, api_paste_key)
删除成功返回' true ',否则返回' false '。
6. 数据库设计
以下是对我们所存储数据性质的几点观察:
- 我们需要存储数十亿条记录。
- 我们存储的每个元数据对象都很小(小于100字节)。
- 我们存储的每个
Paste
对象可以是中等大小(可以是几MB)。- 记录之间没有关系,除非我们想存储哪个用户创建了什么 Paste。
- 我们的服务阅读量很大。
数据库模式:
我们需要两个表,一个用于存储关于paste的信息,另一个用于存储用户数据。
- paste信息表
URLHash: varchar(16)
ContentKey: varchar(512)
Name: varchar(20)
ExpirationDate: datatime
Email: varchar(32)
CreationDate: datetime
- 用户数据表
UserID: int
Name: varchar(20)
Email: varchar(32)
CreationDate: datetime
LastLogin: datatime
高级设计
在较高的层次上,我们需要一个应用程序层来处理所有的读写请求。应用层与存储层通信以存储和检索数据。我们可以将存储层隔离为一个数据库存储与每个粘贴、用户等相关的元数据,而另一个数据库将粘贴内容存储在某个对象存储(如Amazon S3)中。这种数据划分还允许我们单独地扩展它们。
8. 组件设计
我们的应用程序层将处理所有传入和传出请求。应用服务器将与后端数据存储组件进行通信以处理请求。
a.应用程序层
如何处理写请求? 在接收到写请求时,我们的应用服务器将生成一个6个字母的随机字符串,它将作为粘贴的密钥(如果用户没有提供自定义密钥)。然后,应用服务器将在数据库中存储粘贴的内容和生成的密钥。成功插入之后,服务器可以将密钥返回给用户。这里可能出现的一个问题是由于重复的键导致插入失败。因为我们正在生成一个随机密钥,所以新生成的密钥有可能与现有的密钥相匹配。在这种情况下,我们应该重新生成一个新密钥并重试。我们应该继续尝试,直到我们没有看到由于重复的密钥而失败。如果用户提供的自定义密钥已经存在于我们的数据库中,则应该向用户返回一个错误。
上述问题的另一个解决方案可能是运行一个独立的密钥生成服务(KGS),它事先生成随机的6个字母字符串,并将它们存储在数据库中(我们称之为Key - db)。每当我们想要存储一个新的粘贴时,我们只需要取一个已经生成的键并使用它。这种方法将使事情变得非常简单和快速,因为我们不需要担心重复或碰撞。KGS将确保key-DB中插入的所有键都是惟一的。KGS可以使用两个表存储键,一个用于尚未使用的键,另一个用于所有已使用的键。一旦KGS将一些密钥提供给应用服务器,它就可以将这些密钥移动到使用的密钥表。KGS可以始终在内存中保留一些键,以便在服务器需要时快速提供它们。一旦KGS在内存中加载了一些键,它就可以将它们移动到已使用的键表中,这样我们就可以确保每个服务器获得唯一的键。如果KGS在使用内存中加载的所有键之前死亡,我们将浪费这些键。我们可以忽略这些键因为它们的数量非常多。
KGS难道不是单点故障吗? 是的,它是。为了解决这个问题,我们可以有一个KGS的备用副本,当主服务器死亡时,它可以接管生成并提供密钥。
每个应用服务器可以从key-DB缓存一些键吗? 是的,这肯定能加快速度。尽管在本例中,如果应用服务器在使用所有密钥之前死亡,我们将最终丢失这些密钥。这是可以接受的,因为我们有68B个唯一的6个字母的键,这比我们需要的多得多。
它如何处理粘贴读取请求? 在接收到读粘贴请求后,应用程序服务层与数据存储联系。数据存储将搜索键,如果找到键,则返回粘贴的内容。否则返回错误码。
b.数据存储层
我们可以将数据存储层分为两层:
- 元数据数据库:我们可以使用像MySQL这样的关系数据库,或者像Dynamo或Cassandra这样的分布式键值存储。
- 对象存储:我们可以将内容存储在类似Amazon S3的对象存储中。当我们想要达到内容存储的最大容量时,我们可以通过添加更多的服务器来轻松地增加容量。