随着以函数即服务(Function as a Service)为代表的无服务器计算(Serverless)的广泛使用,很多用户遇到了涉及多个函数的场景,需要组合多个函数来共同完成一个业务目标,这正是微服务“分而治之,合而用之”的精髓所在。本文以阿里云 函数计算
为例,试图全面介绍函数组合的常见模式和使用场景,希望有助于选择合适的解决方案。
虽然本文主要介绍的是函数组合,但是基本思想也可用于服务组合。
函数同步调用函数
在这种模式里,函数直接调用 InvokeFunction 同步 API 执行一个或者多个函数,等待被调用函数返回结果,然后继续执行。这是一个有些争议的模式,不使用同步调用通常有以下原因:
- 从费用的角度:由于函数计算按照函数实际执行时间收费,调用者在等待被调用函数返回前也会产生一定费用。
- 执行时长限制:由于函数最长执行10分钟,这就决定了调用的其它函数执行时间之和有限。
- 从容错的角度:被调用者出错会直接影响调用者,如果这个调用链很长,则这种错误会一直蔓延到最初的调用者,容错性较差。同时由于执行时长限制,调用者通常不容易针对错误做长时间重试。
上面的理由是在有些场景下成立的,但是微服务最经典最常见的组合方式就是同步调用,函数作为微服务的一种实现方式,这种同步调用的需求是不可回避的,在有些场景下采用同步调用模式是值得考虑的,这些场景包括:
- 调用者函数需要被调用函数执行结果做后续处理或者返回给客户端。
- 函数执行时间较短,最好在毫秒到秒级别。
- 调用者是无状态的,不需要针对被调用者的错误做复杂重试。
在这种模式里,调用者通常不需要做复杂的计算,主要时间花在调用函数和等待返回上,因此调用者函数可以设置较小的内存,以减少费用;调用者还可以根据业务需求缓存调用结果,减少对其它函数的调用,从而节约费用和增强容错性。
函数通过 API 网关调用函数
与函数直接调用函数不同,这里
被调用函数在 API 网关后面,使它看起来更像一个微服务。这种模式的限制和使用场景跟上面的直接调用模式类似,不同之处是 API 网关提供了一些额外的能力,比如认证和限流等。如果被调用者要根据某些业务信息对请求做处理,则使用 API 网关是一个好的选择。当然,使用 API 网关也带来了额外的延迟和费用,如果不需要使用 API 网关提供的能力,则让函数直接调用函数是一个更好的选择。
函数异步调用函数
在这种模式里,函数执行完自身业务逻辑后,调用 InvokeFunction 异步 API 通知其它函数执行并退出,它不关心被调用函数是否执行完成。函数计算的 InvokeFunction 异步 API 是通过队列实现的,异步请求首先被写入队列,然后有一个组件从队列里消费请求,执行相应函数。这种模式的优势和使用场景有:
- 对执行延迟不敏感的场景,适合削峰填谷,减轻对依赖服务的压力。函数计算会根据用户的资源使用情况动态的调整队列的处理速度,平滑请求对系统的压力,当然这种平滑也可能会导致一些请求被延缓执行。
- 调用者可以较容易的触发多个被调用者执行,实现 Fan-Out 模式。比如一个视频处理函数可以异步调用多个函数来分别将视频转换成不同格式。
- 由于每个函数所花时间都用在执行业务逻辑,而非等待其它函数返回上,没有产生不必要的费用。
这种模式有如下局限性:
- 调用者显然无法知道被调用者的执行结果。
- 被调用者之间最好是独立执行的,不需要相互协调,否则通过数据库来协调执行会增加复杂度。例如,上面提到的视频处理函数调用多个函数分别将视频转换成不同格式,如果需要在所有的视频转换完成后发邮件通知用户,这就需要等待所有的转换函数执行结束。这种等待多个事件发生的模式叫做 Fan-In,不适合通过这种异步调用模式实现。
基于消息主题的函数调用
与上面的模式需要指定被调用函数不同,基于事件触发模式让调用者只需要发布消息,不关心谁去消费消息。在消息服务(MNS)中,主题是发布消息的目的地,发布者可以通过 PublishMessage API 向主题发布消息, 主题的订阅者会接收到发布到主题上的消息,
MNS 与函数计算服务的集成让函数可以直接作为订阅者,简化了消息处理应用的开发和运维代价。
和上面的异步调用模式相比,基于消息主题的调用模式有以下优势:
- 进一步解耦函数调用。调用者无需知道被调用者的信息,只需要约定消息的格式。
- 得益于一个主题可以对应多个订阅者,更容易实现 Fan-Out 模式。发布者只需要发布一条消息就能触发多个函数。在上面的异步调用方式中,调用者仍需要显式调用一个或者多个函数来触发执行。
- 虽然主题模式本质上是消息服务推送消息给订阅者,不会考虑订阅者的承载能力,但是由于阿里云消息服务是通过 InvokeFunction 异步 API 调用函数,因此这种模式也具备基于消息队列模式的削峰填谷能力。
这种模式的局限性跟异步调用函数类似:
- 很难支持等待多个事件触发一个函数的场景(Fan-In)。
- 需要做一定工作才能跟踪多个函数执行状态。
- 很难限制单个函数的执行时间或者所有函数的总执行时间。
- 无法针对函数执行错误定义重试策略。
基于对象存储服务的函数调用
函数计算服务除了集成了消息主题以外,还集成了对象存储服务 (OSS),表格存储等其它事件源,这些事件源服务同样可以作为连接函数的渠道。比如,一个非函数应用对 OSS 对象操作(创建,删除等)后,通常需要其它渠道(比如消息服务)通知其它应用做后续处理,而由于
OSS 和函数计算的集成,只需简单配置,这些事件就可以直接传递给自定义函数,而不需要额外渠道再传递信息,简化了数据处理流水线的开发和运维代价。
基于日志库的函数调用
日志服务的
日志库是一个流(Stream)存储,生产者写入数据到日志库,消费者读出数据并处理。
日志服务和函数计算的集成,使得消费者可以是函数,从而实现了通过流存储来协调函数调用。上面所有模式都是调用者显式或者隐式的触发一个或多个被调用函数执行,而这里生产者函数写入的数据不一定会立刻被消费者函数处理,日志服务会根据用户配置将多个数据批量推送给消费者函数。
这种模式有以下优势:
- 日志服务会针对每个分区(Shard)并行调用函数,如果数据吞吐较大,可以通过扩展分区来提高消费吞吐能力。
- 日志服务给函数推送的同一分区数据是串行的和严格保序的,在老的数据没有消费成功前不会推送新的数据。
- 日志服务会持续推送相同数据到函数直到函数消费数据后返回正常。
基于函数工作流的函数组合
函数工作流(Function Flow,简称 FnF)是一个用来协调多个分布式任务执行的全托管 Serverless 云服务,简化了开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让用户更好的专注业务逻辑开发。可以说函数工作流是转为函数组合而生,有效的解决了上面几种异步组合模式的局限性。
上面的所有模式都是通过点对点的方式来组合函数,而函数工作流是一个集中的协调者,函数之前不再直接或者间接通信,所有的触发都是由函数工作流发起,不同函数的输入和输出是通过函数工作流来传递。因此,这种方式下的函数代码全是业务逻辑相关,没有上面模式里的发送主题消息,或者调用其它函数的逻辑,实现更加清晰。
函数工作流有以下优势:
- 服务编排能力:可以将流程逻辑与任务执行分开,支持多种控制原语,比如顺序执行多个函数,根据函数执行结果选择执行其它函数,让多个函数并行处理数据,或者让一个函数并行处理一组数据等,以及上面的 Fan-In 模式。函数工作流还内置了错误重试和捕获能力,节省了编写编排代码的时间。
- 支持长流程:无论是毫秒级还是长达一年的业务流程,FnF 都可以跟踪整个流程,确保流程执行完成。
- 流程状态管理:FnF 会管理流程执行中的所有状态,包括跟踪它所处的执行步骤,以及存储在步骤之间的输入输出。您无需自己管理流程状态,也不必将复杂的状态管理构建到任务中。
- 协调分布式组件:FnF 能够协调运行在不同架构,不同网络,不同语言实现的分布式应用。无论是私有云、专有云的应用想要平滑过渡到混合云、公共云,还是单体架构的应用想要演进到微服务架构,FnF 都能在其中发挥协调作用。函数工作流通过集成消息队列,让任何可以访问消息队列的应用也可以作为流程一部分,相互协作,共同完成业务目标。
- 可视化监控:FnF 提供了可视化界面来协助定义流程和查看执行状态,方便您快速识别故障位置,并快速排除故障问题。
- 运维全托管和按需付费:FnF 让您从基础设施维护中解放出来,提供了安全的、高可用的、高容错的弹性服务。用户只需支付步骤转换费用,不使用不产生费用。
函数工作流目前还不支持同步调用方式,如果您有同步调用需求,欢迎联系我们(见文章最后钉钉客户群)。
总结
总的来说,上面的组合模式可以分为同步和异步两种,在场景适合的情况下优先选择异步模式,享受异步模式带来的松耦合,高容错等特性,否则使用同步模式。在异步模式中,如果需要编写复杂的组合逻辑,支持可靠的重试,把控整个流程,则推荐使用函数工作流,否则使用消息主题或者其它事件源服务来组合函数。
上面的模式也不是割裂的,它们在有些场景下可以搭配使用,比如有时候基于对象存储服务的调用需要触发多个函数,这就可以
结合使用 OSS 的事件触发和函数工作流;又比如
函数工作流通过消息队列将任务发送给更广泛的消费者,触达函数计算无法触达的地方。
最后,欢迎加入函数工作流和函数计算客户群。
“ 阿里巴巴云原生 关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”