读构建可扩展分布式系统:方法与实践07无服务器处理系统

1. 无服务器的魅力

1.1. 对于某些应用程序,负载在工作时间可能很高,而在非工作时间可能很低或者不存在

1.2. 其他应用程序后台流量可能在99%的时间里都很低

  • 1.2.1. 一旦到了一些大型节目的门票发布时间,负载需求可能会在数小时内飙升至平均水平的10000倍,然后回落至正常水平

1.3. 主流机构的IT系统从内部部署转到公有云平台部署似乎是不可避免的

  • 1.3.1. 云平台的两个巨大的魅力在于,它们按需付费的计费方式和快速扩展(和缩减)虚拟资源以满足不断变化的工作负载和数据量的能力

  • 1.3.2. 系统长时间使用的资源越多,月底的云账单金额就越大

  • 1.3.2.1. 超支的原因有很多,包括缺乏自动扩展方案的部署、长期不合理的容量规划,以及云架构利用不足导致系统占用空间过大

  • 1.3.3. 架构上的重大决策遍及云上系统设计和部署的方方面面

  • 1.3.3.1. 如果负载增加,弹性应用程序可以启动新的虚拟机来增加容量,通常是利用云提供的负载均衡服务

  • 1.3.3.2. 你的成本基本上与选择的VM类型、部署持续时间以及应用程序存储和传输的数据量成正比

1.4. 主流的云供应商提供了一种替代方法来显式配置虚拟的处理资源,它们被称为无服务器平台,不需要静态配置任何计算资源

  • 1.4.1. 使用AWS Lambda或GAE(Google App Engine)等技术,应用程序代码在请求到达时按需加载和执行

  • 1.4.2. 如果没有活跃的请求,基本上不使用资源,也无须支付费用

1.5. 无服务器平台还为你管理自动缩放(向上和向下)

  • 1.5.1. 当请求同时到达时,平台创建额外的处理能力来处理请求,并在理想情况下提供始终如一的低响应时间

  • 1.5.2. 当请求负载下降时,额外的处理能力将停用,并且不会产生任何费用

1.6. 影响成本因素

  • 1.6.1. 所选择的用于执行请求的处理实例的类型

  • 1.6.2. 请求的数量和处理每个请求所持续的时间

  • 1.6.3. 每个应用服务器实例在无服务器基础设施上驻留的时长

2. GAE

2.1. GAE(Google App Engine)是Google的第一个产品

  • 2.1.1. 作为GCP(Google Cloud Platform,谷歌云平台)的一部分

2.2. GAE有两种类型,即标准环境和灵活环境

  • 2.2.1. 基本区别在于,标准环境由GAE更紧密地管理,在支持的开发语言版本方面存在限制

2.3. GAE标准环境

  • 2.3.1. 对于长时间处于非活动状态的应用程序来说是非常合适的,没有实例便不会产生任何成本

  • 2.3.2. GAE的标准环境是一个非常强大的可扩展应用程序平台

2.4. 自动扩展

  • 2.4.1. 随着请求负载的增长,GAE调度器将动态加载更多实例来处理请求

  • 2.4.2. 目标CPU利用率

  • 2.4.2.1. 设置CPU利用率阈值,超过该阈值将启动更多实例来处理流量

  • 2.4.2.2. 该参数的范围是0.5(50%)至0.95(95%)

>  2.4.2.2.1. 默认值为0.6(60%)
  • 2.4.3. 最大并发请求数

  • 2.4.3.1. 设置在调度程序生成新实例之前实例可以接受的最大并发请求数

  • 2.4.3.2. 默认值为10,最大值为80

  • 2.4.4. 目标吞吐量利用率

  • 2.4.4.1. 与最大并发请求数的值结合在一起使用,以指定何时启动新实例

  • 2.4.4.2. 范围为0.5(50%)至0.95(95%)

>  2.4.4.2.1. 默认值为0.6(60%)
  • 2.4.4.3. 当一个实例的并发请求数达到最大并发请求数乘以目标吞吐量利用率时,调度程序会尝试启动一个新实例

  • 2.4.5. 三种设置相互影响,让配置工作变得有些复杂

  • 2.4.6. max-pending-latency参数

  • 2.4.6.1. 指定了GAE在启动其他实例来处理请求和减少延迟之前,允许请求在挂起队列中等待的最长时间

  • 2.4.6.2. 默认值为30 ms

  • 2.4.6.3. 值越低,应用程序扩展的速度就越快,可能会让你付出更多的费用

3. AWS Lambda

3.1. AWS Lambda是Amazon的无服务器平台

  • 3.1.1. 底层设计原则和主要功能与GAE及其他无服务器平台相似

3.2. Lambda函数生命周期

  • 3.2.1. Lambda函数可以用多种语言构建,并支持常见的服务容器,如Java的Spring和Python的Flask

  • 3.2.2. 对于每种语言(即Node.js、Python、Ruby、Java、Go以及基于.NET的代码)​,Lambda支持许多运行时版本

  • 3.2.3. Lambda函数必须设计为无状态的,以便Lambda运行时环境可以按需扩展服务

  • 3.2.4. 当Lambda函数定义的API对应的请求首次到达时,Lambda会下载该函数的代码,初始化运行时环境和所有特定实例(例如,创建数据库连接)​,最后调用函数代码处理程序

  • 3.2.5. Lambda的初始调用被称为冷启动,所花费的时间取决于所选的语言环境、函数代码的大小以及初始化函数所花费的时间

  • 3.2.5.1. 与GAE一样,Node.js和Go等轻量级语言的初始化时间通常需要几百毫秒,而较重的Java或.NET可能需要一秒或更长时间

  • 3.2.5.2. 通过预置并发(provisioned concurrency)可以降低冷启动的成本

  • 3.2.6. API一旦执行完成,Lambda就可以使用已部署的函数运行时环境处理后续请求

  • 3.2.6.1. 意味着不会产生冷启动成本

  • 3.2.7. Lambda不会向同一运行时实例发送多个并发请求

  • 3.2.7.1. 考虑冷启动成本,所有并发请求都将产生额外的响应时间

3.3. 执行中的注意事项

  • 3.3.1. 在定义Lambda函数时,你需要指定分配给运行时环境的内存大小

  • 3.3.1.1. 与GAE不同的是,你无须指定要使用的vCPU的数量

  • 3.3.2. Lambda函数是按每次执行的毫秒数进行计费的

  • 3.3.2.1. 每毫秒的成本随着分配给运行时环境的内存量的增加而增加

  • 3.3.2.2. 分配的内存量越大,Lambda函数的执行速度可能就会越快

  • 3.3.2.3. 找到以更低成本提供更快响应时间的最佳点是一项可以获得高额回报的性能调整实验

>  3.3.2.3.1. Lambda只有一个参数(内存分配)可以改变,让实验变得简单

3.4. 可扩展性

  • 3.4.1. 随着函数并发请求数量的增加,Lambda将部署更多运行时实例来扩展处理能力

  • 3.4.2. 当请求负载下降时,Lambda会通过停止未使用的实例来缩小规模

  • 3.4.3. 所有Lambda函数针对请求突发的情况都有内置的并发限制

  • 3.4.3.1. 一旦达到突发限制,函数就能以每分钟500个实例的速度进行扩展

  • 3.4.4. 如果一个函数负载突然意外升高,它会消耗突发限制资源,并对同一时刻其他希望扩展的函数的可用性产生负面影响

  • 3.4.5. 预留并发(reserved concurrency)

  • 3.4.5.1. 可以对部署在同一区域同一AWS账户下的每个Lambda函数相关的并发级别进行微调

  • 3.4.5.2. 每个单独的函数可以关联一个小于突发极限的值

>  3.4.5.2.1. 该值定义为可同时执行的函数的最大实例数
  • 3.4.5.3. 具有预留并发性的Lambda函数始终拥有专用于自身调用的执行能力
>  3.4.5.3.1. 不会因该区域中其他函数的并发调用而意外“饿死”
  • 3.4.5.4. 预留的容量限制了该函数的最大驻留实例数
>  3.4.5.4.1. 当实例数达到预留的值时,无法处理的请求会失败并返回一个HTTP 429的错误

3.5. AWS Lambda提供了一个强大而灵活的无服务器环境

  • 3.5.1. 经过适当配置,可以有效地扩展运行环境,以处理高容量、突发性的请求负载

4. 总结

4.1. 当你的应用程序每天可能需要处理数百万个请求时,即使降低10%的成本也可以节省大量资金

  • 4.1.1. 相同的代码,相同的请求负载,不同的配置参数

  • 4.1.2. 对于多个相互依赖的配置参数,你不太可能通过直觉和专业知识找到“最佳”配置

4.2. 无服务器平台是构建可扩展应用程序的强大工具

  • 4.2.1. 它们消除了许多部署的复杂性,这主要与管理和更新显式分配的虚拟机集群相关

4.3. 可以使用一些重要的参数来调整底层无服务器平台管理你的函数的方式

  • 4.3.1. 参数都具有平台特异性,但很多都与性能和可扩展性有关,最终会影响你支付的费用

4.4. 想要利用无服务器计算的优势,就需要你从云服务提供商购买云服务

4.5. 无服务器平台是实现微服务架构的常用技术

  • 4.5.1. 微服务是一种架构模式,用于将应用程序分解为多个可独立部署和扩展的部分