关于云函数冷启动优化的思考
随着容器技术的广泛应用,XaaS
形式的概念层出不穷。从IaaS
(Infrastructure as a Service)、PaaS
(Platform as a Service)、SaaS
(Software as a Service)到容器云引领的CaaS(
Containers as a Service),再到火热的微服务架构,它们都在试着将各种软、硬件资源等抽象为一种服务提供给开发者使用,让他们不再担心基础设施、资源需求、中间件等等,在减轻心智负担的同时更好地专注于业务。FaaS
是Functions as a Service的简称,它往往和无服务架构(Serverless Architecture)一同被提起。
小程序开发云函数正是FaaS
形式的架构,微信官方对其推崇至极。但是实际的应用情况我们有目共睹,云函数的冷启动对客户端带来的是高延迟的糟糕体验。一个云函数冷启动,需要经过资源调度,代码下载,代码部署几个步骤。还没等到执行代码逻辑,用户已经退出程序了。如果仅仅从云函数现在的表现出发进行分析,想要去撼动现在市场格局是远远不够的。
本人技术水平有限,也不知道云开发的完整的技术框架,只从可能的几个角度进行分析,找寻可能存在的优化方向,如果错误,希望各位不吝斧正。
# 思路一
在云函数中调用另一个云函数逻辑,假设执行 \(A\) 云函数逻辑需要 \(T_1\) 时长,冷启动需要 \(C_1\) 时长,执行 \(B\) 云函数逻辑需要 \(T_2\) 时长,冷启动需要 \(C_2\) 时长。
那么执行这个逻辑的需要总时长大概是 \(T_1 + C_1 + T_2 + C_2\)
那么是否可以对上传的云函数代码进行上下文分析,如果云函数中存在对其他云函数(如 \(B\) 云函数)的调用,在冷启动 \(A\) 云函数时,也调度资源给 \(B\) 云函数。
思路简述如下:
- 对上传代码分析,将上下文出现的对其他云函数的调用记录下来,记作
link_container_list
。 - 在调用云函数之前,检查该云函数的
link_container_list
,冷启动该云函数同时,对link_container_list
中的云函数也进行冷启动(资源调度)。
那么执行这个逻辑的需要的总时长大概是 \(T_1 + T_2 + max\{C_1,C_2\}\)
该思路可以进行拓展,即使有\(N\) 个相关的云函数,那么理想情况下最终执行的逻辑时长,也是 \(\sum_{i=1}^{n}T_i + max\{C_1,...,C_n\}\)
# 思路二
云函数创建对应资源容器时,装载的资源包括号称比黑洞更重的node_modules
依赖文件等。这或许是影响冷启动速度的原因之一。
那么服务提供商是否可以维护这样一个容器池,容器池中存放着已经装载完依赖环境的半完成态容器。
如何维持半完成态的容器?
用户第一次调用云函数时,以nodejs
云函数为例,判断package.json
文件中的依赖,容器池中是否有满足的半完成态容器,将对应容器直接分配该用户,半完成态容器直接装载业务逻辑代码,跳过装载依赖文件和环境这一步。
# 思路三
考虑业务上的相关联,比如说对于购物小程序来说,当用户调用查询商品详情云函数 \(A\) 时,那么下一步用户可能就要调用 统一下单云函数\(B\) 。显然,在调用 \(A\) 云函数时,对 \(B\) 云函数进行资源容器预装载是合理的,一般习惯称这种方法叫预热。 这是业务逻辑上的关联,然而不同的程序有不同的关联逻辑,服务提供商是否可以提供一个配置文件,由开发者指定预热的云函数资源容器。
例如对于查询商品详情的云函数\(A\) , 假定其关联预热云函数文件名为trigger.json
{
"uniOrder",//统一下单
"addCart",//加入购物车
"addFavor"//加入手册
}
当调用云函数 \(A\) 时,就会预热 uniOrder
等云函数。
现在对于这种预热方法的实现方式,是在调用云函数 \(A\) 的时候,在执行代码逻辑内部 cloud.callFunction()
来提前预热云函数,这种写法方式不够优雅,而且如果对正常触发和预热触发逻辑处理不够完善,还可能会引起其他问题。
# 思路四
既然冷启动的原因是因为资源容器会被销毁,再次触发需要重新创建,那么为什么不能一次创建长期维持呢?保持最小的资源消耗的情况下,使得云函数容器不被销毁,相比较于每次冷启动对用户端明显的交互影响,这些微不足道的资源消耗简直不值一提。服务商可以给愿意付出资源消耗量的开发者提供配置,由开发者指定每次创建容器的存活时间。
例如在config.json
文件中新增配置项keep_alive:60
,就代表容器如果60分钟内无操作才会被销毁。配置为 0 代表永不销毁,然后为开发者提供个主动销毁容器的方法调用。
现阶段,开发者们常用维护容器状态的思路是配置定期器,每隔一段时间触发一次,以维持容器不被销毁,这样子对资源的消耗量其实是比较大的。而且是不够优雅,属于空逻辑,还需要为触发器实现专门的空逻辑处理部分,防止对业务代码产生干扰。
# 思路五
既然客户端开发可以使用插件,可以依赖于第三方服务,那么为什么云函数不可以呢?云服务商可以提供常驻的通用的云函数服务,如提供一个用户角色权限管理云函数,不需要用户自己设计部署,由官方提供调用方式即可。
亦或者是提供一个地址管理云函数,与官方数据打通,使用云函数提供调用方法等等,不胜枚举。
总而言之,言而总之,我是很喜欢FaaS
架构的开发形式,如果能解决冷启动对交互体验的影响,我相信会有更多的开发者投身于这个生态中。
参考文献
[1] 苗立尧. Faas,又一个未来? [EB/OL] 求知
[2] Linux中国. 如何提升你的云函数性能 [EB/OL] 小程序官方社区