浏览器和Nodejs也是事件驱动的,本质上都是将用户的操作抽象为事件,由事件是监听器监听事件,然后驱动程序执行,只是不同技术的驱动模型实现不同。
- • 对于Faas平台来说一方面可以通过事件来触发,另一方面可以直接调用API来执行(Faas平台都提供了执行函数的API)
- • Faas函数的两种调用方式:同步调用和异步调用
调用方式
同步调用
Faas平台收到函数调用之后会立即给函数分配运行环境来执行函数。
使用函数计算Node.js SDK来同步调用函数
这是一个函数同步调用的实例
- • 其中handler中的x-fc-invocation-type用来表示同步和异步
- • event是事件对象,使用sdk的时候可以自定义事件对象
同步执行的结果
异步调用
异步调用无法直接获取返回结果,适用于运行时间比较长的场景。对于函数计算来说,定时触发器就是异步调用的。
异步调用的结果
异步调用怎么重试?
Faas平台会默认帮你有限次数的重试,但大部分情况下不能只靠Faas所提供的功能。
对于异步调用,如果你关心调用结果的正确性,可以为函数配置“异步调用目标”,将调用结果发送到消息队列或其他服务中,通过监听消息判断得到异步执行结果。
不管函数是同步还是异步执行的,都会有一个默认超时时间60s,否则对于Faas平台持续运行的函数会一直占用资源无法释放。
如果1分钟内的日志量很大,导致查询时间很长,函数执行时间比如3分钟或者更长
那么设置超时时间为10分钟,但是运行函数会越来越多。Faas默认只会存在100个运行中的实例,超过之后,事件队列就会等待其他实例执行完毕之后再生成新的函数实例。
对于案例由于函数并发的限制,如果函数执行时间过长,那么使用new Date()获取时间就会有问题
你以为在12点的时候执行,可能实际在12点10分的时候执行,所以就不能通过new Date来获取当前代码执行时间,而是应用从函数触发器对象当中获取函数被触发执行的时间。
因为定时触发器是异步调用的,需要为函数设置调用目标,对于异常的调用结果进行处理。不过由于这个问题需要函数并发超过限制的时候才会出现,所以没有第一时间发现问题,为未来埋下隐患。如果这个问题不能解决,那么很有可能处理的数据是不准确的。
那现在知道了函数并发限制是怎么造成的,函数上下文启用是怎么回事呢?这就涉及到函数的生命周期了
生命周期
函数启动过程
整个函数的运行过程分为4个阶段:下载代码、启动容器、初始化运行环境、运行代码。只有当Faas接收到触发器事件之后才会启动并运行函数。
下载代码
Faas平台本身不存储代码,而是放在对象存储中,需要执行函数的时候从对象存储中将函数代码下载并解压,所以Faas平台对代码包的大小进行限制,通常代码包不会超过50M。
启动容器
Faas会根据函数的配置启动对应的容器,然后使用容器。
初始化运行环境
初始化运行环境,分析代码依赖,执行用户初始化逻辑。初始化入口函数之外的代码等,最后才是运行代码,调用入口函数执行代码。当函数第一次执行的时候会经过完整的四个步骤,前三个过程称为冷启动,最后一个过程称为热启动。整个冷启动流程耗时达到百毫秒级别。函数运行完毕之后,运行环境保留2-5分钟(和具体云厂商有关)。
如果这段时间内函数需要再次执行,那么Faas平台就会使用上次的运行环境,这就是执行上下文重用。
函数的这个过程也被称为热启动,热启动的耗时完全是启动函数的耗时。
当一段时间内没有请求的时候,函数运行环境就会被释放,直到下次执行到来,直接从冷启动开始初始化。
函数的请求示意图
请求1和请求3是冷启动,请求2是热启动。函数执行完毕之后销毁运行环境,虽然对首次函数执行的性能有损耗,但却极大的提高了资源利用效率,只有在需要执行代码的时候需要初始化运行环境,消耗硬件资源。
如果函数每分钟都执行,则函数几乎都是热启动的,也就是会重复使用之前的执行上下文。
执行上下文就包括函数的容器环境和入口函数之外的代码,但是在实时处理日志的案例中就会出现问题了,由于执行上下文重用所以代码中除入口函数handler之外的代码都会在函数第一次运行的冷启动中执行,后面函数执行的时候都会使用第一次函数执行时已经初始化完毕的值。
这也就是为什么函数每次处理得到的都是同一份数据。
解决这个问题的方法就是让处理时间不被初始化就可以了,将处理时间的代码放在入口函数当中。