干货分享 | 移动云函数计算中的CloudEvents应用实践
Posted 苏研大云人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了干货分享 | 移动云函数计算中的CloudEvents应用实践相关的知识,希望对你有一定的参考价值。
友情提示:全文6000多文字,预计阅读时间6分钟
摘要
本文简要介绍CloudEvents事件规范及其优势,以CloudEvents对象存储事件在函数计算中的应用场景为例,阐述了对象存储事件的CloudEvents规范化改造过程,最后详细描述了对象存储事件在函数计算系统中自动化处理的实现过程,为开发者实现CloudEvents规范事件落地实践提供了重要的参考意义。
一、CloudEvents简介
CloudEvents是一种以通用方式描述事件数据的规范,该规范旨在简化跨服务、跨平台事件的声明和交互。CloudEvents规范的目标是定义事件系统的互操作性,允许服务产生或消费事件,其中生产者和消费者可以独立开发和部署。
CloudEvents的核心是定义了一组关于系统间传输事件的元数据(被称为属性),以及这些元数据应该如何出现在该消息中。这些元数据是将请求路由到适当的组件并促进该组件对事件进行适当处理所需的最低限度的信息集。
许多云服务提供商和开源项目已经采用了CloudEvents规范。例如:CloudEvents v1.0已经在Knative的Eventing框架,Red Hat的EventFlow,Eclipse Vert.x和Debezium,SAP的Kyma,Servelss.com的Event Gateway等项目中实现。微软宣布将通过Event Grid服务(一项由Azure集中管理的事件服务,支持用户通过“发布-订阅”机制发送和接收事件)提供对CloudEvents的支持;字节跳动在其函数计算产品中利用CloudEvents规范统一事件源标准;阿里云发布了最新产品 EventBridge,一款无服务器(Serverless)事件总线服务,其使命是作为云事件的枢纽,以标准化的CloudEvents 1.0协议连接云产品和云应用,提供中心化的事件治理和驱动能力,帮助用户轻松构建松耦合、分布式的事件驱动架构。
二、CloudEvents优势
从历史上看,行业缺乏描述事件数据的统一标准,这意味着开发人员需要学习如何跨系统使用各种类型的事件数据,从而难以构建可移植的工具。例如,服务器端代码中处理一种类型的事件数据,当接受到其他事件类型的数据时,该服务端代码无法处理,就需要开发另外一种处理事件逻辑。CloudEvents定义了一套一致的元数据,且将业界的云供应商、企业软件巨头和初创企业的无服务器社区聚集在一起,实现并支持该规范。另外从阿里云EventBridge、Azure Event Grid的规划来看,都将CloudEvent作为了云内通信、云间通信的事件格式标准。
CloudEvents规范优势体现在以下几个方面:
2.1 一致性优势
每个产生事件的系统通常都有自己的格式,自己的事物标记方式,自己的命名约定。生产者生产事件供消费者使用时,使用CloudEvents规范,消费者不需要为平台或服务的差异性编写特定的消息格式处理逻辑,进而使用通用逻辑处理事件数据,方便事件消费者提高开发效率,并降低系统复杂度。
在基于CloudEvents实现的Knative Eventing框架中,无论事件如何传递到事件处理系统中,只需要将事件转换为CloudEvents,事件生产者和消费者都无需了解事件在Knative Eventing系统中的路由转发,订阅,过滤等任何处理过程。
2.2 路由优势
CloudEvents使用定义明确的元数据来扩展或标识事件,中间件消息系统可以根据其属性完成相应的事件处理,而不必了解流经系统的每个事件。中间件将事件从生产者路由到消费者,或者转发到其他中间件的时,CloudEvents会保留事件的身份和语义完整性,用于事件的分类过滤或元数据的鉴别。例如:消费者利用过滤功能只关注特定用户;或者利用元数据鉴别只接收后缀为.doc的新建文件等。
2.3 交互优势
CloudEvents提供了各种事件格式(例如JSON)和协议(例如HTTP,AMQP,MQTT和Kafka)序列化事件的规范,同时也提供了多种开发语言的SDK(如:Go、javascript、Java、C#、Ruby和Python等),利用SDK可以极大方便开发人员进行集成开发。利用 CloudEvents系统框架对内解耦各模块的通信能力,提高可维护性;对外与其他事件平台基础设施的交互将更简单,并且方便为其他平台设施提供通用 API,降低交互的复杂性,提高系统平台的扩展性。
三、基于CloudEvents的对象存储事件应用场景介绍
移动云函数计算(SFC)是一项基于事件驱动的函数托管计算服务,用户无需购买和管理服务器等基础设施,只需编写业务代码(即函数),即可在弹性、免运维、高可靠的环境中运行业务代码。函数计算中采用CloudEvents为标准事件格式,其他云上产品可以通过CloudEvents与函数计算集成。目前函数计算已经与对象存储、Redis产品集成。其中,对象存储事件能触发用户的业务函数执行,实现对象存储数据自动化处理,实现诸如图片压缩、水印处理、文件加密等功能。
3.1 对象存储介绍
移动云对象存储是面向企业和开发者的具有高安全、高可靠、大容量、低成本等特点的对象存储产品,用于存储图片、音视频、文档等非结构化数据,并实现在线管理数据。支持高并发访问,具有完备的API及SDK接口,帮助用户数据快速上云。
3.2 函数计算介绍
基于事件驱动服务是函数计算的核心功能之一,我们的产品使用 Knative Eventing 的Broker/Trigger事件处理模型对事件进行过滤分发,采用CloudEvents标准数据格式进行事件传输。Knative Eventing系统架构如下所示:
上图所示函数计算侧接收到CloudEvents事件通知后,采用Broker/Trigger模型对事件进行过滤处理。Trigger通过配置过滤规则订阅Broker事件消息,Filter根据Trigger 过滤规则进行事件的过滤处理,最后将满足过滤规则的事件分发到KService(函数计算中的用户函数),进而触发用户函数执行。
3.3 对象存储事件应用场景
函数计算产品支持触发器功能,触发器的作用就是接受外部消息后对用户函数进行触发,用户可以在被触发的函数中根据外部消息来进行针对性的处理。
在函数计算与对象存储的集成方案中,我们利用对象存储产品的事件通知功能,将它作为触发器的事件源,即触发器可以订阅对象存储的相关消息,当“感兴趣”的事件发生时,对象存储会发送事件通知给函数计算触发器,最终触发用户函数。典型的应用场景如下图所示:
例如,客户上传大量图像数据到对象存储中后,需要尽快把图像按照客户指定的方式处理。图像处理是基于预先编写好的图像处理函数,在短时间内准备大量的计算资源进行大规模并行处理,函数从对象存储中下载原图,实时处理后将结果图像返回到对象存储中。
下面提供了一段视频,演示了函数计算对象存储触发器的使用,并展示了函数计算对对象存储上传图片的自动化处理过程。
针对上述对象存储事件的应用场景示例,本文根据移动云上对象存储与函数计算的集成实践经验,将在第四章节介绍如何实现对象存储的CloudEvents事件,在第五章节介绍函数计算与对象存储集成过程以及如何实现CloudEvents事件的自动化处理。
四、如何实现对象存储的CloudEvents事件
本节介绍如何将对象存储事件转化为CloudEvents事件,详细阐述了设计思路和实现方式,供其他产品参考。
4.1 获取Go语言版本CloudEvents SDK
针对第三章节中的对象存储示例场景,使用Go语言实现对象存储事件的适配器,通过官方Golang CloudEvents SDK可将应用程序与CloudEvents集成,具体步骤如下:
使用go mod将sdk模块添加为依赖项:
go get github.com/cloudevents/sdk-go/v2@v2.2.0
在业务代码中导入cloudevents模块:
import cloudevents "github.com/cloudevents/sdk-go/v2"
通过http协议接受CloudEvents事件,简单示例如下:
func Receive(event cloudevents.Event) {
fmt.Printf("CloudEvents.Event\n%s", event.String())
}
func main() {
c, err := CloudEvents.NewDefaultClient()
if err != nil {
log.Fatalf("failed to create client, %v", err)
}
log.Fatal(c.StartReceiver(context.Background(), Receive));
}
4.2 获取对象存储事件的事件内容格式
对象存储的桶通知功能提供了一种信息发送机制,当桶上发生某些事件时,对象存储能够发送对象存储事件通知,对象存储事件通知内容格式采用Json格式,其数据结构示例如下所示:
{
"Records": [{
"onestRegion": "us-west-2",
"eventName": "ObjectCreated:Put",
"eventSource": "cmcc:onest",
"eventTime": "2020-06-03T09:10:41.378Z",
"eventVersion": "2.1",
"requestParameters": {
"sourceIPAddress": "42.200.244.155"
},
"responseElements": {
"ID-2": "6YnMz839c09h3LBq8XXQM2EgtcUs6lb4kXmp",
"request-ID": "3CF269A8CE04E45D"
},
"S3": {
"bucket": {
"arn": "arn:cmcc:onest:::bucketnotifytest",
"name": "bucketnotifytest",
"ownerIDentity": {
"principalID": "A28IQMSJ3JKNE7"
}
},
"configurationID": "1f88a7ff-9b3b-4dee-84da-893024a26c96",
"object": {
"key": "b21b84d653bb07b05b1e6b33684dc11b",
"size": 1305107,
"eTag": "b21b84d653bb07b05b1e6b33684dc11b",
"sequencer": "0C0F6F405D6ED209E1"
},
"onestSchemaVersion": "1.0"
},
"userIDentity": {
"principalID": "A28IQMSJ3JKNE7"
}
}]
}
在对象存储CloudEvents改造过程中,需要对象存储源事件的部分字段为CloudEvents属性赋值,以供事件消费者使用。对象存储事件部分字段说明如下:
eventName字段表示对象存储支持事件类型列表,可以作为CloudEvents的事件类型以供消费者筛选消息;
s3.bucket.name字段表示存储桶名称;
s3.configurationID字段表示对象存储事件的配置ID,可通过对象存储配置接口配置,在函数计算中,该字段值配置为函数ID,用作事件过滤的关键字段;
s3.object.key字段表示对象存储中对象名称或key值,对象存储桶中该对象的变更触发桶通知。
4.3 将对象存储事件转化为CloudEvents格式
4.3.1设计思路
CloudEvents使用定义明确的元数据来扩展或注释现有事件,为了正确传递和处理消息,现有事件本身的某些数据会作CloudEvents属性集的一部分。一个CloudEvents事件中,其必须具备ID,Source,Type, Specversion四个属性,同时有Datacontenttype,Dataschema,Subject,Time四个可选属性,也可包含任意数量的扩展属性。
ID属性:生产者必须确保id 对于每个独立的事件都是唯一的。
Source属性:标示事件发生的上下文。通常包括事件源的类型、发布事件的组织或产生事件的过程等信息。
Type属性:该属性包含一个描述事件类型的值,描述与起源事件相关的事件类型。该属性通常用于路由、可观察性、策略执行等。
Specversion属性:事件所使用的 CloudEvents 规范的版本,默认为1.0版本。
Datacontenttype属性:该属性使数据可以携带任何类型的内容,其格式和编码可能与所选事件格式不同,例如:“application/json”格式、“application/xml”格式。
Dataschema属性:指明事件数据所遵循的统一资源标识。
Subject属性:描述了事件生产者上下文中的事件主题。
Time属性:事件发生的时间戳。
对象存储事件在函数计算中的应用,需要根据CloudEvents的核心定义配置事件各个属性,将对象存储事件转化为CloudEvents事件。在函数计算Knative Eventing的Broker/Trigger事件处理模型中,Trigger根据CloudEvents的属性Source字段对事件进行过滤,将满足过滤规则的事件路由到函数服务。因此对象存储的CloudEvents事件,要设置Source属性字段作为过滤事件的字段。对象存储事件所有必要属性配置在下面实现方式中具体描述。
4.3.2实现方式
对象存储事件转化为CloudEvents规范采用Go语言开发实现,首先获取到Go语言实现的CloudEvents SDK,该SDK中定义了CloudEvents事件的数据结构,通过NewEvent方法创建了一个CloudEvents事件实体,通过CloudEvents的Set方法写入数据字段。
下面代码示例是实现对象存储事件CloudEvents格式化中定义Event属性并写入对象存储事件数据字段。
event := CloudEvents.NewEvent(CloudEvents.VersionV1)
event.SetID(notification.ResponseElements.ID-2 )
event.SetSource(notification.Onest.ConfigurationID )
event.SetType( notification.EventName)
event.SetDataContentType(CloudEvents.ApplicationJSON)
event.SetSubject(notification.Onest.Object.Key)
event.SetTime(eventTime)
event.SetData(notification)
实现对象存储事件CloudEvents1.0的关键设置说明如下:
通过CloudEvents的NewEvent方法创建CloudEvents事件实体event;
利用SetSource方法为CloudEvents的Source属性赋值,该字段的值由事件通知的ConfigurationID组成,在Trigger过滤规则配置中,通过Source字段进行匹配过滤,如果该属性的值与过滤规则完全匹配,则触发函数执行;
通过SetType方法设置CloudEvents的事件类型,该属性的值由对象存储事件的EventName(事件类型)构成,当消费者接受到CloudEvents事件时,可以通过Type方法获取到对象存储事件类型;
通过SetID方法,配置CloudEvents事件的ID,该ID作为事件的索引区分其他同类事件。该字段可自定义唯一的ID赋值,如自定义生成的UUID。该实例中采用对象存储事件的ResponseElements.ID-2赋值;
通过SetDataContentType方法,设置CloudEvents事件数据内容格式,该属性设置为CloudEvents规范支持的JSON数据格式;
SetSubject方法主要设置CloudEvents事件的主题,可为空,在本实例中设置为对象存储事件的对象名称,消费者可以通过Subject方法获取事件对象名称;
CloudEvents的SetData方法将源事件内容整体写入。上述示例把对象存储的全部事件内容写入,以便消费者获取到对象存储的原始事件。
4.3.3 对象存储事件的CloudEvents格式
转化后的对象存储CloudEvents格式如下所示:
cloudevents.Event
Validation: valid
Context Attributes,
specversion: 1.0
type: cmcc.onest.ObjectCreated:Put
source: cmcc:onest..test1
subject: test9.txt
id: 203678b5-23db-4039-bd64-83aac26e0d7f
time: 2020-08-24T04:27:05.683863583Z
dataschema: 1f88a7ff-9b3b-4dee-84da-893024a26c96
datacontenttype: application/json
Extensions,
knativearrivaltime: 2020-08-24T04:27:05.819158468Z
knativehistory: onest-broker-kne-trigger-kn-channel.b4be0500-c445-11ea-8af1-4c32758b00ad.svc.cluster.local
traceparent: 00-2a74d1962585d8d7307dc61ab4f9ba51-d77649ad284ee371-00
Data,
{
"eventVersion": "2.0",
"eventSource": "cmcc:onest",
"onestRegion": "",
"eventTime": "2020-08-24T04:27:05.683863583Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "test"
},
"requestParameters": {
"sourceIPAddress": "223.105.0.190"
},
"responseElements": {
"request-id": "",
"id-2": ""
},
"S3": {
"onestSchemaVersion": "1.0",
"configurationId": "1f88a7ff-9b3b-4dee-84da-893024a26c96",
"bucket": {
"name": "test1",
"ownerIdentity": {
"principalId": "test"
},
"arn": "arn:cmcc:onest:::test1"
},
"object": {
"key": "test9.txt",
"size": 10,
"eTag": "3ee9d34dff913552247a6d9e489b58ed",
"versionId": "",
"sequencer": "839a5bd9-c213-4d1d-9deb-25c73c6d1df6.74185.281",
"metadata": null
}
},
"eventId": "213-4d1d-9deb-25c73c6d1df"
}
五、实现对象存储事件自动化处理
5.1 对象存储与函数计算集成架构
集成系统架构如下图所示:
如以上架构图所示,对象存储与函数集成主要由三个部分组成,它们之间通过事件进行交互通信。
第一部分是对象存储服务,客户端授权用户访问对象存储上传下载文件等操作,用户可设置桶通知规则。用户设置桶通知规则,如果用户的请求符合该规则,则对象存储侧发送事件通知给BC-MQ消息队列;
第二部分是中间件BC-MQ服务,BC-MQ作为对象存储和函数计算通信的桥梁,将对象存储事件系统与函数计算系统进行解耦;
第三部分是函数计算服务,函数计算侧对象存储事件适配器通过订阅BC-MQ消息,接受对象存储的事件通知,将事件通知转化为CloudEvents消息格式并转发Knative Eventing系统。用户通过给指定函数创建对象存储触发器,配置函数触发规则,若Eventing系统接受的事件与该规则匹配,触发器将触发用户函数执行。
5.2对象存储与函数计算集成过程
函数计算侧通过调用对象存储侧事件通知接口配置事件通知,并将触发目标函数的ID通过接口配置到对象存储的触发规则中。当对象存储触发事件规则后,对象存储的事件消息中携带所配置的函数ID信息。函数计算侧通过函数ID触发目标函数。
对象存储侧提供配置用户事件通知接口,函数计算侧调用该接口配置存储桶通知规则;
对象存储侧支持用户桶事件消息通知功能,并可将存储桶消息事件主动发送到中间件BC-MQ;
函数计算侧通过消费中间件BC-MQ指定Topic中的消息,获取对象存储侧事件通知;
函数计算侧将获取到的事件通知转化为CloudEvents格式;通过一定规则过滤CloudEvents格式消息,将消息发送到指定的函数,触发函数执行;
函数计算侧可通过设置多种类型的对象存储处理函数模板供用户创建函数,通过创建触发器触发函数执行;
函数计算侧提供函数模板包括(文件处理函数模板,图片处理函数模板等)。
5.3接收事件触发函数自动化处理
对象存储侧通过配置事件通知规则,当符合对象存储事件通知规则后,对象存储侧将事件通知发送到消息队列BC-MQ中,函数计算侧通过对象存储适配器订阅消息队列中的消息获取消息。
对象存储将事件通知发送到BC-MQ的一个Topic中,该Topic由对象存储侧与函数计算侧共同约定;
函数侧对象存储适配器订阅该Topic中的事件通知。
函数计算订阅BC-MQ的消息,通过对象存储适配器接受消息并转化为CloudEvents格式,将消息转发到函数计算Eventing系统中,Trigger对事件进行过滤。Trigger通过对CloudEvents格式的对象存储事件的事件源属性(Source属性)进行过滤,获取到事件源中ConfigurationID,通过ConfigurationID匹配目标函数ID,最后将满足过滤规则的事件分发到函数,并触发目标函数执行,实现对象存储事件的自动化处理。
六、未来展望
通过使用Severless框架,可以大大减少开销和成本。新的架构打破了人们的习惯思维,它让服务器不可见,并提供了一个极具成本效益的服务。它给所有开发人员带来的是软件架构和应用程序部署新方式。
事件驱动通过采用统一的事件标准,CloudEvents来建立云上的事件枢纽,让Serverless开发集成云服务、云边端应用更简单。
展望未来十年,对于所有主要的云提供商而言,支持CloudEvents将有助于减轻支持多种事件服务的负担,并允许开发人员创建标准库。CloudEvents规范下构建的Serverless系统,跨不同服务和平台的事件声明和交付的新兴标准也逐渐变为CloudEvents。
END
作者简介
毕小红
2018年6月加入中国移动云能力中心,Iaas产品部云原生组函数计算小组成员,主要负责移动云函数计算产品后端研发工作。
往期 · 精选
1、
2、
3、
以上是关于干货分享 | 移动云函数计算中的CloudEvents应用实践的主要内容,如果未能解决你的问题,请参考以下文章