Autodesk基于Mesos和Kafka的通用事件系统架构

 叶振安 分布式实验室

Autodesk基于Mesos和Kafka的通用事件系统架构

几个月前我被分配一个新任务,要求拿出一个集中事件系统的解决方案,这个系统可以允许各种后端彼此通讯。这些后端包括动态消息、渲染、数据转换、BIM、身份验证、日志报告、分析等等后端应用。这应该是一个可以适配多种应用、使用场景与可扩展配置文件的通用系统。当然,这个系统还应该有易用的接口,以及动态扩展性。

显然我不可能有时间写很多代码。因为Kafka可以动态扩展,在业内被广泛应用,所以选择它作为核心存储方案(请注意不一定非要使用Kafka)。现在我当然不能直接把它发布,而是通过一些API对前端提供服务。如果不深思熟虑,我也拒绝让后端处理偏移量(offsets),因为这会对如何应对实例失效造成太多限制。

那我该怎么办呢?两个独立的分层:第一是处理访问流量的API层,然后是保持长期链接和有状态消息流进程的后台层,后台层和Kafka通信(也就是说,完成生产者和消费者的角色)。每一层都各自独立扩展,只需要一致性路由来保证客户端持续跟同一个后台消息流进程沟通。

这两层都是100%使用Scala语言开发的Play!框架,而且严重依赖Akka Actor系统(每个节点都运行几百个Actor)。后台层部署了一系列客制化的Kafka消息生产者和消费者,用一系列指定的actors来处理预读和写缓冲,所有服务都以内置的有限状态机(我喜欢这个概念)方式部署,数据搜集由Librato完成(实际上是运行在容器内的collectd),而分析由Splunk完成。

Autodesk基于Mesos和Kafka的通用事件系统架构

手绘图:发布流程图

那么,这两层直接如何路由呢?可以使用RabbitMQ来实现就好了,它可以提供足够的容错性和一致性。AMQP队列可以很好地实现“电话交换机”模式,纵向扩展就不用说了,同时可以使用某些逻辑分区(例如通过hash分配到代表每个交易的cookie上或者类似的特征上),是的一部分固定的后端节点与一个RabbitMQ代理联系起来。

为什么我不对RabbitMQ代理采用集群模式呢?嗯,主要是因为我比较懒而且这也不是必须的。在我看来,每个代理之间的分区流量既有效又容易控制,跟好处比起来额外工作量可以认为忽略不计。

因此简要来说,考虑到不同后台节点运行不同消息流, 因此我们需要的容器技术会继续使用特定的路径。扩展整个架构就跟互不影响扩展每一层任务一样微不足道,唯一实际限制来自于虚拟网卡和带宽。

Autodesk基于Mesos和Kafka的通用事件系统架构

虚线代表某一个特定session持续使用的通道

接下来就是比较有趣的部分:我们如何确保可信交易以及避免错综复杂的失效。要我来说,这个更加容易,只需要一个简单的双向握手(2-phase commit-esque protocol and mode) 协议和模式,这样你的客户端和背景作为镜像状态机处于完全同步状态。可以通过给读写操作请求返回一个明确的请求响应方式来实现。

当需要读取的时候,如果失败就一直重试直到得到一个响应,然后转变为后台服务响应(例如,转发kafka offset,或者预约发布事件)。这样客户端和后台之间的交互流量就真的像“分配一个session”,“读”,“应答响应”,“读”,“应答响应”……..“部署”。

通过这些处理,系统的巨大优势在于可以有效地呈现操作幂等,同时还可以在状态机上编译所有逻辑,无需使用烦人的说明语句。此外,任何网络失效当然会重试,也顺便会从中获得免费的控制流和背压路由(back-pressure routing)。

这样所有的服务对外就表现为一个Apache Thrift的API(现在通过带压缩HTTPS加密隧道,将在未来某个时间计划改为明码TCP)。我有使用Python、Scala、.Net和Ruby的SDK客户端来使用这些令人目眩的新技术。请注意尽管Kafka偏移被客户端管理(尽管这样不透明),但是这样可以使得后端变的更加简单且容易控制。

等一下,当后端节点失效怎么来处置呢?因为有了双向握手协议,因此当读数据时,这并不会成为问题:当后端节点失效时,客户端持续得到失败返回,接下里就会使用现在的偏移量重新申请一个链接。如果向Kafka写入数据时,因为这是异步过程,而且可能会出现双向失效(例如客户端和Kafka代理节点同时有问题),因此有可能会出现问题。为了解决写问题,当有等待任何等待写操作完成时,后台节点会对任何其它访问请求返回失败,或者在最近的可恢复点将任何挂起数据刷新入磁盘(之后在重新读入)。

那么当部分架构出现问题怎么处理呢?同样的解决办法。任何客户端和后台节点之间的中断链接会影响到链接的响应速度,但是因为有双向握手协议,并不会有任何严重的问题。

哦,我忘了提到数据写入Kafka日志之前数据都会自动(AES256)加密,而在Kafka消息生产者和消费者之间共享秘钥是不可能的。从安全角度来看,流链接是通过OAUTH2认证的,每个连接使用单独的MD5-HMAC认证,并通过TLS在后端集群之间传输。

那么,你现在会问到底是怎么部署这套酷毙的系统呢?我们100%部署这套系统是通过标准的Mesos、Marathon集群来部署的(现在还不是DCOS,但是我们很有可能会转向它,并从炫酷的仪表盘受益)。集群目前都是运行在AWS的EC2上,我们一般会在多个c3.2xlarge实例上被复用(在给定区域中执行一个小型部署,10到20算不少了)。请注意,在Kubernetes(不管是EC2还是GCE)也可以使用同样的方法。

Autodesk基于Mesos和Kafka的通用事件系统架构

我们集群运行的一个简单架构示意图

我们使用Ochopod技术完成部署(自集群容器),它同样是开源的,可以将交互操作减到最少。比如将一次构建推入API层时,此系统只负责分配一些新的容器,等分配好之后再逐步清理旧的。所有这些操作都通过一个专门的、在集群中运行的Jenkins从节点来处理(其本身也是一个Ochopod容器)。

事实上,我也开发了Ochothon mini-PaaS,只是为了快速开发运维(devops)所有的容器。

Autodesk基于Mesos和Kafka的通用事件系统架构

Ochothon 命令行展示我们一套预配置的集群

这些Ocho-*平台到底能起到多大作用呢?可以说一个人(比如我)可以管理管理跨越两个区域管理五套这样的系统部署,包括备份架构。而且同时我还有时间来记录下这些博客和代码。

总体上来说,设计和编码实现这样的系统有很多乐趣,而且它现在作为云基础架构的关键部分支撑着我们的生产环境。如果想了解关于这套迷人系统更多的内容,可以联系我们。


上一篇:Revit使用WinForm和WPF编程范式


下一篇:Autodesk Netfabb:助力企业智能化生产