我们的系统中或多或少都会用到如下两类业务技术:
异步任务,用于降低接口时延或削峰,提升用户体验,降低系统并发压力;
通知类RPC,用于微服务间状态变更,用户行为的联动等场景;
以上两种场景,都十分适合使用事件驱动来实现。
但是对于事件的描述,现在还缺乏一种统一描述,服务使用方和提供方往往要花费大量的时间沟通字段定义,凭直觉设计事件属性,并在将来的使用过程中会因为新增或修改事件的属性而增加很大的额外工作量。
随着云原生的到来,急需一种统一的事件定义和描述规范,以提供跨服务、跨平台的交互能力,而这就是CloudEvents项目的诞生背景。
CloudEvents是一个用通用格式描述事件数据的规范,以提供跨服务、跨平台和跨系统的互操作性。目前得到了大量的行业关注,从主要的云提供商到流行的SaaS公司都有。CloudEvents由云原生计算基金会(CNCF)主办,于2018年5月15日获批为云原生沙盒级项目。
https://github.com/cloudevents 概要地描述其标准如下:
Event: 描述Occurrence的数据,包括data和context。
Data:最主要的业务数据,由使用者定义。
Context:上下文属性,可以理解成发送event时候的环境信息。用来标识事件与系统或其他事件之间的关系。
业务相关的信息都封装在数据属性中,由业务方自行约定,
一般来说,都是业务model,比如protobuf,这样可以更好的管理。
data: {
"metadata": {
"name": "event-display.15a0a2b54007189b",
"namespace": "default",
"selfLink": "/api/v1/namespaces/default/events/event-display.15a0a2b54007189b",
"uid": "9195ff11-7b9b-11e9-b1f1-00163f005e02",
"resourceVersion": "18070551",
"creationTimestamp": "2019-05-21T07:39:30Z"
},
"involvedObject": {
"kind": "Route",
"namespace": "default",
"name": "event-display",
"uid": "31c68419-675b-11e9-a087-00163e08f3bc",
"apiVersion": "serving.knative.dev/v1alpha1",
"resourceVersion": "9242540"
},
"reason": "InternalError",
"message": "Operation cannot be fulfilled on clusteringresses.networking.internal.knative.dev \"route-31c68419-675b-11e9-a087-00163e08f3bc\": the object has been modified; please apply your changes to the latest version and try again",
"source": {
"component": "route-controller"
},
"firstTimestamp": "2019-05-21T07:39:30Z",
"lastTimestamp": "2019-05-26T07:10:51Z",
"count": 5636,
"type": "Warning",
"eventTime": null,
"reportingComponent": "",
"reportingInstance": ""
}
必选(REQUIRED)参数:
id:事件唯一编号,如UUID
source:数据源信息,用来区别发送方
specversion:使用的cloudEvents规范版本,如0.2
type:发送方定义的事件类型,可以用来过滤和转发,通常此属性用于路由、监控、安全策略等
可选(OPTIONAL)参数:
datacontentencoding:数据属性的编码格式
datacontenttype:数据属性的解析方式,从json扩展到其他可选类型
schemaurl:数据的schema地址
subject:事件的主题,类似MQ里的topic
time:事件发生的时间戳
extions:扩展能力,在实现中是个List
官方目前提供了9种语言的SDK,极大方便开发人员进行集成开发。在业界,已有包括谷歌、AWS、微软、阿里云、腾讯云等开始使用CloudEvents规范向用户提供云平台内的事件通知。
下面使用sdk-go进行简单的使用示范。
事件接收端:
func Receive(event cloudevents.Event) {
fmt.Printf("event info: %v", event)
}
func main() {
c, err := cloudevents.NewDefaultClient()
if err != nil {
log.Fatalf("failed to create client, %+v", err)
}
log.Fatal(c.StartReceiver(context.Background(), Receive));
}
事件发送端:
func main() {
t, err := cloudevents.NewHTTPTransport(
cloudevents.WithTarget("xxx"),
cloudevents.WithEncoding(cloudevents.HTTPBinaryV02),
)
if err != nil {
log.Fatal("failed to create transport, err: %+v ", err)
}
c, err := cloudevents.NewClient(t)
if err != nil {
log.Fatal("failed to create client, err: %+v", err)
}
event := cloudevents.NewEvent()
event.SetID("11111")
event.SetType("com.cloudevents.ts.test")
event.SetSource("xxx")
data := NewXXX()
event.SetData(data)
if err := c.Send(ctx, event); err != nil {
log.Fatal("failed to send cloudevent, err: %+v", err)
}
}