作者:渐意
前语
无论是在大数据处理范畴,还是在音讯处理范畴,使命体系都有一个很关键的才能 – 使命触发去重。这个才能在一些对精确性要求极高的场景(如金融范畴)中是必不行少的。作为 Serverless 化使命处理渠道,Serverless Task 也需求供给这类确保,在用户运用层面及本身体系内部两个维度具备使命的精确触发语义。本文主要针对音讯处理可靠性这一主题来介绍函数核算异步使命功用的技术细节,并展现如安在实践运用中运用函数核算所供给的这方面才能来增强使命履行的可靠性。
浅谈使命去重
在讨论异步音讯处理体系时,音讯处理的基本语义是无法绕开的话题。在一个异步的音讯处理体系(使命体系)中,一条音讯的处理流程简化如下图所示:
图 1
用户下发使命 – 进入行列 – 使命处理单元监听并获取音讯 – 调度到实践 worker 履行
在使命音讯流转过程中,任何组件(环节)或许呈现的宕机等问题会导致音讯的过错传递。一般的使命体系会供给至多 3 个层级的音讯处理语义:
-
At-Most-Once:确保音讯最多被传递一次。当呈现网络分区、体系组件宕机时,或许呈现音讯丢失;
-
At-Least-Once:确保音讯至少被传递一次。音讯传递链路支撑过错重试,运用音讯重发机制确保下游必定收到上游音讯,但是在宕机或许网络分区的场景下,或许导致相同音讯传递屡次。
-
Exactly-Once 机制则能够确保音讯精确被传送一次,精确一次并不是意味着在宕机或网络分区的场景下没有重传,而是重传关于接受方的状况不发生任何改变,与传送一次的结果相同。在实践生产中,往往是依赖重传机制 & 接收方去重(幂等)来做到 Exactly Once。
函数核算能够供给使命分发的 Exactly Once 语义,即无论在何种状况下,重复的使命将被体系以为是相同的触发,从而只进行一次的使命分发。
结合图 1,假如要做到使命去重,体系至少需求供给两个维度的确保:
-
体系侧确保:使命调度体系本身的 failover 不影响音讯的传递正确性及仅有性;
-
供给给用户一种机制,能够结合事务场景,做到整个事务逻辑的触发+履行去重。
下面,咱们将结合简化的 Serverless Task 体系架构,谈一谈函数核算是怎么做到上面的才能的。
函数核算异步使命触发去重的完结背景
函数核算的使命体系架构如下图所示:
图 2
首先,用户调用函数核算 API 下发一个使命(过程 1)进入体系的 API-Server 中,API-Server 进行校验后将音讯传入内部行列(过程 2.1)。后台有一个异步模块实时监听内部行列(过程 2.2),之后调用资源办理模块获取运行时资源(过程 2.2-2.3)。获取运行时资源后,调度模块将使命数据下发到 VM 等级的客户端中(过程 3.1),并由客户端将使命转发至实践的用户运行资源(过程 3.2)。为了做到上文中所说到的两个维度的确保,咱们需求在以下层面进行支撑:
-
体系侧确保:在过程 2.1 – 3.1 中,任何一个中间过程的 Failover 只能触发一次过程 3.2 的履行,即只会调度一次用户实例的运行;
-
用户侧运用等级去重才能:能够支撑用户屡次重复履行过程 1,但实践只会触发一次 过程 3.2 的履行。
体系侧高雅晋级 & Failover 时的使命分发去重确保
当用户的音讯进入函数核算体系中(即完结过程 2.1)后,用户的恳求将收到 HTTP 状况码 202 的 Response,用户能够以为现已成功提交一次使命。从该使命音讯进入 MQ 起,其生命周期便由 Scheduler 维护,所以 Scheduler 的稳定性及 MQ 的稳定性将直接影响体系 Exactly Once 的完结方案。
在大多数开源音讯体系中(如 MQ、Kafka)一般都供给音讯多副本存储及仅有消费的语义。函数核算所运用的音讯行列(最底层为 RocketMQ)也是同样的,底层存储的 3 副本完结使得咱们无需关注音讯存储方面的稳定性。除此之外,函数核算所运用的的音讯行列还具有以下特性:
-
消费的仅有性:每一个行列中的每一条音讯当被消费后,会进入“不行见形式”。在此形式下,其他顾客无法获取该音讯;
-
每条音讯的实践顾客需求实时更新该形式的不行见时刻;当顾客消费完结后,需求显现的删去该音讯。
因而,音讯在行列中的的整个生命周期如下图所示:
图 3
Scheduler 主要负责音讯的处理,其使命主要有以下几个部分组成:
-
根据函数核算负载均衡模块的调度策略,监听本身所负责的行列;
-
当行列中呈现音讯后,拉取音讯,并在内存中保持一个状况:直到音讯消费完结(用户实例回来函数履行结果)前,不断更新音讯的可见时刻,确保音讯不会再次在行列中呈现;
-
当使命履行完结后,显现删去该音讯。
在行列的调度模型方面,函数核算关于普通用户选用“单行列”的办理形式;即每一个用户的所有异步履行恳求由一个独立行列彼此阻隔,并且由一个 Scheduler 固定负责。这个负载的映射关系由函数核算的负载均衡服务进行办理,如下图所示(咱们在后续文章中还会更为详细的介绍这部分内容):
图 4
当 Scheduler 1 发生宕机或晋级时,使命由两种履行状况:
-
假如音讯还未传递到用户的履行实例中(图 2 中的过程 3.1 ~ 3.2),那么当这台 Scheduler 负责的行列被其他 Scheduler 捡起后,音讯将在消费可见期后再次呈现,因而 Scheduler 2 将再次获取该音讯,做到后续的触发。
-
假如音讯现已开始履行(过程 3.2),当音讯在 Scheduler 2 中再次呈现后,咱们依赖用户 VM 中的 Agent 进行状况办理。此刻 Scheduler 2 将向对应的 Agent 发送履行恳求;此刻 Agent 发现该音讯现已存在于内存中,那么将直接忽略履行恳求,并将履行的结果在履行后通过此链接告知 Scheduler 2,从而完结 Failover 的康复。
用户侧事务等级的分发去重完结
函数核算体系能够做到关于单点故障下的每条音讯精确的消费才能,但是假如用户侧关于同一条事务数据重复触发函数履行的话,函数核算无法辨认不同音讯是否在逻辑上是同一个使命。这种状况往往发生在网络分区。在图 2 中,假如用户调用 1 发生超时,此刻有或许有两种状况:
-
音讯未到达函数核算体系,使命未成功提交;
-
音讯现已到达函数核算并入队,使命提交成功,但由于超时用户无法得知提交成功的信息。
大多数状况下用户会对此次的提交进行重试。假如是第 2 种状况,那么同一个使命将被提交并履行屡次。因而函数核算需求供给一种机制,确保这种场景下事务的精确性。
函数核算供给了 TaskID 这一使命概念(StatefulAsyncInvocationID)。该 ID 大局仅有。用户每次提交使命均能够指定这样一个 ID。当发生恳求超时时,用户能够进行无限次重试。所有的重复重试将在函数核算侧进行校验。函数核算内部运用 DB 对使命 Meta 数据进行存储;当有相同 ID 进入体系时该次恳求将被拒绝,并回来 400 过错。此刻客户端即可得知使命的提交状况。
在实践运用中以 Go SDK 为例,您能够编辑如下触发使命的代码:
import fc "github.com/aliyun/fc-go-sdk"
func SubmitJob() {
invokeInput := fc.NewInvokeFunctionInput("ServiceName", "FunctionName")
invokeInput = invokeInput.WithAsyncInvocation().WithStatefulAsyncInvocationID("TaskUUID")
invokeOutput, err := fcClient.InvokeFunction(invokeInput)
...
}
便提交了一个绝无仅有的使命。
总结
本文介绍了函数核算 Serverless Task 关于使命触发去重的相关技术细节,以便支撑关于使命履行精确性有严格要求的场景。在运用 Serverless Task 后,您无需忧虑任何体系组件的 Failover,您每次提交的使命将被精确履行一次。为了支撑事务侧语义的分发去重,您能够在提交使命时设置使命的大局仅有 ID,运用函数核算供给的才能帮您对使命进行去重处理。
往期推荐
异步使命处理体系,怎么处理事务长耗时、高并发难题?
1 分钟Serverless 极速搭建真网站
运用 Serverless 1 分钟轻松搭建你的首个个人网站!
免费额度,轻松上手!小白也可极速建站:无需考虑服务器和网站源码,咱们为你供给免费核算资源,运维办理服务器。活动期间完结场景体会,即有机会收取天猫超市 10 元代金券。(建议 pc 端体会)
活动时刻: 2022 年 6 月 20 日-7 月 1 日(工作日期间收取)
答疑群: 钉钉搜索 “44700570”
体会地址: 点击阅读原文 or 扫描二维码