崛起的 Kafka

本文译自 Braedon Vickers 发布在 Movio 上的一篇文章,详尽的探讨了在微服务架构升级的过程当中,如何使用 Kafka 将微服务之间耦合降到最低,同时能让整个系统在保证高可用的前提下作到高可扩展。数据库

随着微服务的流行,不少公司都在尝试将现有的系统进行架构升级。促成 Movio 公司架构改造的一项关键技术就是 Kafka 消息队列。Kafka 是一个开源的分布式消息队列,在可靠性和可扩展性方面有很是大的优点。 咱们正处于把系统迁移到微服务新世界的过程当中。安全

促成此次架构改造的一项关键的技术就是 Apache Kafka 消息队列。它不只成为了咱们基础架构的关键组成部分,还为咱们正在建立的系统架构提供了依据。网络

1.Kafka 概览架构

虽然这篇文章的目的不是在宣扬 Kafka 比其余消息队列系统更优秀,可是本文讨论的某些部分是专门针对它的。对于外行来讲,Kafka 是一个开源的分布式消息队列系统。它最初是由 LinkedIn 研发,如今由 Apache 软件基金会维护。和其余的消息队列系统同样,你能够给它发送消息,同时也能够读取消息。用 Kafka 的说法就是 “生产者” 发送消息,“消费者”接收它们。负载均衡

Kafka 的独特性在于它同时提供简单的文件系统和桥接这两种功能。一个 Kafka 的代理器(broker)最基本的任务就是尽量快地将消息写到磁盘上的日志中,并从中读取消息。消息队列中的消息在持久化以后就不会丢失了,这是整个项目的核心所在。异步

队列(Queue)在 Kafka 中被称为 “主题”(topic),同一个主题共享 1 个或多个 “分区”(partition)。每条消息都有一个 “偏移”(offset)- 一个表明它所在分区位置的偏移量。这使得消费者能够记录它们当前读到的位置,并向代理(broker)请求读取接下来一条(或多条)消息。多个消费者能够同时读取同一个分区的数据,每一个消费者从某个位置上读取数据都是独立于其余消费者的。它们甚至能够在整个分区内随意跳跃式前进或后退。多个 Kafka 代理组合到一块儿成为一个集群。分区是在分散在整个集群中的,这样就提供了可扩展性。它们也能够复制到集群中的多个节点上,实现了高可用性。综合复制与分区持久化特色,进而达到高可靠性。分布式

2. 构建微服务的系统架构微服务

为了帮助咱们理解 Kafka 的用途和影响力,想象一个经过 REST API 从外部接收数据的系统,进而用某种方式进行变换,最后将结果保存到数据库中。咱们想要把这个系统升级为微服务架构,因此首先把这个系统切分为两个服务 - 一个用来提供外部的 REST 接口 (Alpha 服务),另一个用来作数据变换(Beta 服务)。简单起见,Beta 服务同时会负责存储变换后的数据。工具

由一对微服务组成的系统会比提供总体服务(每每是糟糕的)的系统要好一点,因此咱们定义一个接口用于从 Alpha 服务将数据发送到 Beta 服务,用来减小耦合。和使用 REST 接口实现的系统相比,让咱们看看使用 Kafka 实现这个接口将会如何影响该系统的设计和运行。性能

3. 系统的可用性和性能

当数据被提交到 Alpha 的服务,它在响应客户端以前须要确保数据安全地存储在某个地方,或者已经失败 - 客户端须要知道,由于若是出现了错误它能够从新发送(或用一些其余的方式进行恢复), 使用 REST 接口,Alpha 服务在响应客户端以前将须要一直等待 Beta 服务的响应,直到 Beta 服务将数据存储到数据库中。

这种作法存在两个问题。首先,Alpha 服务如今要求 Beta 服务是启动状态而且能够响应请求 - 它的正常运行依赖于 Beta 服务。其次,Alpha 服务要等到 Beta 服务的响应才能响应客户端 - 它的性能也依赖于 Beta 服务!

在这两种状况下,Alpha 服务耦合于 Beta 服务。Beta 服务失败的频率是多少?它须要停机维护的频率是多少?它的峰值性能是多少?难道真的只有在数据被安全的存储以后才能够响应,或者说提早响应就是不可行的?若是它依赖于其余服务,相应的会须要进一步延伸耦合链?像这样的系统好坏程度取决于其最薄弱(weakest)的服务。

若是咱们使用 Kafaka 消息队列作为接口替换上面描述的,咱们将会获得一个彻底不一样的结果,Kafka 有一招:一个 Kafka 消息队列是持久化存储的。当数据已安全地放到队列中时,Alpha 服务就能够响应请求了;咱们能够确信数据将最终会存储到数据库中,由于 Beta 服务致力于处理队列中的消息。Alpha 服务如今仅仅依赖于 Kafka 的正常运行和性能了 - 这两个指标均可能远远好于系统中的其余微服务。它是如此松耦合于 Beta 服务,它甚至都不须要知道它的存在!

固然,消息队列历来就不是性能问题的灵丹妙药 - 它并不能神奇的改善系统的总体性能(举起手来,若是在你曾经参加的会议中有人相信的话)。然而,它确实容许你应对来自外部系统的可变负载,或者甚至是你本身系统中的微服务(例如那些必须作某种形式的批处理)。消息队列可以应对峰值负载,从而使下游服务能够平滑的速度处理。一个给定的服务的性能只须要大于系统的平均负载,而不是峰值负载。

使用 REST 接口时,你能够经过在每一个服务中存储数据来打破依赖链并实现相似的效果。然而,为了达到这一点,在每个服务中你都须要本身实现消息代理模块。使用现有的服务你本身没必要再设计、构建和维护一套(或多套)相似的系统。

4. 服务间松耦合

在设计系统的时候,咱们发现若是 Alpha 服务返回的结果是转换后的数据,一些客户可能会发现它会很方便。

若是使用 REST 接口这将变得很是容易 - Beta 服务能够返回转换后的数据,Alpha 服务直接透传给客户端。同时咱们在两个服务之间增长了一个新的依赖关系 - Alpha 服务如今依赖于 Beta 服务的转换功能。这和上面小节中 Alpha 服务依赖于 Beta 服务存储数据是相似的情景。一样的问题出现了:若是 Beta 服务不能及时而且正确执行其功能,Alpha 服务一样的也不能返回结果给其客户端。

和存储数据不一样的是 Kafka 针对这个问题没有提供有效的解决方案。事实上,用 Kafka 接口来达到这个目的将会更复杂(但毫不是不可能)。由于消息队列是单向通讯信道,从 Beta 服务获取转换后的数据须要相反方向的第二条信道。匹配这些异步响应结果和等待的客户端也会须要一些额外的工做。

然而,用消息队列不容易作到这个事实的给了咱们一个启发,那就是咱们新生的系统有些地方作的并很差,咱们应该从新评估。纵观咱们加诸于系统的额外功能与数据流的关系,结果代表无论它是如何实现的,是该功能引入了服务之间耦合。要想让 Alpha 返回转换后的数据则他必须依赖于真正作数据转换的服务,咱们架构的系统不可能绕过这个事实。

这让咱们停下来检查咱们是否确实须要这个功能。有没有其余的方法能够提供访问转换后的结果数据而且不会引入这个问题?客户端是否须要全部转换后的数据?若是这个功能是必要,代表咱们在切分微服务的时候并非最优的,咱们应该将数据转换这步放到 Alpha 服务中。若是不是必要的,咱们只是阻止了本身添加没必要要的或设计不当的功能,这些功能将来可能会影响咱们系统的发展和性能。系统的功能一旦添加上每每很难移除掉了。

为了不在系统中引入沉重的包袱,在实现以前辨别有疑问的功能(或者功能设计)是很是必要的。Kafka 消息队列的自身的局限性能够指导咱们设计出更好的系统。当你试图砍掉一个紧急的功能时,他们可能会感到沮丧,可是从长远来看是值得被称赞的。

5. 提升可用性和扩展性

随着负载的增长,咱们的系统应该保持高可用性,可扩展性。做为实现这一目标的一部分,咱们但愿可以运行多个 Beta 实例。若是其中一个实例宕机,其余的实例会(但愿)仍然可以正常运行。能够增长更多的实例来处理增长的负载。

使用 REST 接口时,咱们能够在 Beta 服务实例前面加一个负载均衡器,而且把 Alpha 服务从直接指向 Beta 服务的实例替换为指向这个负载均衡器。负载均衡器须要可以自动检测宕机的实例,从而一旦有实例宕机能够将负载转移到别的实例上。

一个 Kafka 消息队列默认支持可变数量的消费者(例如 Beta 服务),不须要额外的基础架构的支持。多个消费者能够组成一个 “消费组”,只须要在链接集群的时候指定一个相同的组名。Kafka 会将每一个主题全部分区的数据共享传输给整个组的消费者。只要咱们使用的主题有足够多的分区,咱们能够持续增长 Beta 服务的实例,这么它们将会分担一部分负载。若是某些实例不可用,他们的分区将由剩余的实例处理。

6. 增长更多服务

咱们须要在咱们的系统中添加一些新的功能(这并不重要),咱们将把它放在一个新的 Gamma 服务中。它依赖 Alpha 服务的数据的方式和 Beta 服务依赖 Alpha 服务的数据的方式是同样的,而且数据是用一样的接口提供的。数据必须通过 Gamma 服务处理才算被整个系统完整的处理。

若是使用 REST 接口,则 Alpha 服务将会耦合一个额外的服务,加重前面讨论的服务间耦合的问题。随着下游服务的增长,依赖会变得原来越多。

此外,一组数据可能成功的被一个下游的服务处理,可是在另一个服务中却处理失败。这种状况下,可能很难通知到客户端和作到自动恢复,特别是若是它们能够在状态不一致的状况下就离开。

使用 Kafka 接口容许新的 Gamma 服务和 Beta 服务同样简单地从同一消息队列中读取数据。只要它使用一个不一样于 Beta 服务的消费组,二者之间不会互相干扰。因为 Alpha 服务不须要知道它写入数据的消息队列有什么服务在使用,咱们能够持续的添加服务而不会对它形成任何影响。

数据被安全地存储在 Kafka 中,因此各个服务能够在瞬时故障的状况下重试,例如数据库死锁或者是网络问题。非瞬时错误,例如脏数据,和使用 REST 接口同样都会存在相似一些问题。检查数据合法性越早越好,例如在阿尔法服务,是减小这些问题的关键。

7. 总结

以上的例子都是直接取自于咱们在 Movio 推动微服务化过程当中的真实案例。在这个过程当中它已经证实了是一个很是宝贵的工具,催生了优秀的系统架构,并能够简单和快速的实现它。咱们将继续探索使用它的新方法,并指望咱们的使用会促进系统的进一步发展。LinkedIn 和 Apache 的团队创造了一件伟大的做品。

针对学习这件事,我想一想讲讲个人见解, 第一:绝对的三个字:靠本身,任何人都帮不了你。 第二:切勿眼高手低,好说懒作,动动嘴比谁都快,动手的时候就怂了 ,这不是一个技术人应该有的态度。 第三:别人给你的只会是思路与方法,实际内容还须要本身去填充, 没有坐等这回事,绝逼没有。 赞成的老铁请点赞、转发此文章支持!        分享一个技术交流学习,群号是:  558787436