37、构建容错Web应用:原理与实践

构建容错Web应用:原理与实践

1. 使用冗余EC2实例提高可用性

虚拟机器可能出现故障的原因有多个方面,主要分为硬件和软件两部分:
- 硬件方面
- 若主机硬件发生故障,将无法继续承载其上的虚拟机器。
- 主机的网络连接中断时,虚拟机器会失去网络通信能力。
- 主机系统断电,虚拟机器也会随之故障。
- 软件方面
- 应用程序存在内存泄漏问题,最终会耗尽内存导致故障。
- 应用程序不断向磁盘写入数据且不删除,会导致磁盘空间耗尽,应用程序失败。
- 应用程序对边界情况处理不当,可能会意外崩溃。

由于上述原因,单个EC2实例是单点故障。若仅依赖单个EC2实例,系统迟早会崩溃。

1.1 冗余可消除单点故障

以制作蓬松云派的生产线为例,原本的单条生产线存在问题,只要其中一个步骤崩溃,整个生产线就必须停止。比如冷却派皮的步骤出现故障,后续步骤因无法获得冷却的派皮而无法工作。

若采用多条生产线,例如三条,当其中一条生产线出现故障时,另外两条仍可继续为客户生产蓬松云派。虽然需要三倍数量的机器,但系统稳定性大大提高。

将此例子应用到EC2实例,不使用单个EC2实例运行应用程序,而是使用三个。若其中一个实例发生故障,另外两个仍能处理传入的请求。并且可以选择三个小的EC2实例来替代一个大的实例,以降低成本。

对于动态EC2实例池,可通过解耦的方式实现与实例的通信,即在EC2实例和请求者之间放置负载均衡器或消息队列。

1.2 冗余需要解耦

通过冗余和同步解耦可使EC2实例具备容错能力。若某个EC2实例崩溃,弹性负载均衡器(ELB)会停止向该实例路由请求,自动扩展组会在几分钟内替换崩溃的实例,然后ELB开始向新实例路由请求。

以下是系统中冗余部分的说明:
| 冗余部分 | 说明 |
| ---- | ---- |
| 可用区(AZs) | 使用两个可用区,若其中一个发生故障,另一个仍有实例运行 |
| 子网 | 子网与可用区紧密耦合,每个可用区需要一个子网 |
| EC2实例 | 在一个子网(可用区)中有多个实例,且在两个子网(可用区)中都有实例 |

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    Internet --> LoadBalancer(Load balancer):::process
    LoadBalancer --> AZA(Availability zone A):::process
    LoadBalancer --> AZB(Availability zone B):::process
    AZA --> SubnetA(10.0.1.0/24):::process
    AZB --> SubnetB(10.0.2.0/24):::process
    SubnetA --> WebServersA(Web servers):::process
    SubnetB --> WebServersB(Web servers):::process
    WebServersA --> AutoScalingA(Auto - scaling group):::process
    WebServersB --> AutoScalingB(Auto - scaling group):::process
2. 使代码具备容错性的考虑因素

若要实现容错,需相应地构建应用程序,可参考以下两个建议。

2.1 允许崩溃,但也要重试

Erlang编程语言以“让它崩溃”的概念而闻名,即程序遇到无法处理的情况时崩溃,由其他部分处理崩溃。但人们常忽略Erlang也以重试而闻名。只崩溃不重试是无用的,因为若无法从崩溃中恢复,系统将停机。

“让它崩溃”(或“快速失败”)可应用于同步和异步解耦场景:
- 同步解耦场景 :请求发送者必须实现重试逻辑。若在一定时间内未收到响应或收到错误,发送者会再次发送相同请求。
- 异步解耦场景 :若消息在一定时间内被消费但未得到确认,它会返回队列,下一个消费者会再次处理该消息。异步系统默认具备重试机制。

不过,“让它崩溃”并非适用于所有情况。若请求内容无效,让服务器崩溃并不能解决问题;但若服务器无法访问数据库,重试是有意义的,因为数据库可能在几秒后恢复正常。

重试也存在问题,例如重试创建博客文章时,会在数据库中创建多个重复条目。可通过幂等重试来解决此问题。

2.2 幂等重试使容错成为可能

为防止因重试导致博客文章多次添加到数据库,可采用简单方法,如使用标题作为主键。若主键已存在,则跳过插入步骤,使博客文章的插入操作具有幂等性,即无论操作执行多少次,结果都相同。

插入博客文章的实际过程更复杂,可分为以下步骤:
1. 在数据库中创建博客文章条目 :使用通用唯一标识符(UUID)作为主键。客户端生成UUID,若请求包含的UUID在数据库中不存在,则创建新记录;若存在,则继续插入流程。
2. 使缓存失效 :向缓存层发送失效消息。多次使缓存失效不会造成太大影响,最坏情况是需要多查询几次数据库。
3. 在博客的Twitter动态中发布链接 :由于Twitter不支持幂等操作,无法保证只发布一次状态更新。可采用以下两种方法:
- 向Twitter API查询最新状态更新,若与要发布的内容匹配,则跳过该步骤。但Twitter是最终一致性系统,可能会导致多次发布。
- 在数据库中记录是否已发布状态更新。但可能出现数据库记录已发布,而实际未发布的情况。需要根据业务需求选择容忍缺失状态更新还是容忍多次发布。

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    Start(Start):::process --> CheckUUID{Is the UUID already in the database?}:::process
    CheckUUID -- Yes --> End(End):::process
    CheckUUID -- No --> CreateEntry(Create database entry):::process
    CreateEntry --> End

构建容错Web应用:原理与实践

3. 构建容错Web应用:Imagery案例

在设计Imagery应用时,最初的流程是用户上传图像,应用对图像应用滤镜,然后返回处理后的图像。但这个过程是同步的,存在问题:若Web服务器在请求和响应期间崩溃,用户的图像将无法处理;当大量用户使用该应用时,系统会变得繁忙,可能会变慢或停止工作。因此,需要将其转换为异步过程。

3.1 异步处理流程

当用户想要上传图像时,首先创建一个处理流程,系统会返回一个唯一ID。用户使用该ID上传图像,上传完成后,工作者会在后台处理图像。用户可以随时使用处理ID查询处理状态,在图像处理期间,用户无法看到处理后的图像,处理完成后,查询将返回处理后的图像。

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    User(User):::process --> CreateProcess(Create an image process):::process
    CreateProcess --> GetID(Get's back an ID):::process
    GetID --> UploadImage(Upload an image to the process identified by the ID):::process
    UploadImage --> Worker(Worker picks up the job to process the image):::process
    Worker --> ApplyFilter(Apply the sepia filter to the image):::process
    ApplyFilter --> Wait(Wait until image is processed asynchronously):::process
    Wait --> ViewImage(View the sepia image by the ID):::process
3.2 映射到AWS服务

将异步处理流程映射到AWS服务,大部分AWS服务默认具有容错能力,因此尽可能选择这些服务。具体步骤如下:
1. 创建处理流程 :用户创建一个带有唯一ID的处理流程,该流程存储在DynamoDB中。
2. 上传图像 :用户使用处理ID将图像上传到S3,S3键与新的处理状态“已上传”一起持久化到DynamoDB,并生成一个SQS消息以触发处理。
3. 处理图像 :SQS消息被EC2实例消费,原始图像从S3下载,处理后将处理后的图像上传到S3,DynamoDB中的处理状态更新为“已处理”,并记录处理后图像的S3键。
4. 查看图像 :用户等待处理状态变为“已处理”,然后通过DynamoDB知道处理后图像的S3键,从而查看图像。

步骤 操作 涉及服务
1. 创建处理流程 用户创建带有唯一ID的处理流程,存储在DynamoDB DynamoDB
2. 上传图像 用户使用ID上传图像到S3,S3键和状态存到DynamoDB,生成SQS消息 S3, DynamoDB, SQS
3. 处理图像 EC2实例消费SQS消息,处理图像并上传到S3,更新DynamoDB状态 EC2, S3, DynamoDB
4. 查看图像 用户等待状态更新,通过DynamoDB获取S3键查看图像 DynamoDB, S3
3.3 幂等状态机

幂等状态机是Imagery应用的核心。有限状态机有至少一个起始状态和一个结束状态,状态之间有定义的转换。Imagery状态机如下:
(Created) -> (Uploaded) -> (Processed)

  • Created -> Uploaded :此转换由函数uploaded(s3Key)定义,需要上传的原始图像的S3键。
  • Uploaded -> Processed :此转换由函数processed(s3Key)定义,需要处理后的图像的S3键。

需要注意的是,状态机只关注操作的结果,不跟踪操作的进度。

整个示例在AWS免费套餐范围内,只要在几天内完成示例,并且是为该项目创建的全新AWS账户且无其他操作,就无需支付费用。完成示例后,需要清理账户。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值