📜  设计媒体分享社交网络系统

📅  最后修改于: 2021-09-10 02:40:37             🧑  作者: Mango

媒体社交网络服务系统的目的
该系统将允许用户与其他用户共享照片和视频。此外,用户可以根据关注请求关注其他用户,他们可以查看其他用户的照片和视频。在此系统中,您可以搜索用户并查看他们的个人资料(如果他们的帐户是公开的)。否则,您需要发送关注请求。
在开始设计任何像照片和视频共享社交网络服务系统这样的系统之前,建议详细考虑系统边界和需求,并尝试了解未来(例如 5 年或 10 年)系统容量是多少。这一点很关键,因为如果系统的用户数量呈指数增长,系统的容量将不足以提供快速响应。在建筑设计的背后,你必须考虑一些支柱。这些都是;
– 可用性
– 可靠性
– 弹性
– 耐用性
– 性价比
这些是我们应该一起考虑的支柱,因为它们是相互耦合的。简而言之,可用性意味着系统应该始终可用。可靠性意味着系统应该按预期工作。弹性意味着如果出现任何问题,系统将如何以及何时恢复自身。持久性是系统每个部分应该存在的支柱,直到我们删除。性价比也是一个重要的话题,基本上与在成本效率下使用服务有关。可以这样说,如果系统将建立在AWS上并且使用t2微型EC2实例就足够了,那么就没有任何理由使用更大的EC2实例并支付额外费用。
要求和系统边界
如果要设计系统,首先必须定义需求和系统边界。可能您将拥有一份服务设计文档,您将在此服务设计文档中定义需求、边界、架构决策和其他内容。但基本上,照片和视频共享社交网络系统将是一种用户可以与其他用户共享图像和视频的服务。用户可以拥有公共或私人帐户,这意味着如果您拥有公共帐户,则其他用户可以看到您的图像/视频(无论您是否有关系)。但如果您有私人帐户,那么您的图片/视频将仅对您的朋友可见。所以你的系统将支持这些功能;
– 用户必须能够创建帐户。
– 每个注册用户必须有自己的个人账户页面。
– 用户必须能够登录系统和退出系统。
– 用户必须能够在他们的时间线中看到其他用户的照片和视频。
– 用户必须能够在登录后上传照片和视频。
– 用户必须能够在登录后删除他们的照片和视频。
– 用户必须能够搜索用户。
– 系统必须能够支持公共和私人帐户。
– 用户必须能够向其他用户发送关注请求。
– 用户必须能够接受或拒绝关注请求。
– 用户必须能够在需要时删除他们的帐户。
– 用户必须能够喜欢其他用户的照片和视频。
– 系统应该是高可用的
– 系统应高度可靠
– 系统应该是耐用的
– 系统应该是有弹性的
– 系统应该具有很高的成本和性能效率
在定义系统边界和功能要求时,需要考虑云或承诺选项。你的系统可以;
– %100 承诺(您自己的数据中心/服务器)
– %100 云(AWS、谷歌云、Azure)
– 承诺和云的混合(您可以在迁移过程中同时拥有)
如今,云服务凭借着云机制的优势而大受欢迎。这些优势;
– 成本效益
– 高速
– 安全
– 备份解决方案
– 无限存储容量
– 许多不同的服务选项。你不需要从头开始创造世界
– 可靠性
– 耐用性
– 弹性
– 监控几乎所有服务
– 与其他服务的轻松软件集成
– 来自云供应商的维护等等……
让我们考虑一下设计边界;
– 服务将是大量写入和大量读取的。
– 服务将保持一致和可靠,这意味着不应有任何数据丢失。 – 服务将是持久的,这意味着系统的所有部分都应该存在,直到它们被手动删除。
在定义容量考虑之前,您必须定义服务的目的是什么。即使它对于承诺服务更重要,它对于承诺服务和云服务也是必不可少的,因为您可以根据目的选择正确的服务,根据可用区域定位它们并定义容量。这样的例子是;
– 创建比写服务更多的读服务。
– 根据操作类型选择服务器类型。
– 根据您的容量估计定义缓存策略。
– 根据您的要求选择数据库类型(SQL、NoSQL)。
– 根据您的容量估计定义备用解决方案。
– 根据您的要求等定义数据分片策略……
假设您的用户总数为 1 亿。在您的系统中,我们假设下载数据比上传数据重,假设读写比例为 10:3。
我们假设照片的平均大小为 200 KB,视频的平均大小为 25 MB,因此系统将具有;
5年内的照片容量;
– 5 * 100M * 10 * 200KB = 1 PB。 (假设每个用户每年将上传 10 张照片)。
– 12PB 用于复制和备份。
这样5年内照片总容量将达到3PB。
5年内的视频容量;
– 5 * 100M * 1 * 25 MB = 12 PB。 (假设每个用户每年将上传 1 个视频)。
– 36 PB 用于复制和备份。
此计算只是如何定义系统容量的一个简短示例,我们不会计算每日下载/上传容量和元数据容量,但您应该考虑此计算(以及每日读/写容量估计)以进行服务/数据库扩展。
API设计
我们可以使用 REST 或 SOAP 来为我们的 API 提供服务。基本上,照片和视频共享服务系统将有三个重要的API。
1- PostMedia (api_dev_key, media_type, media_data, title, description, tags[], media_details)
PostMedia 将负责上传照片或图像。 api_dev_key 是注册账号的 API 开发者密钥。我们可以通过 api_dev_key 消除黑客攻击。此 API 返回 HTTP 响应。 (如果成功则接受 202)
2- GetMedia(api_dev_key、media_type、search_query、user_location、page、maximum_video_count = 20)
返回包含有关照片和视频列表的信息的 JSON。每个媒体资源都会有一个标题、创建日期、像计数、总观看次数、所有者和其他元信息。
3- DeleteMedia(api_dev_key,ID,类型)
检查用户是否有权删除媒体。如果操作已排队,它将返回 HTTP 响应 200(OK)、202(已接受)或基于您的响应的 204(无内容)。
**有更多的 API 来设计照片和视频共享服务,但是,这三个 API 比其他 API 更重要。其他 API 会像媒体、搜索、推荐等……
数据库架构
您可以将数据库部分分为两部分。第一部分将与如何以安全的方式保存图像/视频有关,第二部分将与如何将图像/视频元数据和用户信息/用户现实数据保存在数据库中有关。视频和图像是静态数据,因此您可以将图像/视频保存在图像存储中。您可以使用 Chomecast 等 3rd 方服务,或者如果您使用的是 AWS,则可以在 S3 上存储真实的媒体文件。 S3 将根据您的策略提供不同类型的存储。为了说明这一点,S3 将提供 S3 标准、s3 不常访问、S3 Glacier 等……如果我们考虑 Instagram 我们可以开始使用 S3 标准来保存图像/视频,如果他们在今年上传,第一年之后我们可以移动它们很少访问 S3,10 年后我们可以将它们移至 S3 Glacier。这使系统具有成本效益,因为尽管 S3 标准是 AWS 中最便宜的服务之一,但 S3 不经常访问比 S3 标准便宜。此外,我们 S3 Standard 和 S3 Infrequently access 会自动将数据保存在不同的可用区(如数据中心)中,这样您就不必担心可靠性。但是最好将镜像数据(复制)保持在不同的区域以增加数据冗余。此外,将 Cloudfront 用作分布式缓存层以减少读取/访问时间会很好。 Cloudfront 是一种分布式 AWS 缓存服务,位于不同的边缘站点。您可以使用 cloudfront 读取和写入选项。
对于用户,您可以使用 RDBMS 或 NoSQL。我们可以使用图形数据库,因此用户之间会有很强的关系。为此,AWS Neptune 或 Neo4j 可能是合适的数据库。在 MySQL 或 PostgreSQL 上设计;
用户:
用户名:INT
昵称:NVARCHAR(50)
密码:VARCHAR(255) 带哈希函数
电子邮件:NVARCHAR(50)
生日:日期时间
注册日期:日期时间
最后登录日期:日期时间
主键:USERID
用户关系
身份证:INT
追随者:INT
以下ID:INT
主键:ID
外键:FOLLOWERID、FOLLOWINGID 与用户表
对于后期元数据,您可以使用 RDBMS,如 MySQL 或 PostgreSQL。
邮政
身份证:INT
用户名:INT
MEDIA_TYPE_ID:INT
路径:NVARCHAR(100)
描述:文本
可见性:布尔值
添加日期:日期时间
VIEWS_COUNT:INT
主键:ID
外键:带用户表的 USERID
外键:MEDIA_TYPE_ID 和 Media_Type 表
主键:(ID,类型)
外键:带有媒体表的 MEDIAID
用户喜欢
身份证:INT
媒体:INT
用户名:INT
主键:ID
外键:带用户表的 USERID
外键:带有媒体表的 MEDIAID
评论
身份证:INT
媒体:INT
用户名:INT
评论:NVARCHAR(256)
主键:ID
外键:带用户表的 USERID
外键:带有媒体表的 MEDIAID
当然,我们会有更多的数据库表,这些只是示例。遵循数据库设计过程的规范化规则会很好。
** 我们将在 AWS S3 中存储照片/视频。我们也可以使用 S3 生命周期规则来提高成本效率。
** 我们可以使用Cassandra,基于列的数据存储,来保存用户的跟进。
注意:很多 NoSQL 数据库都支持复制。
注意:我们可以在 Media Table 上创建二级索引——ADDEDDATE 字段,因为我们需要获取最新的媒体文件。
系统设计考虑
– 系统将具有缓存机制以在下载媒体文件时快速响应。
– 系统最终会保持一致,但我们将有缓存驱逐策略来清理缓存。
– 系统将有推送通知机制向用户发送信息(比如用户喜欢照片/视频)。
– 系统将 Cloudfront 作为 CDN。 Cloudfront 位于边缘位置,因此响应时间会很快。我们可以使用 Cloudfront 进行下载和上传。
– 系统将使用 NGinx 作为负载均衡器,我们将实施智能路由算法以仅发送健康服务请求。
– 系统将有预先生成的服务为用户创建时间线。
– 系统会保存多个数据和文件。 (复制、备份)
– 系统将有监控机制。如果系统组件出现故障,系统将根据警报考虑发送警报
– 系统将支持代码流水线机制。我们可以使用 AWS Codecommit、Codebuild、CodeDeploy 和 CodePipeline。

高级系统设计
如果我们正在设计一个系统,我们需要的基本概念是;
– 客户
– 服务
– 网络服务器
– 应用服务器
– 媒体文件存储
– 数据库
– 缓存
– 复制
– 冗余
– 负载均衡
– 分片
该系统中有两个独立的服务,分别是上传/下载媒体。媒体存储用于保存静态媒体内容。数据库用于保存有关用户和媒体内容的所有元数据。当一个请求到达系统时,它会首先到达 Web 服务器。 Web 服务器将传入请求重定向到应用程序服务器。
复制和备份是提供我们之前提到的支柱的两个重要概念。复制是处理服务或服务器故障的一个非常重要的概念。复制可以是应用数据库服务器、Web 服务器、应用服务器、媒体存储等。实际上我们可以复制系统的所有部分。 (某些 AWS 服务,例如 Route53,它们本身具有高可用性,因此您无需处理 Route53、负载均衡器等的复制。)请注意,复制还有助于系统缩短响应时间。你想象一下,如果我们将传入的请求分成更多的资源而不是一个资源,系统可以轻松满足所有传入的请求。此外,每个资源的最佳副本数为 3 个或更多。您可以通过将数据保存在 AWS 中的不同可用区或不同区域来提供冗余。
对于缓存策略,我们可以通过使用缓存服务器来使用全局缓存机制。我们可以使用 Redis 或 memcache,但缓存策略最重要的部分是如何提供缓存驱逐。如果我们使用全局缓存服务器,我们会保证每个用户在缓存中看到相同的数据,但是如果我们使用全局缓存服务器,就会有时间延迟。作为缓存策略,我们可以使用 LRU(最近最少使用)算法。
对于媒体文件缓存,正如我们之前提到的,我们将使用 CDN。 CDN 位于不同的边缘位置,因此响应时间将比直接从 AWS S3 获取媒体内容要短。
在这种服务中分片 ID 总是很困难,因为会有大量的数据,但你可以检查;
a href=”https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c/”>
负载均衡器允许根据特定标准将传入请求重定向到资源。我们可以在系统的每一层使用负载均衡器。如果我们想使用 AWS 负载均衡器服务,AWS 将支持三种不同的负载均衡器类型:
– 网络负载均衡器
– 经典负载均衡器(已弃用)
– 应用程序负载均衡器
对于此服务,应用程序负载均衡器将适合我们的服务,它还将自行处理 AZ 分配。否则你可以使用NGinx,但你必须实现算法,如果我们想使用NGinx,你必须提供维护。
我们可以使用负载均衡器;
– 在请求和 Web 服务器之间。
– 在 Web 服务器和应用程序服务器之间。
– 应用服务器和数据库之间
– 在应用服务器和图像存储之间。
– 在应用服务器和缓存数据库之间。
– 我们可以对负载均衡器使用轮询方法。 Round Robin 方法可以防止请求进入死服务器,但 Round Robin 方法不处理任何服务器流量大的情况。我们可以修改 Round Robin 方法,使其成为一种更智能的方法来处理这个问题。
基本编码示例

Java
// Java Program to explain the design
 
public enum InvitationStatus{
  PENDING,
  ACCEPTED,
  REJECTED,
  CANCELED
}
 
public enum AccountStatus{
  PUBLIC,
  PRIVATE,
  CLOSED
}
 
public enum MediaStatus {
  PUBLIC,
  PRIVATE
}
 
public enum MediaType {
  PHOTO,
  VIDEO
}
 
public class AddressDetails {
  private String streetAddress;
  private String city;
  private String country;
  ...
}
 
public class AccountDetails {
  private Date createdTime;
  private AccountStatus status;
  private boolean updateAccountStatus(AccountStatus accountStatus);
  ...
}
 
public class Invitation {
  private Integer userID;
  private InvitationStatus status;
  private Date sentDate;
 
  public boolean updateInvitation(InvitationStatus status);
  ...
}
 
public class PendingInvitation extends Invitation{
  public boolean acceptConnection();
  public boolean rejectConnection();
  ...
}
 
public class UserRelations {
  private HashSet userFollower;
  private HashSet userFollowing;
  private HashSet connectionInvitations;
  ...
}
 
public class Comment {
  private Integer id;
  private User addedBy;
  private Date addedDate;
  private String comment;
 
  public boolean updateComment(String comment);
  ...
}
 
public class Media {
  private Integer id;
  private User createdBy;
  private MediaType mediaType;
  private String path;
  private MediaStatus mediaStatus;
  private int viewsCount;
 
  private HashSet userLikes;
  private HashSet userComments;
  ...
}
 
public class User {
  private int id;
  private String password;
  private String nickname;
  private String email;
  private AddressDetails addressDetails;
  private AccountDetails accountDetails;
  private UserRelations userRelations;
  private HashSet invitationsByMe;
  private HashSet invitationsToMe;
 
  public boolean updatePassword();
  public boolean createMedia(Media media);
  public boolean updateMedia(int mediaId, MediaStatus mediaStatus);
  public boolean sendInvitation(ConnectionInvitation invitation);
  public List searchUser(string term);
  public List searchMedia(string term);
  ...
}