核心规范

CloudEvents核心规范

备注:内容来自 https://github.com/cloudevents/spec/blob/v1.0/spec.md

摘要

CloudEvents 是一个厂商中立的规范,用于定义事件数据的格式。

概述

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

缺乏描述事件的常用方法意味着开发人员必须不断重新学习如何接收事件。这也限制了库,工具和基础设施的可能性,以帮助跨环境(如SDK,事件路由器或跟踪系统)发送事件数据。我们可以从事件数据中实现的可移植性和生产力总体上受到阻碍。

CloudEvents 是一种用通用格式描述事件数据的规范,以提供跨服务、平台和系统的互操作性。

事件格式指定了如何用特定的编码格式对 CloudEvents 进行序列化。支持这些编码的 CloudEvents 实现必须遵守相应事件格式中指定的编码规则。所有实现都必须支持 JSON 格式

设计目标

这一段应该是搬到prier里面去了

CloudEvents通常用于分布式系统,以允许服务在开发期间松散耦合,独立部署,以后可以连接以创建新应用程序。

CloudEvents规范的目标是定义事件系统的互操作性,这些事件系统允许服务生成或消费事件,其中生产者和消费者可以独立开发和部署。生产者可以在消费者收听之前生成事件,并且消费者可以表达对尚未生成的事件或事件类别的兴趣。

为此,规范将包括促进互操作性的事件的公共元数据属性,而事件不包含可能用于发送事件的消费者或传输的任何细节。

非目标

以下内容不在规范中:

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

使用场景

下面的列表列举了为开发此规范而考虑的关键使用场景和开发人员视角。这些使用场景并非详尽无遗,并且该规范并非旨在规定如何使用。

这些情景不是规范性的; 任何人都可以自由地创建一个混合这些场景的系统。 这些案例建立了事件生成器,消费者,中间件和框架的通用词汇表。

在这些场景中,我们将事件生成器和事件使用者的角色保持不同。 单个应用程序上下文始终可以同时承担多个角色,包括既是事件生产者又是事件消费者。

  1. 应用程序生成供其他方使用的事件,例如为消费者提供有关终端用户活动,状态更改或环境观察的见解,或允许通过事件驱动的扩展来补充应用程序的功能。

    通常生成与上下文或生产者选择的分类相关的事件。例如,房间中的温度传感器可能由安装位置,房间,楼层和建筑物进行上下文限定。运动会结果可能按联赛和球队分类。

    生产者应用程序可以在任何地方运行,例如在服务器或设备上运行。

    生产的事件可以由生产者或中间人直接呈现和发布; 作为后者的示例,考虑由设备在诸如LoRaWAN或ModBus之类的有效载荷大小约束的网络上发送的事件数据,并且其中符合该规范的事件将由网络网关代表生产者呈现。

    例如,气象站通过LoRaWAN每5分钟发送一个12字节的专有事件有效载荷,指示天气状况。 然后使用LoRaWAN网关以CloudEvents格式将事件发布到Internet目标。LoRaWAN网关是事件生产者,代表气象站发布,并将适当地设置事件元数据以反映事件的来源。

  2. 应用程序将事件用于显示,归档,分析,工作流程处理,监视条件和/或为业务解决方案及其基础构建块的操作提供透明度等目的。

    消费者应用程序可以在任何地方运行,例如在服务器或设备上运行。

    消费事件的应用程序通常会对以下内容感兴趣:

    • 区分事件,使得完全相同的事件不会被处理两次。
    • 识别和选择原始上下文或生产者指定的分类。
    • 识别事件相对于始发上下文和/或相对于挂钟的时间顺序。
    • 理解事件中携带的与上下文相关的详细信息。
    • 关联来自多个事件生成器的事件实例,并将它们发送到相同的使用者上下文。

    在某些情况下,消费应用程序可能对以下内容感兴趣:

    • 从原始上下文获取有关事件主题的更多详细信息,例如获取有关需要特别访问授权的已更改对象的详细信息。例如,HR解决方案可能出于隐私原因在事件中仅发布非常有限的信息,并且任何需要更多数据的事件消费者将必须用他们自己的授权上下文中从HR系统获得与事件相关的细节。
    • 在原始上下文中与事件的主题交互,例如在被告知刚刚创建了该blob之后读取存储blob。

    消费者的兴趣所在会激励生产者在事件中包含哪些信息。

  3. 中间件将事件从生产者路由到消费者,或者转发到其他中间件。生成事件的应用程序可能会将因消费者需求而产生的某些任务委托给中间件:

    • 针对多个类别之一或原始事件上下文,管理许多并发感兴趣的消费者
    • 代表消费者根据事件的类别或原始上下文处理过滤条件。
    • 转码,如从JSON解码后编码为MsgPack
    • 改变事件结构的转换,例如从专有格式到CloudEvents的映射,同时保留事件的身份和语义完整性。
    • 即时“推送式”发送给感兴趣的消费者。
    • 存储最终交付的事件,由消费者的(“pull”),或者在延迟之后由中间件的(“push”)发起提取。
    • 监视事件内容或事件流以进行监视或诊断。

    为了满足这些需求,中间件将对以下内容感兴趣:

    • 可用于事件的分类或上下文化的元数据鉴别器,使得消费者可以表达对一个或多个这样的类或上下文的兴趣。例如,消费者可能对与文件存储帐户内的特定目录相关的所有事件感兴趣。
    • 元数据鉴别器,允许区分该类型或上下文的特定事件的主题。例如,消费者可能想要过滤掉与以“.jpg”结尾的新文件相关的所有事件(文件名是"新文件"事件的主题),用于描述它已经注册为感兴趣的文件存储帐户内的特定目录的上下文。
    • 用于编码事件及其数据的指示符。
    • 事件及其数据的结构布局(模式)的指示符。

    生产者的事件是否可通过中间件消费,是生产者的委托选择。

    在实践中,中间件可以在改变事件的语义含义时承担生成器的角色,在基于事件采取行动时承担消费者的角色,或在路由事件不进行语义更改时承担中间件的角色。

  4. 框架和其他抽象使得与事件平台基础设施的交互更简单,并且通常为多个事件平台基础设施提供通用API区域。

    • 框架通常用于将事件转换为对象图,并将事件分派给某些特定的用户代码处理或用户规则,以允许使用应用程序对原始上下文和特定主题中的特定类型的事件作出反应。

    • 框架最感兴趣的是它们抽象的平台上的语义元数据共性,因此可以统一处理类似的活动。

    对于体育应用程序,使用该框架的开发人员可能对来自联盟中的球队(感兴趣的topic)的今天的游戏(subject)的所有事件感兴趣,但是想要处理与“substitution”的报告不同的“goal”的报告。为此,框架将需要一个合适的元数据鉴别器,使其不必理解事件细节。

符号和术语

符号约定

本文档中的关键词 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, 和 “OPTIONAL” 按照 RFC 2119 中的描述进行解释。

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

术语

该规范定义了以下术语:

Occurrence/发生

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

Event/事件

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

Producer/生产者

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

Source/来源

“Source/来源”是指发生(occurrence)出现的上下文。在一个分布式系统中,它可能由多个Producer组成。如果一个源不知道CloudEvents,外部生产者代表源创建CloudEvents。

Consumer/消费者

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

中介机构

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

Context/上下文

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

数据

有关事件(即payload/有效载荷)的特定领域信息。这可能包括有关发生(occurrence)的信息、有关被更改的数据的详细信息或更多信息。有关更多信息,请参阅事件数据(Event Data)部分。

事件格式 / Event Format

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

Message/消息

事件通过消息从源(Source)传输到目的地(Destination)。

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

“二进制模式报文/binary-mode message” 是指事件数据(event data)存储在消息体中,事件属性作为消息元数据的一部分存储。

Data/数据

关于发生/occurrence(如有效载荷)的领域特定信息。这可能包括有关发生的信息,有关已更改数据的详细信息或更多。

Protocol/协议

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

Protocol Binding/协议绑定

协议绑定描述了事件如何通过给定协议发送和接收事件,特别是事件如何映射到该协议中的消息。

上下文属性

符合本规范的每个 CloudEvent 必须包含上下文属性,而且要指定为 REQUIRED,可以包含一个或多个 OPTIONAL 上下文属性,也可以包含一个或多个扩展属性。

这些属性在描述事件的同时,被设计为可以 独立于事件数据进行序列化。这使得它们可以在目的地被检查,而不需要对事件数据进行反序列化。

属性命名约定

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

CloudEvents 属性名称必须由 ASCII 字符集中的小写字母(‘a’至’z’)或数字(‘0’至'9’)组成。属性名称应具有描述性和简洁性,长度不得超过 20 个字符。

类型系统

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

  • Boolean - 值为 “true “或 “false “的布尔值。

    • 字符串编码:大小写敏感的 “true “或 “false” 值。
  • Integer -2,147,483,648到+2,147,483,647之间的整数。这是一个有符号的、32位的、二进制编码的范围。事件格式不一定要使用这个编码,但它们必须只使用这个范围内的整数值。

    • 字符串编码: 根据RFC 7159,第6节,JSON号码的整数部分。
  • String - 允许的Unicode字符序列。以下字符不允许使用。

    • U+0000-U+001F 和 U+007F-U+009F (两个范围都包括在内)中的 “控制字符”,因为大多数字符没有约定的含义,有些字符,如 U+000A (换行符),在HTTP 头等上下文中不能使用。

    • 代码点被Unicode识别为非字符。

    • U+D800-U+DBFF和U+DC00-U+DFFF,这两个范围都包括在内,除非正确地成对使用。因此,(在JSON符号中)"\uDEAD “是无效的,因为它是一个未配对的代名词,而”\uD800\uDEAD “是合法的。

  • Binary - 字节序列。

    • 字符串编码: Base64 编码,按照 RFC4648 的规定进行编码。
  • URI–绝对统一的资源标识符。

    • 字符串编码:RFC4648中定义的绝对统一资源标识符。字符串编码:RFC 3986 第 4.3 节中定义的绝对 URI。
  • URI-reference - 统一资源标识符引用。

    • 字符串编码:RFC 3986 第4.3 节中定义的URI-reference。URI-reference - RFC 3986 第4.1 节中定义的URI-reference。
  • Timestamp - 使用Gregorian Calendar的日期和时间表达式。

    • 字符串编码:RFC 3339。

所有的上下文属性值必须是上面列出的类型之一。属性值可以以本地类型或标准字符串的形式呈现。

表示 CloudEvent 或任何扩展的强类型编程模型必须能够将常规字符串编码转换为最适合抽象类型的运行时/语言原生类型。

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

同样,CloudEvents 协议绑定或事件格式实现也必须能够在编码或协议元数据字段中将标准字符串编码转换为相应的数据类型。

时间戳类型的属性值确实可以作为一个字符串通过多次跳转,并且只在生产者和最终消费者那里以本地运行时/语言类型的形式实现。时间戳也可能被路由为本地协议类型,并可能在生产者和消费者端被映射到/从各自的语言/运行时类型,而永远不会以字符串的形式实现。

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

必须的属性

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

id

  • Type: String
  • 描述: 标示事件。生产者必须确保 source + id 对于每个独立的事件都是唯一的。如果一个重复的事件被重新发送(例如,由于网络错误),它可能有相同的id。消费者可能会认为 source 和 id 相同的事件是重复的。
  • Examples:
    • 由生产者维护的事件计数器
    • UUID
  • Constraints:
    • REQUIRED
    • 必须是一个非空字符串
    • 必须在生产者范围内是唯一的

source

  • Type: URI-reference

  • Description: 标示事件发生的上下文。通常包括事件源的类型、发布事件的组织或产生事件的过程等信息。URI 中编码的数据背后的确切语法和语义由事件生产者定义。

    生产者必须确保 source + id 对于每个独立的事件都是唯一的。

    应用程序可以为每个独立的生产者分配一个唯一的source,这样就很容易产生唯一的ID,因为没有其他生产者会有相同的source。应用程序可以使用 UUIDs、URNs、DNS 权限或特定于应用程序的方案来创建唯一的source标识符。

    一个源也可以包括多个的生产者。在这种情况下,生产者必须合作,确保 source + id 对于每个独立的事件都是唯一的。

  • Constraints:

    • REQUIRED
    • MUST be a non-empty URI-reference
    • 推荐使用绝对URI
  • Examples

specversion

  • Type: String
  • Description: 事件所使用的 CloudEvents 规范的版本。该版本可用于解释上下文。兼容的事件制作者在引用此版本的规范时,必须使用 1.0 的值。
  • Constraints:
    • REQUIRED
    • MUST be a non-empty string

type

  • Type: String
  • Description: 该属性包含一个描述事件类型的值,描述与起源事件相关的事件类型。该属性通常用于路由、可观察性、策略执行等。该属性的格式是由生产者定义的,可能包括类型的版本等信息–更多信息请参见Primer中的属性版本化。
  • Constraints:
    • REQUIRED
    • MUST be a non-empty string
    • 应该以一个反转的DNS名称为前缀。前缀域决定了定义这个事件类型的语义的组织。
  • Examples
    • com.github.pull.create
    • com.example.object.delete.v2

可选属性

以下为可在 CloudEvents 中出现的可选属性。有关 OPTIONAL 定义的更多信息,请参阅 Notational Conventions 部分。

datacontenttype

  • Type: String per RFC 2046

  • Description: data 值的内容类型。该属性使数据可以携带任何类型的内容,其格式和编码可能与所选事件格式不同。例如,使用JSON信封格式渲染的事件可能会携带一个XML有效载荷的数据,这个属性被设置为 “application/xml “就会通知消费者。不同的数据内容如何渲染不同的数据内容类型值的规则在事件格式规范中定义了;例如,JSON事件格式在3.1节中定义了这种关系。

    对于一些二进制模式的协议绑定,该字段直接映射到各自协议的内容类型元数据属性。二进制模式和内容类型元数据映射的规范性规则可以在相应的协议中找到。

    在某些事件格式中,datacontententtype属性可能会被省略。例如,如果一个JSON格式的事件没有datacontententtype属性,那么就意味着数据是符合 “application/json “媒体类型的JSON值。换句话说:一个没有datacontententtype的JSON格式事件与一个datacontenttype=“application/json “的事件完全等同。

    当将一个没有datacontenttype属性的事件消息翻译成不同的格式或协议绑定时,目标datacontenttype应该明确地设置为源的隐含datacontenttype。

  • Constraints:

    • OPTIONAL
    • 如果存在,必须遵守RFC 2046中规定的格式。
  • For Media Type examples see IANA Media Types

dataschema

  • Type: URI
  • 指明 data 所遵循的 schema。对模式的不兼容的更改应该通过不同的URI来反映。更多信息请参见Primer中的属性版本化。
  • Constraints:
    • OPTIONAL
    • If present, MUST be a non-empty URI

subject

  • Type: String

  • Description: 描述了事件生产者(通过 source 标识)上下文中的事件主题。在发布-订阅场景中,订阅者通常会订阅由源发出的事件,但如果源上下文有内部子结构,仅有源标识符可能不足以作为任何特定事件的限定符。

    在上下文元数据中识别事件的主体(而不是仅在 data 有效载荷中),这在中间件无法解释 data 内容的通用订阅过滤场景中特别有帮助。在上面的例子中,订阅者可能只对以'.jpg’或'.jpeg’结尾的blob感兴趣,而主题属性允许为该事件的子集构造一个简单有效的字符串后缀过滤器。

  • Constraints:

    • OPTIONAL
    • If present, MUST be a non-empty string
  • Example:

    • 当在blob-存储容器内创建新的blob时,订阅者可能会注册兴趣。在这种情况下,事件 source 标识了订阅范围(存储容器),type 标识了 “blob创建 “事件,id唯一标识了事件实例,以区分已创建的相同名称的blob的单独出现;新创建的blob的名称在 subject中携带。
      • source:https://example.com/storage/tenant/container
      • subject: mynewfile.jpg

time

  • Type: Timestamp
  • Description: 事件发生的时间戳。如果无法确定事件发生的时间,则该属性可以由 CloudEvents 生产者设置为其他时间(如当前时间),但同一源的所有生产者在这方面必须保持一致。换句话说,它们要么都使用实际发生的时间,要么都使用相同的算法来确定所使用的值。
  • Constraints:
    • OPTIONAL
    • If present, MUST adhere to the format specified in RFC 3339

扩展上下文属性

CloudEvent 可包含任意数量的具有不同名称的附加上下文属性,称为 “扩展属性”。扩展属性必须遵循与标准属性相同的命名惯例,并使用与标准属性相同的类型系统。扩展属性在本规范中没有定义的含义,它们允许外部系统将元数据附加到事件中,就像HTTP自定义头一样。

扩展属性总是按照与标准属性一样的绑定规则进行序列化。然而,本规范并不妨碍扩展将事件属性值复制到消息的其他部分,以便与同样处理消息的非CloudEvents系统进行交互。这样做的扩展规范应该指定如果复制的值与 cloud event 序列化的值不同,接收者应该如何解释消息。

定义扩展

请参阅 CloudEvent 属性扩展,了解有关扩展的使用和定义的更多信息。

扩展的定义应该完全定义属性的所有方面,例如,其名称、类型、语义和可能的值。新的扩展定义应该使用一个描述性足够强的名称,以减少与其他扩展名称碰撞的可能性。特别是,扩展作者应该检查文档中的扩展文档中的已知扩展集–不仅仅是检查可能的名称冲突,还应该检查可能感兴趣的扩展。

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

下面是一个例子,说明了对附加属性的需求。在许多 IoT 和企业用例中,一个事件可以在无服务器的应用程序中使用,该事件可以在多个类型的事件中执行操作。为了支持这样的用例,事件生产者需要在 “上下文属性 “中添加额外的身份属性,事件消费者可以使用这些属性将该事件与其他事件关联起来。如果这种身份属性恰好是事件 “数据 “的一部分,事件生产者也将把身份属性添加到 “上下文属性 “中,这样,事件消费者就可以方便地访问这些信息,而不需要解码和检查事件数据。这样的身份属性也可以用来帮助中间网关确定如何路由事件。

事件数据

正如术语 “Data “所定义的那样,CloudEvents 可包括与事件发生相关的领域特定信息。如果存在,此信息将被封装在 data 中。

  • 描述:事件有效载荷(Payload)。本规范不对该信息的类型进行任何限制。它被编码成由datacontententtype属性指定的媒体格式(例如:application/json),并且当这些相应的属性存在时,它将遵循dataschema格式。
  • Constraints:
    • OPTIONAL

大小限制

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

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

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

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

实际上,这些规则将允许生产者安全地发布大小不超过64KB的事件。这里的 “安全 “指的是,一般来说,期望事件被所有中间人接受并转发是合理的。出于本地的考虑,它是否愿意接受或拒绝该大小的事件,是在任何特定消费者的控制范围内。

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

隐私和安全

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

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

  • 上下文属性

    敏感信息不应在上下文属性中携带或表示。

    CloudEvent 生产者、消费者和中间人可以审查并记录上下文属性。

  • Data

    领域特定 event data 应进行加密,以限制受信任方的可见性。用于这种加密的机制是生产者和消费者之间的协议,因此不属于本规范的范围。

  • Protocol Bindings

    应采用协议级的安全性,以确保CloudEvents的可信和安全交换。

示例

下面的示例展示了一个被序列化为JSON的CloudEvent:

{
    "specversion" : "1.0",
    "type" : "com.github.pull.create",
    "source" : "https://github.com/cloudevents/spec/pull",
    "subject" : "123",
    "id" : "A234-1234-1234",
    "time" : "2018-04-05T17:31:00Z",
    "comexampleextension1" : "value",
    "comexampleothervalue" : 5,
    "datacontenttype" : "text/xml",
    "data" : "<much wow=\"xml\"/>"
}