CloudEvents三部曲:规范篇

随着云原生的发展(云原生的下一个五年在哪里?),逐步进入深水区,业界需要一种统一的事件定义和描述规范,以提供跨服务、跨平台的交互能力。CloudEvents事件规范应运而生,并得到了行业的广泛关注,包括主要的云提供商和 SaaS 公司。
对于CloudEvent的介绍、规范说明及实践落地,将以三篇系列文章进行说明,本文为《CloudEvent三部曲:规范篇》.


一、规范概述

事件虽无处不在,但事件生产者往往会以不同的方式来描述事件。

关于事件的行业规范的缺失意味着开发人员必须重复对接事件处理流程。这增加了跨系统(多系统)发送事件的难度,降低事件处理的可移植性。

CloudEvents是一种用通用格式描述事件数据的规范,以提供跨服务、平台和系统的互操作性。事件格式指定了如何用特定的编码格式对 CloudEvents 进行序列化。支持这些编码的CloudEvents实现必须遵守相应事件格式中指定的编码规则,所有实现都必须支持JSON格式,protobuf的格式也在有序推进中。

设计目标

CloudEvents通常用于分布式系统,以允许服务在开发期间低耦合,并独立部署,便于连接以构建新应用程序。

CloudEvents的目标是定义事件系统的互操作性,该系统允许服务生成或使用事件,其中包括独立开发和部署生产者和使用者。生产者可以在消费者收听之前生成事件,并且消费者可以进行相关订阅操作。需要注意的是,此工作产生的规范集中于事件格式的互操作性以及在各种协议(例如HTTP、protobuf)上发送事件格式的显示方式。规范不关注事件产生者或事件消费者的处理模型。

CloudEvents的核心是定义了一组元数据,称为属性,以及有关在系统之间传输的事件和这些元数据应如何出现在消息中。该元数据是定义了将请求路由到适当组件并促进该组件对事件进行适当处理所需的最少数据集。尽管这可能意味着事件本身的某些应用程序数据可能会作为CloudEvents属性集的一部分,但这也是为了正确传递和处理消息而进行的必要操作。

非规范范畴

以下内容不属于规范考虑范畴:

  • 函数构建和调用过程
  • 特定语言的运行时API
  • 单一身份/访问控制系统

二、符号和术语

符号约定

为了清楚起见,当一个功能被标记为”可选”时,这表明消息的生产者和消费者都可以选择支持该功能。换句话说,如果生产者愿意,可以选择将该功能包含在消息中,如果消费者愿意,可以选择支持该功能。那么不支持该功能的消费者就会默默地忽略消息中的那部分内容。生产者需要为消费者忽略该功能的情况做好准备。中间人应该转发选择性属性。

触发

“触发”是指在软件系统运行过程中获取到的事件。这可能是由于系统引发的信号或系统观察到的信号、状态变化、计时器过期或任何其他值得注意的活动而发生。例如,设备可能因为电池电量不足而进入警报状态,或者虚拟机即将执行预定的重启。

事件

“事件”是表示事件发生及其上下文的数据记录。事件被从事件生产者(源)分发到感兴趣的事件消费者。可以根据事件中包含的信息进行分发,但事件不会确定具体的分发目的地。事件将包含两类信息:事件数据代表触发后的数据,而上下文元数据提供关于事件触发的上下文信息。一个事件触发可能会产生多个事件。

生产者

“生产者”是特定实例、过程或设备,用于创建描述CloudEvent的数据结构。

事件源

“来源”是指事件触发时的上下文。在一个分布式系统中,它可能由多个生产者组成。

消费者

“消费者”接收事件,并对其采取行动。它使用上下文和数据来执行一些逻辑,这可能导致新事件的发生。

中介机构

“中介机构”接收包含事件的消息(如中间件),目的是将其转发给下一个接收者,而接收者可能是另一个中间人或消费者。中间人的一个典型任务是根据上下文中的信息将事件转发到接收者。

上下文

上下文元数据将被封装在上下文属性中。工具和应用程序代码可以使用这些信息来识别事件与系统的各个方面或与其他事件的关系。

数据

事件的特定信息。这可能包括有关触发事件的信息、修改后的数据等。

事件格式

事件格式指定了如何将CloudEvent序列化为字节序列。独立的事件格式(如JSON、protobuf格式)可指定独立于任何协议或存储介质的序列化。

消息

事件通过消息从事件源传输到目的地。

“结构化报文”是指使用独立的事件格式对事件进行完全编码并存储在消息主体中。

“二进制报文”是指事件数据存储在消息体中,事件属性作为消息元数据的一部分进行存储。

协议

消息可以通过各种行业标准协议(例如HTTP,AMQP,MQTT,SMTP),开源协议(例如Kafka,NATS)或平台/供应商特定协议(AWS Kinesis,Azure Event Grid)进行传递。

上下文属性

符合本规范的每个CloudEvent必须包含上下文等属性。这些属性在描述事件的同时,被设计为可以独立于事件数据进行序列化。这使得它们可以在目的地被检查,而不需要对事件数据进行反序列化。

属性命名约定

CloudEvents规范定义了对各种协议和编码的映射,随附的CloudEvents SDK针对各种运行时和语言。其中有些将元数据元素视为大小写敏感,而另一些则不敏感,而且单个CloudEvent可能会通过多个跳转来实现,中间涉及协议、编码和运行时。因此,本规范限制了所有属性的可用字符集,以防止大小写敏感问题或与通用语言中标识符的允许字符集冲突。

CloudEvents属性名称必须由ASCII字符集中的小写字母或数字组成。属性名称应具有描述性和简洁性,长度不得超过20个字符。

类型系统

以下是可用于属性中的抽象数据类型。这些类型中的每个类型都可以由不同的事件格式和协议元数据字段来表示。本规范为每个类型定义了一个标准的字符串编码,所有的实现都必须支持。

  • 布尔型 - 值为truefalse的布尔值。
    字符串编码:大小写敏感的truefalse值。
  • 整型 - 2,147,483,648+2,147,483,647之间的整数。这是一个有符号的、32位的、二进制编码的范围。事件格式不一定要使用这个编码,但它们必须只
    使用这个范围内的整数值。
    字符串编码: 根据RFC 7159,第6节,JSON号码的整数部分。
  • 字符串 - 允许的Unicode字符序列。以下字符不允许使用。
  • U+0000-U+001F 和 U+007F-U+009F (两个范围都包括在内)中的”控制字符”,因为大多数字符没有约定的含义,有些字符,如 U+000A (换行符),在HTTP头等上下文中不能使用。
  • 代码点被Unicode识别为非字符。
  • U+D800-U+DBFF和U+DC00-U+DFFF,这两个范围都包括在内,除非正确地成对使用。

而”\uD800\uDEAD”是合法的。

  • 字节 - 字节序列。
  • URI–绝对统一的资源标识符。
  • 字符串编码:RFC4648中定义的绝对统一资源标识符。
  • URI-reference - 统一资源标识符引用。
  • 时间戳 -使用Gregorian Calendar的日期和时间表达式。

所有的上下文属性值必须是上面列出的类型之一。属性值可以以本地类型或标准字符串的形式呈现。表示 CloudEvent 或任何扩展的强类型编程模型必须能够将常规字符串编码转换为最适合抽象类型的运行时/语言原生类型。

例如,在给定的实现中,时间属性可以用语言的本机日期时间类型来表示,但必须提供RFC3339字符串,并且在映射到HTTP消息的报文头时,必须可转换为RFC3339字符串。

同样,CloudEvents协议绑定或事件格式实现也必须能够在编码或协议元数据字段中将标准字符串编码转换为相应的数据类型。时间戳类型的属性值确实可以作为一个字符串通过多次跳转,并且只在生产者和最终消费者那里以本地运行时/语言类型的形式实现。时间戳也可能被路由为本地协议类型,并可能在生产者和消费者端被映射到/从各自的语言/运行时类型,而永远不会以字符串的形式实现。

序列化机制的选择将决定上下文属性和事件数据的序列化方式。例如,在JSON序列化的情况下,上下文属性和事件数据可能同时出现在同一个JSON对象中。

必要属性

以下属性必须在所有CloudEvents中出现。

id

属性名 id
类型 字符串
描述 标示事件。生产者必须确保 source + id 对于每个独立的事件都是唯一的。如果一个重复的事件被重新发送(例如,由于网络错误),它可能有相同的id。消费者可能会认为 source 和 id 相同的事件是重复的。|
约束 1. 必须
2. 必须是一个非空字符串
3.必须在生产者范围内是唯一的
范例 1.由生产者维护的事件计数器
2.UUID

source

属性名 source
类型 URI-reference
描述 标示事件发生的上下文。通常包括事件源的类型、发布事件的组织或产生事件的过程等信息。URI中编码的数据背后的确切语法和语义由事件生产者定义。生产者必须确保 source + id 对于每个独立的事件都是唯一的。应用程序可以为每个独立的生产者分配一个唯一的source,这样就很容易产生唯一的ID,因为没有其他生产者会有相同的source。应用程序可以使用 UUIDs、URNs、DNS 权限或特定于应用程序的方案来创建唯一的source标识符。一个源也可以包括多个的生产者。在这种情况下,生产者必须合作,确保 source + id 对于每个独立的事件都是唯一的。
约束 1. 必须
2.必须为非空URI-reference
3.推荐使用绝对URI
范例 全网唯一的URI,具有DNS权限。
1.https://github.com/cloudevents
2.mailto:cncf-wg-serverless@lists.cncf.io

通用唯一的URN与UUID
1.urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66

应用特定的标识符
1./cloudevents/spec/pull/123
2./sensors/tn-1234567/alertsd.1-555-123-4567

specversion

属性名 specversion
类型 字符串
描述 事件所使用的CloudEvents规范的版本。该版本可用于解释上下文。
约束 1. 必须
2.必须是一个非空字符串
3.必须在生产者范围内是唯一的

type

属性名 type
类型 字符串
描述 该属性包含一个描述事件类型的值,描述与起源事件相关的事件类型。该属性通常用于路由、监控、策略执行等。该属性的格式是由生产者定义的。
约束 1. 必须
2.必须是一个非空字符串
3.应该以一个反转的DNS名称为前缀。前缀域决定了定义这个事件类型的语义的组织。
范例 a.com.github.pull.create
b.com.example.object.delete.v2
可选属性

以下为在CloudEvents中出现的可选属性。

datacontenttype

属性名 datacontenttype
类型 字符串
描述 数据的内容类型。该属性使数据可以携带任何类型的内容,其格式和编码可能与所选事件格式不同。例如,使用JSON信封格式渲染的事件可能会携带一个XM的数据,这个属性被设置为”application/xml”就会通知消费者。不同的数据内容如何渲染不同的数据内容类型值的规则在事件格式规范中定义了,对于一些二进制模式的协议绑定,该字段直接映射到各自协议的内容类型元数据属性。二进制模式和内容类型元数据映射的规范性规则可以在相应的协议中找到。在某些事件格式中,datacontententtype属性可能会被省略。例如,如果一个JSON格式的事件没有datacontententtype属性,那么就意味着数据是符合”application/json”类型的JSON值。换句话说:一个没有datacontententtype的JSON格式事件与一个datacontenttype=”application/json”的事件完全等同。当将一个没有datacontenttype属性的事件消息翻译成不同的格式或协议绑定时,目标datacontenttype应该明确地设置为源的隐含datacontenttype。
约束 1. 可选
2.如果存在,必须遵守RFC 2046中规定的格式。

dataschema

属性名 dataschema
类型 URI
描述 指明数据所遵循的 schema。对模式不兼容的更改应该通过不同的URI来反映。
约束 1. 可选
2.必须是一个非空字符串

subject

属性名 subject
类型 字符串
描述 描述了事件生产者(通过 source 标识)上下文中的事件主题。在发布-订阅场景中,订阅者通常会订阅由源发出的事件,但如果源上下文有内部子结构,仅有源标识符可能不足以作为任何特定事件的限定符。
约束 1.可选
2.必须是一个非空字符串
范例 当在blob-存储容器内创建新的blob时,订阅者可能会对此进行订阅。在这种情况下,事件 source 标识了订阅范围(存储容器),type 标识了”blob创建”事件,id唯一标识了事件实例,以区分已创建的相同名称的blob;新创建的blob的名称携带在subject中。
source:https://example.com/storage/tenant/container
subject: mynewfile.jpg

time

属性名 time
类型 Timestamp
描述 事件发生的时间戳。如果无法确定事件发生的时间,则该属性可以由 CloudEvents 生产者设置为其他时间(如当前时间),但同一源的所有生产者在这方面必须保持一致。换句话说,它们要么都使用实际发生的时间,要么都使用相同的算法来确定所使用的值。
约束 1.必须
2.如果存在,必须遵守RFC 3339中规定的格式。
扩展上下文属性

CloudEvents可包含任意数量的具有不同名称的附加上下文属性,称为”扩展属性”。扩展属性必须遵循与标准属性相同的命名惯例,并使用与标准属性相同的类型系统。扩展属性在本规范中没有定义的含义,它们允许外部系统将元数据附加到事件中,就像HTTP自定义头一样。扩展属性总是按照与标准属性一样的绑定规则进行序列化。然而,本规范并不妨碍扩展将事件属性值复制到消息的其他部分,以便与同样处理消息的非CloudEvents系统进行交互。这样做的扩展规范应该指定如果复制的值与 cloud event 序列化的值不同,接收者应该如何解释消息。

扩展的定义应该定义属性的所有方面,例如,其名称、类型、语义和可能的值。新的扩展定义应该使用一个描述性足够强的名称,以减少与其他扩展名称同名的可能性。

许多协议支持发送者附加元数据的能力,例如作为 HTTP 头文件。虽然 CloudEvents 接收方没有被强制要求传递和处理这些元数据,但建议通过某种机制来解决这些元数据,以表明它们是非CloudEvents元数据。


事件数据

CloudEvents可包括与事件发生相关的特定信息。如果存在,该类信息将被封装在数据中。

长度限制

在许多场景中,CloudEvents 将通过一个或多个中间件转发,每个中间件都可能会对转发事件的大小进行限制。CloudEvents也可能会被转发到消费者,比如嵌入式设备,这些设备受存储或内存限制。

事件的”大小”是指事件的线上大小,包括事件的线上传输的每一个比特:协议帧元数据、事件元数据和事件数据,基于所选的事件格式及所选的绑定协议。

如果应用配置要求在不同的协议之间转发事件,或要求对事件进行重新编码,则应考虑应用所使用的最有效的协议和编码,以符合这些大小限制。

  • a.中间件必须转发大小为64KByte或以下的事件。
  • b.消费者应接受至少64 KByte大小的事件。

实际上,这些规则将允许生产者安全地发布大小不超过64KB的事件。

一般来说,CloudEvents 发布者应该通过避免在事件有效载荷中嵌入大型数据项来保持事件的紧凑性,而是使用事件有效载荷链接到这些数据项。从访问控制的角度来看,这种方法还可以让事件的分布范围更广,因为通过解析链接访问事件相关的细节,可以实现差异化的访问控制和选择性的披露,而不是直接将敏感细节嵌入事件中。

隐私与安全

互操作性是本规范背后的主要驱动力,要实现这样的行为,需要将一些信息公开,导致信息泄露的可能性。

请考虑以下几点,以防止不经意间的数据泄漏,特别是在利用第三方平台及通信网络时。

  1. 上下文属性
    敏感信息不应在上下文属性中携带或表示。
    CloudEvents生产者、消费者和中间人可以审查并记录上下文属性。

  2. 数据
    业务数据应进行加密,以限制受信任方的可见性。数据加密是生产者和消费者之间的协议,不属于本规范的范围。

  3. 协议绑定
    应采用工业级的安全方案,以确保CloudEvents的可信及安全的信息交换。


参考资料