关于云原生 CloudEvents 探索与思考

最近遇到一个很严肃的问题,什么样的 events 算是合格的 events 结构。为什么需要定义source,定义在设计 events 事件体时是否有可以直接遵守的规范。针对这个问题,我收集了大量资料,从基础接口设计规范到CloudEvents 调研。索性也薄浅的了解了一些,就分享下这块的心得吧。有接口设计方面诉求的同学可以参考下。不过大部分也是研发的字段定义诉求了。

首先,我们先来了解下什么是 Events。

Events(以下统称事件)在系统设计中其实已经变的无处不在,它主要是接口级的事件描述。理论来讲定义接口和接口产生的事件是一个比较简单的事儿,但是现在缺乏一种对事件的统一描述,服务使用方和提供方往往要花费大量的时间沟通字段定义,凭直觉设计事件属性,并在将来的使用过程中疲于新增或修改事件的属性。所以在云上,定义事件其实有个 CNCF(云原生计算基金会) 牵头的 CloudEvents 标准协议来制定所有的服务事件规范。这其实也算是业内比较惯常使用的了(当然不包括 AWS,google 原因大家都懂, Azure 支持很到位)。

好了,这个应该很好理解,大体就是服务通过怎样的格式投递使用。

概念篇

那么从事件源到触发,整个流程是怎样的呢?下图很好的解释了里面的关系:

云服务代码中通常使用事件来连接不同的系统,其中一个系统中状态的改变会导致代码在另一个系统中执行。例如,当 Source 源接收到外部信号(例如HTTP或RPC)或检测到系统变化的值,它便会生成事件。Source 生成一条消息并将其中事件封装在协议中,事件到达目的地,触发事件数据并调用。

事件本身其实可以通过各种行业标准协议(例如HTTP,AMQP,MQTT,SMTP),开放源代码协议(例如Kafka,NATS)来传递,当然云厂商产生的事件其实一般是由厂商自定义的,这类事件也是本次探讨的核心。

action 的定义其实往往是指事件所附带的特定的事件触发。通常情况下,源是某种托管服务,而操作通常是 Serverless(无服务器化)的能力(例如AWS Lambda 或云函数 SCF)中的自定义代码。

定义篇

大体的概念大致梳理清楚了,那下面我们来看看最重要的定义,搞清楚定义,我们才能更好的研究事件的范围是如何的。

首先,我们来看下必选参数的定义:

id
类型: String
描述:事件标识,生产者必须确保source+id 对于每个不同的事件都是唯一的。如果重复事件被重新发送(例如网络错误),允许具有相同的id。消费者可以假设事件具有相同source且id重复。

 

source
类型: URI-reference

描述:标识事件发生的上下文,通常将包括诸如事件源的类型,发布事件的组件或产生事件的过程之类的信息。一般由事件生产者定义数据背后的确切语法和语义。生产者必须确保source+id对于每个不同的事件都是唯一的。应用程序可以使用UUID,URN,DNS 授权或特定于整体技术架构的方案来创建唯一 source 标识符。

一个来源可以包括多个生产者。

 

type
类型: String
说明:该参数包含一个描述与源事件相关的事件类型。通常用于路由,策略执行等。格式应该由生产者定义,并且可能包含诸如的版本之类的信息。

其次,是非必选参数定义:

datacontenttype
类型:String

描述:data 值的内容类型,该参数允许 data 赋予任何类型的内容。例如,通常使用 JSON 格式事件可能也会通过XML返回data信息,此时可以通过将此属性设置为“ application/xml”来通知使用者。

 

time

类型: Timestamp
描述:发生该事件的时间戳,如果不能确定发生时间,则生产者可以将此属性设置为其他时间(例如当前时间),但是所有生产者source必须保持一致。换句话说就是要么全部使用实际事件发生时间,要么全部使用相同的时间获取方式来确定所使用的时间戳。

核心参数的定义大抵如此了,其次是一些 Event Data,Attributes,和相关扩展字段,这里就不一一阐述了。这里给放个例子,也是 cloudevents 官方用例,可以供大家参考:

{
    "specversion" : "1.0", // cloudevent版本信息 选择性填写即可
    "type" : "com.github.pull.create",
    "source" : "https://github.com/cloudevents/spec/pull",
    "subject" : "123",
    "id" : "A234-1234-1234",
    "time" : "2020-04-05T17:31:00Z",
    "comexampleextension1" : "value",
    "comexampleothervalue" : 5,
    "datacontenttype" : "text/xml",
    "data" : "<much wow=\"xml\"/>"
}

 

总结篇

CloudEvents 的核心其实是定义了一组关于不同组件间传输事件的元数据,以及这些元数据应该如何出现在消息体中。

其主旨大抵如下:

  • 事件规范化
  • 降低平台集成难度
  • 提高FaaS的可移植性
  • 源事件可追踪
  • 提升事件关联性

准确的事件体,事件信息才可以做出更稳定的系统架构,永远保持对事件的敬畏。

 

附 一些术语及定义:

Occurrence:发生,指事件逻辑上的发生,基于某种情况,事件出现了。

Event:事件,表示事件以及上下文的数据记录。可以根据事件中的信息决定路由,但事件本身并不包含路由信息。

Producer:生产者,真正创造事件的实例或组件

Source:源,事件发生的上下文,可以由多个producer组成。

Consumer:消费者,接收事件并对事件进行消费

Intermediary:中介,接收包含事件的消息(message),并转发给下一个接收方,类似路由器

Context:上下文,上下文元数据被封装到context attributes中。用来判断事件与其它系统的关系

Data:数据,也可以叫做 payload

EventFormat:事件格式,例如 json

Message:消息,封装事件并将其从source传递到destination。

Protocol:协议,可以是行业标准如 http,开源协议如 kafka 或者供应商协议如 AWS Kinesis

Protocol Binding:协议绑定,描述如何通过给定的协议收发事件,如何将事件放到消息里