学习

示例应用程序概述

Simon Prickett
作者
Simon Prickett, Redis 首席开发者倡导者

在本课程中,我们将探讨如何在示例应用程序的背景下使用 Redis 作为数据存储和缓存。假设我们正在构建一个类似社交网络的应用程序,用户可以在不同的地点“签到”并对其进行星级评定……从 0 表示糟糕的体验到 5 表示他们在那儿度过了最美好的时光!

在设计应用程序时,我们确定需要管理三个主要实体的数据

  • 用户
  • 地点
  • 签到

让我们看看我们存储了关于每个实体的信息。由于我们使用 Redis 作为唯一的数据存储,我们还会考虑它们如何映射到 Redis 数据类型……

用户#

我们将每个用户表示为一个扁平的名称/值对映射,没有嵌套对象。正如我们稍后将看到的,这很好地映射到 Redis 哈希。以下是我们将用来表示每个用户的模式的 JSON 表示

{
  "id": 99,
  "firstName": "Isabella",
  "lastName": "Pedersen",
  "email": "isabella.pedersen@example.com",
  "password": "xxxxxx1",
  "numCheckins": 8073,
  "lastCheckin": 1544372326893,
  "lastSeenAt": 138
}

我们为每个用户分配了一个 ID,并存储了有关他们的基本信息。此外,在将示例数据加载到 Redis 时,我们将使用 bcrypt 对他们的密码进行加密。

对于每个用户,我们将跟踪他们提交到系统的签到总数,以及他们最近一次签到的时间戳和地点 ID,以便我们知道他们上次使用系统的时间和地点。

地点#

对于用户可以签到的每个地点,我们将维护两种类型的数据。第一个也是一个扁平的名称/值对映射,包含有关地点的摘要信息

{
  "id": 138,
  "name": "Stacey's Country Bakehouse",
  "category": "restaurant",
  "location": "-122.195447,37.774636",
  "numCheckins": 170,
  "numStars": 724,
  "averageStars": 4
}

我们为每个地点分配了一个 ID 和一个类别——我们将在以后使用类别按类型搜索地点。“地点”字段存储经度、纬度格式的坐标……这与通常的纬度、经度格式相反。在查看 Redis 搜索时,我们将看到如何使用它执行地理空间搜索。

对于每个地点,我们还存储了我们所有用户在那里记录的签到总数、这些签到给该地点的星级总数以及该地点每次签到的平均星级评定。

我们希望为每个地点维护的第二种类型的数据是我们将称为“地点详细信息”。它们采用具有嵌套对象和数组的结构化 JSON 文档的形式。以下是以地点 138 Stacey's Country Bakehouse 为例

{
  "id": 138,
  "hours": [
    { "day": "Monday", "hours": "8-7" },
    { "day": "Tuesday", "hours": "9-7" },
    { "day": "Wednesday", "hours": "6-8" },
    { "day": "Thursday", "hours": "6-6" },
    { "day": "Friday", "hours": "9-5" },
    { "day": "Saturday", "hours": "8-9" },
    { "day": "Sunday", "hours": "7-7" }
  ],
  "socials": [
    {
      "instagram": "staceyscountrybakehouse",
      "facebook": "staceyscountrybakehouse",
      "twitter": "staceyscountrybakehouse"
    }
  ],
  "website": "www.staceyscountrybakehouse.com",
  "description": "Lorem ipsum....",
  "phone": "(316) 157-8620"
}

我们希望构建一个 API,让我们能够检索所有或部分这些额外的详细信息,并保持文档的整体结构。为此,我们将需要 Redis 支持 JSON,正如我们稍后将看到的那样。

签到#

签到与用户和地点不同,它们不是我们需要永远存储的实体。在我们的应用程序中,签到包含用户 ID、地点 ID、星级评定和时间戳——我们将使用这些值来更新我们用户和地点的属性。

每个签到可以被认为是一个扁平的名称/值对映射,例如

{
  "userId": 789,
  "locationId": 171,
  "starRating": 5
}

在这里,我们看到用户 789 访问了地点 171 (“Hair by Parvinder”),并且对服务非常满意。

我们需要一种方法来存储签到,以便能够对其进行处理,但不是永久存储。我们还需要与每个签到关联一个时间戳,因为我们将在处理数据时需要它。

Redis 提供了一个 Stream 数据类型,它非常适合此目的——使用 Redis Streams,我们可以存储名称/值对映射,并让 Redis 服务器为我们进行时间戳。Streams 也非常适合我们希望对这些数据执行的异步处理类型。当用户将新签到发布到我们的 API 时,我们希望存储该数据,并尽快向用户回复我们已经收到该数据。稍后,系统中的一个或多个其他部分可以对其进行进一步处理。此类处理可能包括更新用户的签到总数和最后看到的时间字段,或计算地点的新平均星级评定。

应用程序架构#

我们决定使用 Node.js 和 Express 框架以及 ioredis 客户端构建应用程序。应用程序没有使用单片代码库,而是被拆分为四个组件或服务。它们是

  • 身份验证服务: 侦听 HTTP 端口并处理用户身份验证,使用 Redis 作为其他服务可以访问的共享会话存储。
  • 签到接收器: 侦听 HTTP 端口并接收来自用户的 HTTP POST 请求的签到。每个签到都将放置在 Redis Stream 中以供以后处理。
  • 签到处理器: 监视 Redis 中的签到 Stream,在处理每个签到时更新用户和地点信息。
  • API 服务器: 实现应用程序的大部分 API 端点,包括从 Redis 中检索有关用户和地点的信息的端点。

这些组件的组合方式如下

还有一个数据加载器组件,我们将使用它将一些初始示例数据加载到系统中。

随着课程的进行,我们将依次查看每个组件。在下一模块中,您将亲自动手,克隆应用程序仓库,使用 Docker 启动 Redis 服务器,并加载示例数据。

外部资源#