微服务设计
#aws #javascript #microservices

注意:此内容最初发表在 Simple AWS newsletter 上。了解AWS解决方案背后的原因。免费订阅! 3000名工程师和技术专家已经拥有。

用例:微服务设计

设想

我们有一个在线学习平台作为单片应用程序构建,该平台使用户能够浏览和注册各种课程,访问课程材料,例如视频,测验和作业,并在整个课程中跟踪其进度。该应用程序是deployed in Amazon ECS作为可扩展且高度可用的单个服务。

随着应用程序的发展,我们已经注意到,在正常操作期间,内容交付成为瓶颈。此外,课程目录的更改导致了一些正在进行的错误跟踪。为了解决这些问题,我们决定将应用程序分为三个微服务:课程目录,内容交付和进度跟踪。

出于范围(所以我们不会失去焦点)

  • 身份验证/授权:当我说“用户”时,我的意思是认证的用户。我们可以为此使用Cognito。下周的问题将与Cognito有关。

  • 用户注册和管理:与上述相同。

  • 付款:由于我们的课程太棒了,我们应该为他们收取费用。我们可以使用单独的微服务,该微服务与付款处理器(例如条纹)集成。

  • 缓存和CDN:我们应该使用CloudFront来缓存内容,以降低延迟和成本。我们将在以后的问题中做到这一点,让我们现在关注微服务。

  • 前端:显然,我们需要应用程序的前端。前端代码显然是关于AWS的新闻通讯的范围,但是我们可以说一些有趣的事情,以提供S3,服务器端渲染以及与Amplify进行比较的前端。不过,这是一个未来问题的话题。

  • 数据库设计:假设我们的数据库是正确设计的。过去,我们谈到了DynamoDB的技术细微差别,但是一个完全致力于为DynamoDB设计数据库的问题是一个有趣的想法。

  • 管理员:有人必须创建课程,上传内容,课程元数据等。这样做的操作属于我们的微服务范围,但我担心它会增长太复杂了,所以我将这些功能切开了。

服务

  • ECS:我们的应用程序已经作为单个ECS服务部署在ECS中,我们将将其分为3个微服务,并将每个微服务部署为ECS服务。在阳光下没有什么新鲜事物,但是如果您不熟悉ECS,请检查我们的上一期。

  • dynamodb:此示例的数据库。

  • API网关:用于暴露每个微服务。

  • 弹性负载平衡器:平衡所有任务的流量。

  • S3:课程内容(视频文件)存储。

  • ecr:只是码头注册表。

Final design of the app split into microservices

解决方案逐步

识别微服务

分析整体应用,重点是课程目录,内容交付和进度跟踪功能。基于这些功能,概述了每个微服务的责任:

  • 课程目录:管理课程及其元数据。

  • 内容交付:处理课程内容的存储和分发。

  • 进度跟踪:通过课程管理用户进度。

定义每个微服务的API

设计每个微服务的API端点:

  • 课程目录:

    • 获取 /课程:列出所有课程< /li>
    • 获取/课程/:ID获取特定课程
  • 内容交付:

    • get/content/:id获取特定课程内容的预先签名的URL
  • 进度跟踪:

    • 获取/进度/:用户ID:获取用户的进度
    • put/progress/:userId/:CourseID:更新用户的特定课程进度

为每个微服务创建单独的存储库和项目

设置单个存储库和Node.js项目,用于课程目录,内容交付和进度跟踪微服务。使用最佳实践构建项目,并在路由,控制器和数据库访问代码上提供单独的文件夹。你知道演习。

分开代码

重构整体应用代码,将每个微服务的相关功能移动到其各自的项目中:

  • 将与管理课程及其元数据相关的代码移至课程目录微服务项目。

  • 将与将存储和分发的代码移动到内容交付微服务项目中。

  • 移动与通过课程管理用户进度相关的代码到进度跟踪微服务项目。

代码可能不会像您想要的那样明确分开。在这种情况下,首先将其分开在同一项目中,测试这些更改,然后将代码移至微服务。

分开数据

为每个微服务创建单独的Amazon DynamoDB表:

  • coursecatalog:存储课程元数据,例如标题,描述和内容ID。

  • 内容:存储内容元数据,包括内容ID,内容类型和S3对象密钥。

  • 进度:存储用户进度,使用用于用户ID,课程ID和进度详细信息的字段。

更新每个微服务中的数据库访问代码以与其特定表进行交互。

如果您要为已经具有数据的数据库执行此操作,则可以将其导出到S3,使用胶水过滤数据,然后将其导入到DynamoDB中。

如果系统是实时的,它会变得更棘手:

  1. 首先,如果还没有数据,请在数据中添加时间戳。

  2. 接下来,创建新表。

  3. 然后设置DynamoDB流以复制所有写入相应的表。

  4. 然后用脚本或S3导出 +胶来复制旧数据(不要使用DynamoDB导入,它仅适用于新表,而是手动编写数据)。确保这可以处理重复。

  5. 最后,切换到新表。

配置API网关

设置Amazon API网关来管理和保护每个微服务的API端点:

  1. 为每个微服务(课程目录,内容交付和进度跟踪)创建一个API网关资源。

  2. 应用请求验证以确保传入的请求已正确。

将微服务部署到ECS

  1. 为每个微服务编写Dockerfiles,指定基本node.js映像,复制源代码并设置适当的入口点。

  2. 为每个微服务构建并将Docker图像推到Amazon弹性容器注册表(ECR)。

  3. 在每个微服务的Amazon EC中创建单独的任务定义,指定所需的CPU,内存,环境变量和ECR Image URL。

  4. 为每个微服务创建ECS服务,将它们与相应的任务定义和API Gateway端点相关联。

更新前端

修改前端代码以使用新的基于微服务的体系结构。更新API调用以使用相应的API网关端点进行课程目录,内容交付和进度跟踪微服务。

测试新体系结构

彻底测试转换的应用程序,涵盖了各种用户方案,例如:

  • 登录并浏览课程目录。

  • 使用内容交付微服务生成的预先签名的URL访问课程内容。

  • 跟踪用户在课程中的进度并通过进度跟踪微服务来检索进度信息。

验证该应用程序是否按预期运行并满足绩效,安全性和可靠性要求。

解决方案解释

识别微服务

我在这个问题上为您做了这一点,但是您永远不应该匆匆忙忙,花点时间正确。

有两种拆分微服务的方法:

  • 垂直切片:每个微服务都求解特定的用例或一组紧密相关的用例。添加功能时添加服务,并且每个用户互动都会经历最小可能的服务数量(理想情况下只有1个)。这意味着特征是分解的一个方面。代码重用是通过共享库来实现的。

  • 功能服务:每个服务都处理一个特定步骤,集成,状态或事物。系统行为是一种新兴的属性,是由不同方式组合不同服务而产生的。每个用户互动都调用多个服务。新功能不需要全新的服务。特征是集成的一个方面,而不是分解。代码重用通常是通过调用另一项服务来实现的。

我选择了垂直切片,因为它更简单地理解,并且更容易实现更简单的系统。另外,在这种情况下,我们甚至不需要处理服务发现。缺点是,如果您的系统做200件不同的事情,它将至少需要200个服务。
顺便说一句,甚至不要考虑从一种微服务策略迁移到另一种微服务策略。放下整个系统并从头开始要容易得多。

定义每个微服务的API

这很简单,因为它们与我们为整体的终点相同。如果我们使用功能服务,那将变得更加困难,因为您需要了解服务b。

的服务需求。

请记住,进入微服务的唯一途径(包括数据)是通过服务的API进行的,因此请明智地设计。查看Fowler's post on consumer-driven contracts的一些深刻见解。

为每个微服务创建单独的存储库和项目

基本上,保留单独的微服务。
您可以使用MonorePo,其中整个代码库都位于一个Git存储库中,但服务仍分别部署。这效果很好,但是要做的很难。

分开代码

首先,根据需要进行重构,直到您可以将实现代码从您的整体复制到您的服务(但暂时不要复制)。然后测试重构。最后,进行拷贝。

分开数据

服务和微服务之间的差异是bounded context。每个微服务都拥有其模型,包括数据,访问该模型的唯一方法(以及存储它的数据库)是通过该服务的API。

我们可以在技术上实施它而无需执行它,甚至可以使用DynamoDB的字段级权限来执行它。但是我们无法使用单个表独立扩展服务,因为每个表分配了容量。

DynamoDB表易于创建和管理(除了定义数据模型之外)。在关系数据库中,我们需要考虑必须管理(和付费)每个微服务的一个DB群集,或在每个表 + db用户或每个数据库中分离一个DB群集之间的权衡,从而失去了独立扩展数据存储的能力。 Aurora无服务器也是一个可行的选择,尽管对于连续运行的数据库并不便宜。

配置API网关

这实际上是一种最佳实践,但是我将其添加为解决方案的一部分,因为您将需要它才能进行身份验证。我们已经讨论了benefits of API Gateway and how to implement it

将微服务部署到ECS

如果您已经部署了整体,我认为您知道如何部署服务。如果不是,请检查有关migrating from EC2 to ECS的新闻通讯。

更新前端

为简单起见,我认为这意味着使用新的URL更新前端代码。
如果您已经在API网关中使用了相同的路径,那就是您应该更新的内容。看? API网关的好处!

测试新体系结构

您构建它,部署它,对其进行测试,然后您声明它有效。

讨论

关于微服务有很多话要说(哎呀,我只是在主题上写了3000个单词),但要点是您不需要微服务(对于99%的应用程序)。

微服务存在解决特定问题:复杂域中的问题需要复杂的解决方案,由于域本身的大小和复杂性,它们变得难以管理。微服务(正确完成时)将该复杂的域分为简单的界面上下文,从而封装了复杂性并降低了变化的范围(这就是它们独立改变的原因)。它们还增加了解决方案的复杂性,因为现在您需要弄清楚在何处绘制上下文的边界,以及微服务如何在域级别(跨越多个微服务的复杂动作)和技术上的微服务如何相互作用级别(服务发现,网络,权限)。

那么,您什么时候需要微服务?当域的复杂性降低大于溶液的复杂性增加时。

什么时候您不需要微服务?当域不是那么复杂时。在这种情况下,使用常规服务,其中唯一的分裂是行为(即后端代码)。或坚持使用巨石,Facebook做到了,它的效果很好,我们只能梦想着。

顺便说一句,这是在拆分之前查看课程的用户:

  1. 用户将其凭据带到单片应用程序发送登录请求。

  2. 应用程序验证凭据,如果有效,则为用户生成身份验证令牌。

  3. 用户发送请求查看课程,包括请求标题中的身份验证令牌。

  4. 该应用程序检查了身份验证令牌,并从DynamoDB的课程表中检索课程详细信息。

  5. 应用程序从DynamoDB中的内容表中检索课程内容元数据,包括S3对象键。

  6. 使用S3对象键,该应用程序为来自Amazon S3的课程内容生成了预先签名的URL。

  7. 该应用程序以课程详细信息和课程内容的预先签名的URL响应。

  8. 用户的浏览器显示课程详细信息,并使用预先签名的URL加载课程内容。

这就是拆分后的样子:

  1. 用户向身份验证服务发送了带有其凭据的登录请求(以前的微服务示例中未涵盖)。

  2. 身份验证服务验证凭据,如果有效,则为用户生成身份验证令牌。

  3. 用户发送请求以查看一门课程,包括请求标题中的身份验证令牌,通过API网关到课程目录微服务。

  4. 课程目录微服务检查身份验证令牌,并从DynamoDB中的课程目录表中检索课程详细信息。

  5. 课程目录微服务对课程详细信息做出响应。

  6. 用户的浏览器发送请求以访问课程内容,包括请求标题中的身份验证令牌,通过API Gateway的内容交付微服务。

  7. 内容交付微服务检查身份验证令牌并从DynamoDB中的内容表中检索课程内容元数据,包括S3对象键。

  8. 使用S3对象密钥,内容交付微服务为来自Amazon S3的课程内容生成了预先签名的URL。

  9. 内容交付微服务以课程内容的预先签名的URL做出响应。

  10. 用户的浏览器显示课程详细信息,并使用预先签名的URL加载课程内容。

最佳实践

卓越运营

  • 集中记录:您基本上正在运行3个应用程序。将日志存储在同一位置,例如CloudWatch日志(ECS自动为您配置)。

  • 分布式跟踪:这三个服务不会互相呼叫,但是在一个真实的微服务应用程序中,发生这种情况更为普遍。在这种情况下,遵循呼叫的踪迹变得相当困难。使用X射线使其更简单。

安全

  • 至少特权:不足以编写代码以访问其他服务的数据,您还应该通过IAM权限执行它。您的微服务应每个人都使用不同的IAM角色,使每个角色都可以访问自己的DynamoDB表,而不是 *。

  • 网络:如果服务不需要网络可见性,则不应拥有它。用安全组执行它。

  • 零信任:这个想法是不要信任网络中的代理,而是在每个阶段进行身份验证。通过API网关公开服务可以为您提供一种简单的方法。是的,即使将它们暴露于其他服务时,您也应该这样做。

可靠性

  • 断路器:用户调用服务,您的老板突然着火了(这是一件坏事吗?),一切都起火了。断路器的作用完全像电动版本:它们防止一个组件的故障影响整个系统。 I'll let Fowler explain

  • 考虑不同的缩放速度:如果服务a取决于服务B,请考虑独立使用服务B,这可能意味着服务B的实例在服务A获取时不会立即开始一个要求。服务B可以在不同的平台(EC2自动缩放与Lambda)中实现,该平台以不同的速度缩放。请记住服务依赖性,以及decouple the services

性能效率

  • 标准服务独立:您的微服务是如此独立,即使他们的数据库也是独立的!你知道这意味着什么?您可以随意扩展它们!

  • 权利ECS任务:现在您将整体拆分了,现在该检查每个微服务的资源使用情况,并独立调整它们。

  • 权利化dynamodb表:与上述数据库表相同。

成本优化

  • 优化容量:确定每个服务所需的容量,并为其优化。获取基线容量的储蓄计划。

  • 考虑不同的平台:不同的微服务有不同的需求。面向用户的微服务可能需要以Fargate或Lambda的速度非常快地扩展。仅处理异步交易的服务,例如付款处理服务,可能不需要快速扩展,并且可以使用自动扩展组(每个计算时间便宜)逃脱。批处理处理服务甚至可以使用现场实例!每个服务都是独立的,所以不要限制自己。
    确实考虑了增加的管理工作:管理10个Lambda功能比管理5个Lambda功能,1个ECS群集和2个自动缩放组要容易(因此便宜)。


了解AWS解决方案背后的原因。 加入3000多个开发人员,技术线索和专家,学习如何与Simple AWS newsletter一起构建云解决方案,而不是通过考试。

  • 现实世界情景
  • 解决方案背后的原因
  • 如何运用最佳实践

Subscribe for free!

如果您想进一步了解我,可以找到我on LinkedInwww.guilleojeda.com