写点什么

Serverless 架构开发 / 优化案例

作者:刘宇
  • 2021 年 12 月 15 日
  • 本文字数:1920 字

    阅读完需:约 6 分钟

Serverless 架构开发/优化案例

慎用部分 Web 框架的特性

异步

函数计算是请求级别的隔离,所以可以认为这个请求结束了,实例就有可能进入到一个“静默”的状态了,而在函数计算中,API 网关触发器通常是同步调用(以阿里云函数计算为例,通常只在定时触发器、OSS 事件触发器、MNS 主题触发器和 IoT 触发器等几种情况下是异步触发),这就意味着当 API 网关将结果返回给客户端的时候,整个函数就会进入“静默”状态,或者被销毁,而不是会继续执行完异步方法,所以通常情况下像 Tornado 等框架就很难在 Serverless 架构下发挥其异步的作用。当然,如果使用者需要异步能力,可以参考云厂商所提供异步方法,以阿里云函数计算为例,阿里云函数计算为用户提供了一种异步调用能力,当函数的异步调用被触发后,函数计算会将触发事件放入内部队列中,并返回请求 ID,而具体的调用情况及函数执行状态将不会返回。如果用户希望获得异步调用的结果,则可以通过配置异步调用目标来实现:


定时任务

Serverless 架构下,应用一旦完成当前请求,就会进入到“静默”状态,甚至实例会被销毁,所以这就导致一些自带定时任务的框架没有办法正常执行定时任务。因为函数计算通常是由事件触发,不会自主定时启动,例如 Egg 项目中设定了一个定时任务,但是在实际的函数计算中如果没有通过触发器触发该函数,那么该函数是不会被触发,函数也不会从内部自动启动来执行定时任务,所以此时可以使用各个云厂商为其 FaaS 平台提供的定时触发器,通过定时触发器触发指定方法,来替代定时任务。

要注意应用组成结构

静态资源与业务逻辑

在 Serverless 架构下,静态资源更应该在对象存储与 CDN 的加持下对外提供服务,否则所有的资源都在函数中,并通过函数计算对外暴露,不仅仅会让函数的真实业务逻辑并发度降低,也会造成更多的成本支出。尤其是将一些已有的程序迁移到 Serverless 架构上,例如 Wordpress 等,更是要注意将静态资源与业务逻辑进行拆分,否则在高并发的情况下,性能与成本都将会受到比较严格的考验。

业务逻辑的进一步拆分

在众多云厂商中,函数的收费标准都是依靠运行时间和配置的内存,以及产生的了流量进行收费的。如果一个函数的内存设置不合理,会导致成本成倍增加。想要保证内存设置合理,更要保证业务逻辑结构的可靠性。


以阿里云函数计算为例,当一个应用,有两个对外的接口,其中有一个接口的内存消耗在 128MB 以下,另一个接口的内存消耗稳定在 3000MB 左右。这两个接口,平均每天会被触发 10000 次,并且时间消耗均在 100ms。如果两个接口写到一个函数中,那么这个函数可能需要将内存设置在 3072MB,同时在用户请求内存消耗较少的接口时,可能在冷启动的情况下难以得到较好的性能表现;如果两个接口分别写到两个函数中,则两个函数内存分别设置成 128MB 以及 3072MB 即可:


通过上表,可以明确看出,当合理的、适当地把业务进行拆分之后会在一定程度上更节约成本,以上面的例子来看,成本节约近 50%。

善于利用函数的厂商特性

各个云厂商的 FaaS 平台都有一些“平台特性”,所谓的平台特性就是说这些功能可能并不是《CNCF WG-Serverless Whitepaper v1.0》中规定的能力,或者描述的能力,仅仅是作为云平台根据自身业务发展和诉求,从用户角度出发挖掘出来,并且实现的功能,可能只在某个云平台或者某几个云平台所拥有的功能,这类功能一般情况下如果利用得当会让业务性能等有质的提升。例如阿里云函数计算提 Pre-freeze & Pre-stop


以阿里云函数计算为例,在平台发展过程中,挖掘出用户痛点(尤其是阻碍传统应用平滑迁移至 Serverless 架构):


  • 异步背景指标数据延迟或丢失:如果在请求期间没有发送成功,则可能被延迟至下一次请求,或者数据点被丢弃。

  • 同步发送指标增加延迟:如果在每个请求结束后都调用类似 Flush 接口,不仅增加了每个请求的延迟,对于后端服务也产生了不必要的压力。

  • 函数优雅下线:实例关闭时应用有清理连接,关闭进程,上报状态等需求。在函数计算中实例下线时机开发者无法掌握,也缺少 Webhook 通知函数实例下线事件。


根据这些痛点发布了运行时扩展(runtime extensions)功能。该功能在现有的 HTTP 服务编程模型上扩展,在已有的 HTTP 服务器的模型中增加了 PreFreeze 和 PreStop webhooks。扩展开发者实现 HTTP handler,监听函数实例生命周期事件,如图所示。


PreFreeze:在每次函数计算服务决定冷冻当前函数实例前,函数计算服务会调用 HTTP GET /pre-freeze 路径,扩展开发者负责实现相应逻辑以确保完成实例冷冻前的必要操作,例如等待指标发送成功等。函数调用 InvokeFunction 的时间不包 PreFreeze hook 的执行时间。


PreStop:在每次函数计算决定停止当前函数实例前,函数计算服务会调用 HTTP GET /pre-stop 路径,扩展开发者负责实现相应逻辑以确保完成实例释放前的必要操作,如关闭数据库链接,以及上报、更新状态等。


发布于: 4 小时前阅读数: 6
用户头像

刘宇

关注

阿里云Serverless云布道师 2020.01.04 加入

阿里云Serverless产品经理,国防科大在读博士,《Serverless架构》、《Serverless实践》、《人人都能学会的Serverless架构》等书籍作者,Serverless Devs发起人,Anycodes在线编程负责人。

评论

发布
暂无评论
Serverless 架构开发/优化案例