问题陈述:设计具有以下功能的水平可扩展且高并发的电影票预订平台。
- 显示用户所在城市的剧院中播放的活跃电影。
- 为用户提供一个选项,可以为每部电影添加评论和评分。
- 显示每部电影的平均评分和评论。
- 显示用户所在位置最近的剧院,以预订用户所在城市的电影。
要求
- 约一千万用户
- 约500万部电影的详细信息
- 约1千 预订/秒
- 大约1万 活动/秒(评分,评论)
平台功能
- 水平可扩展
- 高并发
- 微服务架构
- 容器化应用
- 使用Docker容器进行集成测试的生产级代码
一、技术选择与总体架构
数据库选择
我们要存储1000万个用户详细信息和500万个电影详细信息。我们正在寻找一个高度可用的数据库。我们可以协调用户详细信息和电影详细信息的一致性。存储此类大数据的最佳选择是Cassandra。
当用户打开应用程序时, 我们要显示用户所在城市的正在运行的电影列表。用户选择了电影后,我们想向用户显示用户所在城市中最近的剧院的列表,这些剧院将播放给定电影。我们无法使用Cassandra实现这一目标。我们需要一个搜索引擎。我们可以使用ElasticSearch解决这些用例。
Cassandra专为大量写入而设计。任何写操作都只是将数据添加到RAM中的Memtable中,并将数据附加到目标节点中的提交日志中。因此,我们可以将所有电影和剧院的详细信息存储在Cassandra中。Cassandra中的读取操作消耗大量的性能。读取必须通过内存和磁盘中的多个缓存来通过磁盘上的n个SSTable。因此,我们应尽量避免从Cassandra中读取内容。
而对于ElasticSearch,写操作的成本很高,因为每次我们在ElasticSearch中插入文档时,我们都在对该文档建立索引。因此,我们应该只存储希望在其上具有可搜索性的电影和剧院字段。我们不允许用户按Actor名称搜索电影,因此我们不应该在ElasticSearch中存储与电影关联的Actor。
在有可选项的情况下,我们不能在一致性上做出妥协,并且我们不希望多个用户在同一剧院预订相同的座位。因此,即使以可用性为代价,我们也需要一个高度一致的关系数据库。对于此用例,我们可以使用分片的SQL数据库。
技术
- Cassandra
- ElasticSearch
- Docker
- Apache Kafka
- Spring Cloud
- Hashicorp Consul
- Zookeeper
架构
- 由于这是一个容器化微服务架构,因此可以轻松地在Kubernetes集群上部署该架构。利用Kubernetes集群,可以根据传入流量自动放大或缩小moviebuzz服务。
- MovieBuzz网关:用于传入用户API请求的容器化应用程序身份验证和路由
- 负载均衡器服务/入口服务: Kubernetes服务,用户可以通过该服务访问moviebuzz-API。
- MovieBuzz API:所有面向用户和后台的API都将在此处实现。
- Apache Kafka:用户添加任何电影评论后,便可以将其添加到moviebuzz-user-reviews主题。该主题消息可以由多个处理器处理。例如。用户添加评论后,一个处理器可以更新平均评分。另一个处理器可以利用这个Kafka主题将电影推荐模型应用于用户评论。预订完成后,可以使用其他Kafka用例。可以将预订详细信息添加到moviebuzz-booking确认的主题中,可以处理此主题消息,以向用户发送预订确认电子邮件。
- MovieBuzz Kafka处理器:此服务用于处理kafka消息。仅增加处理器副本并不会增加kafka主题消息处理并行性。我们可以通过增加kafka分区数和增加处理器副本或增加每个副本的主题使用者线程来增加处理并行性。
- Hashicorp Consul:微服务架构是使用Spring Cloud开发的。Consul用于存储在单个位置运行的所有微服务的分布式配置。
二、数据库架构
Cassandra
我们可以使用NetworkTopologyStrategy在Cassandra多数据中心集群中创建Moviebuzz键空间,每个数据中心中至少有2个副本。
- 我们可以为所有表启用KEY缓存。
- 我们可以启用将Rows_per_partition缓存设置为1(每个分区最多可以容纳1个电影2)电影表的ROW缓存,因为在电影表上的读取数将比写入数多90%。
- 我们可以启用将row_per_partition缓存设置为10的movie_reviews和movie_bookings表的ROW缓存,因为如果用户单击“预订”选项卡,我们将仅向他显示10个最近的预订,如果用户单击电影,我们将仅向他显示10个最近的评论。(用户可以通过单击更多按钮来选择查看更多预订和评论)
- movie_ratings表上会有大量写操作。因此,我们将利用movie_ratings表的计数器列,并在内存LoadingCache中创建expire_after_write = 30mins,以获取5,000个最常访问的电影分级。
1)moviebuzz.movies:该表用于存储电影详细信息,例如描述,演员,工作人员,发行日期,类型等,并以电影UUID作为分区键。电影UUID是由电影名称和电影发行日期相结合生成的。
2)moviebuzz.theaters:该表用于存储剧院详细信息,例如名称,城市,位置,正在播放的电影列表等,并以剧院UUID作为分区键。剧院UUID由剧院名称和城市名称组合生成。
3)moviebuzz.users:用于存储用户详细信息的表,其中用户UUID为分区键。用户UUID是从用户电子邮件生成的。
4)moviebuzz.user_bookings:用于存储用户电影预订历史记录的表,每个预订具有唯一的bookingId。用户UUID用作分区键,bookingId用作群集列。
5)moviebuzz.movie_ratings:此表用于获取每部电影的平均评分。该表使用两个计数器列,一个计数器列用于存储对电影评分的用户数,另一计数器列用于存储电影的总评分。
6)moviebuzz.movie_reviews:用于存储所有用户为给定电影添加的电影评论的表。
ElasticSearch
- moviebuzz_movies索引:当用户打开应用程序时,我们要显示其城市中所有正在播放的电影的用户列表。另外,我们希望使用户能够按名称搜索电影。可以通过查询moviebuzz_movies索引来实现。
- moviebuzz_theatres索引:当用户单击电影时,我们希望向用户显示最近一次正在运行所选电影节目的剧院的列表。这可以通过将剧院的位置存储为moviebuzz_theatres索引中的geo_point,并通过电影名称和用户位置查询该索引来实现。
三、代码
仓库: https://github.com/SoundHearer/cloudnative
平台功能
- 水平可扩展
- 高并发
- 微服务架构
- 容器化应用
- 使用Docker容器进行集成测试的生产级代码
建立步骤
- 使用主机文件将mkafka和mzookeeper主机名映射到localhost
- 在moviebuzz-parent模块上运行maven build来生成JAR文件
mvn clean install
- 使用
`integrate''配置文件运行Maven构建以启动Docker容器并运行集成测试
mvn clean install -Pintegrate.` - 使用Integrate配置文件运行maven build之后,将创建docker映像,所有docker堆栈都将在本地docker主机上启动,包括Cassandra,ElasticSearch,Kafka,Consul,Processor和API。如果所有集成测试都通过,那么moviebuzz-integration模块测试将成功。
四、后续
待处理的任务:
- 集成SQL数据库以进行实时预订
- SQL分片数据库架构
文丨Soundhearer
图丨来源于网络