Cloud Service Engine, 简称 CSE, 是中间件部门研发的面向通用 Serverless 计算的中间件产品, 目标是具备 AWS Lambda 的各种优势, 同时可以解决 AWS Lambda 的关键技术缺陷.
AWS Lambda 如果用于核心业务, 可能会有以下缺陷:(仅代表个人观点)
要求用户以 Function 为单位开发, 全新的开发框架, 云厂商强绑定. 社区主流技术栈迁移成本高.
Function 启动速度要足够快, 毫秒级或者秒级, 这个限制对适用场景有很强的约束.
Function 之间的调用通过 API Gateway, 响应时间更长.
CSE 目前在集团内测中, 本文非完整产品介绍, Serverless 话题涉及范围极广, 几乎包含了代码管理, 测试, 发布, 运维, 扩容等与应用生命周期关联的所有环节. 本文主要回答个人在探索这个方向时的思考以及中间件部门正在做的解决方案, 目标是尽可能让开发者少改代码, 甚至不改代码, 就能具备 AWS Lambda 的技术优势.
我认为的 Serverless 是什么?
AWS 对 Serverless 的定义
以上观点来自 AWS 官网 链接 https://amazonaws-china.com/cn/serverless/
AWS 定义的 Serverless 包含了哪些功能项
个人的补充观点
个人对 Serverless 的观点仅在 AWS 上补充, AWS 的整个方案非常完善, 但是没有解决存量应用如何迁移到 Serverless 架构, 仅仅针对新开发的应用, 建议用户用 FaaS 方式开发, 才有机会变成 Serverless 架构.
要将 Serverless 架构大规模推广, 必须要能有针对存量业务的解决方案.
Serverless 在云计算中的位置是什么?
云计算归根结底是一种 IT 服务提供模式, 不论是公共云还是专有云 (以 IT 设备的归属不同分类), 其本质都是 IT 的最终使用者可以随时随地并且简便快速地获取 IT 服务, 并以获取服务的层次分为 IaaS,PaaS,SaaS. 目前看 IaaS,PaaS 都已经做到了按需付费. PaaS 甚至做到了按请求付费, 如 DB,CACHE,MQ 等, 但是 IaaS 的付费粒度仍然是时间维度, 最快按照小时付费, 按照分钟来交付.
基于以上现状, 应用的开发维护方式相比传统 IDC 模式的开发维护差别还不是很大, 而 AWS Lambda 提供了一种全新的方式, 只需要用户写业务代码, 提交到云上, 所有和机器容量, 可用性, 机器为单位的运维工作全部交给了云平台, 这种模式极大的放大了云的弹性价值, 真正做到了按需付费.
本文试图提供一种更规模化的解决方案, 像 AWS Lambda 一样, 能继续放大云的弹性价值, 并且是可以兼容存量应用.
现存在线业务演变成 Serverless 架构的关键挑战是什么?
当前的在线应用程序具有以下特点
资源分配速度 = 分钟级
应用程序启动速度 = 10 分钟 +
基于以上客观条件, 通常做法是提前预定好机器数量来应对任意时刻的流量峰值, 假设上述技术参数变为毫秒级, 就有机会将应用程序架构演变成下图所示方式.
上图中 Service A 在调用 Service B 时, 如果 B 的容量充足, 调用成功, 如果 B 容量不足, 这时候可能是线程池满, 可能直接触发限流阀值, A 会收到一个错误码, A 会直接调用资源总控系统, 资源总控系统负责新分配一个 Service B 实例, 这个分配的速度非常快, 耗时几十毫秒, 同时把 B 的服务地址直接返回给 A,A 将之前未完成的请求发送到新创建的 Service B.
以上过程对于开发者完全透明, 具备了以下价值:
价值一: 无需管理服务器, 意味着无需容量评估, 容量评估这件事情对于应用负责人一直是一个极难解的问题, 因为我们很难预测未来的峰值是什么.
价值二: 持续扩展, 之前的做法是每个应用程序独占一定数量的资源, 如果变成 Serverless 模式, 意味着所有应用程序共享资源池, 也意味着每个应用程序几乎可以无限扩展.
价值三: 按照请求计费, 因为每个实例的启动时间甚至比 FaaS 的函数启动时间还快, 就可以像 FaaS 一样来核算成本, 成本只与以下因素有关
请求数量 (QPS)
每次请求 CPU 执行时间, 例如 100ms
每个实例的内存规格
综上所述: 为了做到以上描述的分布式架构, 关键技术点在于应用启动速度, 这里的应用启动速度是指应用可以正常处理流量为止.
如何将应用启动速度加速到毫秒级?
应用在启动过程中通常会初始化多个组件, 如各种中间件, 各种数据结构, 以及网络调用外部服务, 在阿里内部广泛推广 SOA, 微服务情况下, 会大量加载共享业务 SDK, 会存在启动过程达到 10 分钟量级的情况, 个别应用可能会更长.
因此, 这个启动过程必须提前完成, 才有机会临阵磨枪的方式去创建新实例.
方案一: 应用冷启动资源压缩方案
L1 弹性能力是指在一台物理机或者大规格的 ECS 上部署同一个应用的多个实例, 通过操作系统和 JVM 的优化, 一个占用 4G 堆内存的应用, 即使部署 10 份, 仅需占用 2.2G RAM. 以线上菜鸟生产应用为例.
L1 总结来看是一种高密度部署方式, 由于应用已经提前启动, 并且对容器进行冻结, 意味着这个应用实例 CPU 占用率为 0,RAM 占用相当于之前的 1/20, 但是具备了毫秒级弹性的能力. L1 的特点是启动速度极快, 但是需要消耗资源, 且只能垂直弹性.
L2 是通过将应用程序启动后在 RAM 中的指令和数据结构 dump 到磁盘文件, 只需要在机器之间拷贝文件即可以达到横向弹性的能力, 这个时间消耗主要是数据的网络传输时间 + 内存拷贝时间, 大约在 5 秒左右可以完成. L2 的成本开销只有网络磁盘容量, 开销极低, 可忽略不计.
L2 的每个 SNAOSHOT 对应一个可运行的实例, 例如预计一个应用需要最大启动 100 个实例, 那么需要提前生成 100 个 SNAOSHOT, 每个 SNAOSHOT 对应一个运行实例, 需要启动时, 从远程磁盘加载这个 SNAPSHOT.
此方案通过 L1 和 L2 的组合来达到加速应用启动的目的, 在支持一定流量脉冲能力下, 可以最大 50ms 内启动任意应用, 平均在 10ms 内完成.
方案二: 应用热复制启动加速方案
L1 采用通过 fork 种子进程达到快速启动的效果, 操作系统团队专门为此开发了 fork2 技术, 与 Linux native fork 的关键区别是可以指定 PID 来 fork 一个进程
pid_t fork2(pid_t pid);
L2 的单个 SNAPSHOT 可以创建多个进程, 一对多关系.
自研两种方案对比
方案二: 会存在 UUID 问题, 如开发者希望应用每个实例启动都赋值一个 UUID 给一个静态变量, 而通过 fork 会导致每个实例的这个静态变量都相同, 与开发者预期不符.
优势是整个方案更易实现, 语言无关, 成本效果更优.
适合 FaaS, 盒马 NBF 这类场景或者开发者自己定义开发框架, 能避免 UUID 这种情况的 场景使用.
方案一: 不存在 UUID 问题, 但是每种语言的 VM 要单独定制, 成本效果相比方案二略差.
整体来看, 方案一的适用场景更广, 但是实现成本更高, 方案二较适合 FaaS,NBF 这类场景.
与 AWS Lambda 方案对比
Lambda 为了做到快速扩缩容, 要求用户的应用以 Function 为单位开发, Lambda Runtime 动态加载 Function 来快速增加实例.
CSE 通过将一个应用的多个实例启动后, 共享相同的指令数据, 抽取出不同的指令数据, 每次启动实例只需要加载多实例的差异部分. 因此可以透明兼容社区主流技术栈, 如 Spring Boot,PHP/Java/Python/Node.JS 等.
CSE 的成本优势
理论模型
Serverless 方式应用占用的实例数随时在变化, 因此可以多个应用错峰使用同一台机器.
量化分析
Serverless 的成本优势是可以和 CPU Share & 离在线混部等调度技术的成本优势做叠加, 能给最终用户一个更优的总体成本.
CSE 的代码样例
- HSF demo
- package com.test.pandora.hsf;
- import com.alibaba.boot.hsf.annotation.HSFProvider;
- @HSFProvider(serviceInterface = HelloWorldService.class)
- public class HelloWorldServiceImpl implements HelloWorldService {
- @Override
- public String sayHello(String name) {
- return "hello :" + name;
- }
- }
- Spring Boot demo
- package com.example.java.gettingstarted;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.Web.bind.annotation.RestController;
- @SpringBootApplication
- @RestController
- public class HelloworldApplication {
- @RequestMapping("/")
- public String home() {
- return "Hello World!";
- }
- @RequestMapping("/health")
- public String healthy() {
- // Message body required though ignored
- return "Still surviving.";
- }
- public static void main(String[] args) {
- SpringApplication.run(HelloworldApplication.class, args);
- }
- }
CSE 的成功案例
盒马 p0 级服务, 文描在双十二场景验证中, 机器数量从 11 台到 2 台 (2~10 台之间波动).
3.8 女王节的最新数据, 盒马导购域的 P0 级服务流量峰值从 4000 + 瞬间飙到 12 万, CSE 瞬间弹性扩容, 从 2 台 -->5 台 -->10 台, 流量峰值回落后又缩容到 2 台.
盒马 p0 级服务, 天气在双十二场景验证中, 机器数量从 4 台到 2 台 (2~10 台之间波动).
1688 的 buyer-tools 应用, 之前固定 4 台机器, serverless 化完成后, 机器数量变成 1 台 (1~4 台之间波动). 预发可实现 0 ~ 1 台实例之间波动.
CSE 的约束和限制
高脉冲型流量业务消耗的成本会更高.
应用尽可能避免后台活动线程的 CPU 消耗.
应用尽可能无状态.
应用尽可能使用短连接, 长链接要能支持断线毫秒级重连能力.
CSE 引用了以下合作团队的代码, 特此致谢.
使用了操作系统团队开发的 fork2 技术, 感谢 @启翾 @喻望 @笑哲
使用了 JVM 团队开发的 APPCDS 技术, 感谢 @三红 @传胜 @QI, Yumin @在弦
姬风团队帮助优化了中间件 SDK 启动速度, 感谢 @姬风以及他的团队同学
Pouch 团队做了大量容器适配 Serverless 的优化, 感谢 @华敏 @沈凌
打个招聘广告
CSE 团队新财年致力于打造可以在集团规模化应用的产品, 有意向的同学请联系我.
来源: https://yq.aliyun.com/articles/702070