爱喝冬瓜汤的萝卜

导航

设计Pastebin

设计Pastebin

让我们设计一个类似于web服务的Pastebin,用户可以在其中存储纯文本。该服务的用户将输入一段文本,并获得一个随机生成的URL来访问它。类似服务:pastebin.com,chopapp.com。难度等级:简单

1. Pastebin是什么?

    类似于Pastebin的服务允许用户通过网络(通常是Internet)存储纯文本或图像,并生成访问上传数据的唯一url。这类服务还用于通过网络快速共享数据,因为用户只需要传递URL就可以让其他用户看到它。
    如果你以前没有使用过pastebin.com,请尝试创建一个新的“Paste”在那里,花一些时间通过不同的选项他们的服务提供。这将对你理解本章有很大帮助。

2. 系统的需求和目标

我们的Pastebin服务应满足以下要求:

功能需求:

  1. 用户应该能够上传或“Past”他们的数据,并获得一个唯一的URL来访问它。
  2. 用户只能上传文本。
  3. 数据和链接将在指定的时间范围后自动过期;用户也应该能够指定过期时间。
  4. 用户应该有选择地为他们的粘Past择一个自定义别名。

非功能性需求:

  1. 系统应高度可靠,上传的任何数据都不应丢失。
  2. 系统应该具有高可用性。这是必需的,因为如果我们的服务关闭,用户将无法访问他们的Past
  3. 用户应该能够以最小的延迟实时访问他们的Past
  4. Past链接不应该是可猜测的(不可预测的)。

扩展要求:

  1. 分析,例如,一个粘贴被访问了多少次?
  2. 其他服务也应该可以通过REST api访问我们的服务。

3.一些设计方面的考虑

    Pastebin与URL Shortening服务共享一些需求,但还有一些额外的设计考虑我们应该记住。
    用户一次可以粘贴的文本量的上限是多少? 我们可以限制用户的paste大小不超过10MB,以防止滥用该服务。
    我们应该对自定义url设置大小限制吗? 由于我们的服务支持自定义URL,用户可以选择他们喜欢的任何URL,但提供自定义URL不是必须的。但是,对自定义URL施加大小限制是合理的(通常也是可取的),这样我们就有一个一致的URL数据库。

4. 容量估计和约束条件

    我们的服务需要大量阅读;与创建新的paste相比,会有更多的读请求。我们可以假设读写的比例为5:1。
流量估算:Pastebin服务预计不会有类似于Twitter或Facebook的流量,让我们在这里假设我们每天有100万个新粘贴添加到我们的系统。这样我们每天就有500万的阅读量。
每秒新Paste次数:

100万 / (24hour * 3600秒) = ~ 12 pastes/s

Paste每秒读的次数:

500万 /(24hour * 3600秒) = ~58 paste/s

存储容量估算:用户最多可以上传10MB的数据;通常类似于Pastebin的服务
用于共享源代码、配置或日志。这样的文本并不大,所以让我们假设每个粘贴平均包含10KB。
按照这个速度,我们每天将存储10GB的数据。

100万 * 10 KB = 10GB/day
如果我们想要存储这些数据十年,我们将需要36TB的总存储容量。     每天有100万个`Paste`,10年后我们将有36亿个`Paste`。我们需要生成和存储键来惟一地标识这些粘贴。如果我们使用base64编码([A-Z, A-Z, 0-9, ., -]),我们将需要六个字母字符串:  
64 ^6 = 687亿
    如果存储一个字符需要一个字节,则存储36亿键所需的总大小为:
36亿 * 6B = 22G
    与36TB相比,22GB可以忽略不计。为了保留一些剩余空间,我们将假设一个70%的容量模型(这意味着我们在任何时候都不希望使用超过总存储容量的70%),这将使我们的存储需求提高到51.4TB。

带宽估计:对于写请求,我们期望每秒12个新Paste,导致每秒120KB的输入。

12*10K == ~ 120KB/s

对于读请求,我们期望每秒58个请求。因此,发送给用户的总数据出口为0.6 MB/s。

58 *10KB = 0.6 MB/s
虽然总入口和出口并不大,但我们在设计服务时应该牢记这些数字。 **内存估计**:我们可以缓存一些经常被访问的热`Paste`。遵循80-20规则,即20%的热`Paste`产生80%的流量,我们希望缓存这20%的`Paste` 由于我们每天有5M个读请求,要缓存其中的20%,我们需要:
500万*10KB * 0.2= 10G
## 5. 系统api 我们可以使用SOAP或REST api来公开服务的功能。下面是创建/检索/删除`Paste`的api定义: `addPaste(api_dev_key, paste_data, custom_url=None user_name=None, paste_name=None, expire_date=None)` **参数**: > api_dev_key (string): 注册帐户的API开发者密钥。这将被用来,在其他事情的基础上,限制用户分配的配额。 > paste_data (string): `Paste`的文本数据。 > custom_url (string): 可选的自定义URL。 > user_name (string): 可选用户名,用于生成URL。 > paste_name (string): `Paste`的可选名称 > expire_date (string): 可选的`Paste`过期日期。

返回:(字符串)
成功的插入将返回可以访问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. 数据库设计
以下是对我们所存储数据性质的几点观察:

  1. 我们需要存储数十亿条记录。
  2. 我们存储的每个元数据对象都很小(小于100字节)。
  3. 我们存储的每个Paste对象可以是中等大小(可以是几MB)。
  4. 记录之间没有关系,除非我们想存储哪个用户创建了什么 Paste。
  5. 我们的服务阅读量很大。

数据库模式:

我们需要两个表,一个用于存储关于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.数据存储层

    我们可以将数据存储层分为两层:

  1. 元数据数据库:我们可以使用像MySQL这样的关系数据库,或者像Dynamo或Cassandra这样的分布式键值存储。
  2. 对象存储:我们可以将内容存储在类似Amazon S3的对象存储中。当我们想要达到内容存储的最大容量时,我们可以通过添加更多的服务器来轻松地增加容量。

posted on 2022-06-30 19:04  爱喝冬瓜汤的萝卜  阅读(154)  评论(0编辑  收藏  举报