微服务 —— 你需要付出什么?又能有何收获?

如果您阅读过我的文章 —— 微服务中的语义扩散,您可能会识得此标题。本文是那篇文章的一个延续,其目的是强调,只有当我们付出足够的努力来处理我们将要面对的组织和分布式计算问题时,才能拥有微服务并从中受益。在后续段落中,您会看到我们为真正的微服务付出了哪些东西,又从中得到了什么。在这篇文章中,你不会找到任何具体的解决方案,相反地您会看到,关于 “在使用微服务之前需要解决多少个不同而又复杂的问题” 的高级概述。请继续往下阅读!

多维度自治

微服务架构的主要优点之一是,每个微服务都是一个自治单元(Autonomous unit)。自治意味着什么?我们可以从多种角度进行分析。

部署

首先,自治服务可以随意进行部署,与其他服务无关。提供自治性的基本方式是明确区分业务能力,以便我们的有界上下文(Bounded context)不会泄漏到其他服务中,导致出现紧密耦合。如果微服务是自治的,它可以用高内聚性(Cohesion)来表征。此处内聚性是一个从物理学改编而来的术语,在这种情况下,高内聚性就意味着应用程序的所有部分(用源码表示)都非常紧密地连接在一起,并且需要花费大量的精力才能从中分离出一个东西。

自治也与技术有关。当然,在开发应用程序时,您应该明智地选择所使用的技术,以便使它们与公司的能力和策略保持一致。然而,您应该能够通过不同于其它服务的技术栈来创建应用程序。这意味着你不能将你的接口与具体的技术绑定起来 —— 你的 API 应该是技术无关的(Technology agnostic)。

数据管理

值得一提的是,微服务不仅仅是代码相关的 —— 它还是数据库。首先,每个微服务都需要管理它自己所使用的数据。其次,即使您完美地确定了有界上下文,但有某些服务使用了相同的数据库(模式,Schema),您的应用程序仍然是耦合的。您将无法独立部署它们,并且在数据库出错的情况下,所有相关程序都将不可用。

可扩展性

扩展我们的应用程序,这其中有两个主要原因。其一,是因为我们想要更大的服务负载。其二,则与我们在失败情况下提供的弹性(Resilience)相关。在整体式架构(Monolithic architecture)中,我们需要通过其所有组件来扩展整个系统。通常令人头痛的是,扩展整体架构的唯一方法是通过添加更多的 CPU 或内存进行垂直扩展。另一方面,有了微服务,我们就有了自治性,这使我们可以通过独立(甚至自动化)地添加新节点来横向扩展应用程序。

弹性

我已经在前面的段落中提到了弹性,但此处我需要补充的是,除了在单个服务的范围内,提供弹性自治使我们有机会去隔离特定服务中的问题和错误,而其他的不会受到影响,因此系统仍然可以工作。

企业文化

最后一项要点是,自治性也与企业文化有关。每个微服务都应该由一个敏捷型团队进行管理和开发,该团队在整个微服务的整个生命周期中承担全部责任。为了尽可能提供最佳的软件,团队需要有权决定团队中的人员应如何工作。

它不是免费的

权衡(Tradeoff) —— 它无处不在。微服务能给我们带来很多好处。只要商业与技术人员都作出一些让步,他们都可以享受到这些好处。接下来您会发现,为了使您的架构成为微服务架构,您需要付出些什么。

服务发现

在传统环境中,应用程序运行于物理硬件上,并且其位置相对静止,服务之间可以使用简单的,基于文件的带预定义 URL 的配置进行通信。然而,在基于云的微服务基础架构中,服务可以拥有多个具有动态分配资源的实例(特别是主机),而这仅仅依赖于静态配置是不够的。在这种情况下,服务应该使用其标识符(代码或名称)动态地发现彼此。这就是服务发现的意义所在。在这里,您有两种选择 —— 服务器端的服务发现,以及客户端的服务发现。在这两种情况下,每个服务实例会都将自己注册到服务注册表中。在前一种情况下,调用通过一个负载均衡器进行定向,该负载均衡器查询注册表并将请求转发到当前可用的目标实例。诸如此类的功能,在 Amazon 或 Kubernetes 这样的平台有提供。但在后一种情况下,每个应用程序都会自行查询注册表,并由该应用程序选择要调用的正确实例(客户端负载平衡)。Eureka 就是一个很好的例子。如果您没有使用任何我们提到的选项,那么您既没有可伸缩性,也没有自治性,因为添加一个新的依赖于服务的实例,或将其部署在其它的某些主机上,就意味着需要在与它通信的每个应用程序中进行配置更改。

可扩展性和负载平衡

横向扩展不仅关乎所增加的服务实例的数量。它也必须对服务使用者透明(当实例数发生变化时你不应做任何事情),所以你需要使用客户端或服务器端的负载均衡,使流量可以在没有任何额外手动操作的情况下进入新的实例工作。当我们使用 Amazon、 Kubernetes 等云平台时,我们可以根据配置的策略自动扩展我们的应用程序。

未雨绸缪的设计

我们无法避免故障。这并不总是与我们的代码有关,它有可能是网络或硬件的故障,也可能请求过多,导致我们的组件的 CPU 或内存达到了饱和。整体式应用中发生了失败通常就意味着完全不可用。在我所工作的弹性系统中,通过横向扩展提高了性能,但如果某些组件是错误的 —— 这种错误最终发生在了所有实例中,并且还不容易被隔离。总之,当失败发生时,预防并不像应对失败那么重要,这正是我们改变思维方式的原因,尤其是在微服务架构中,因为在这种架构中,可能出现失败的组件数量要多得多。

我们已经讨论过的话题应该已经让您对如何处理这些故障有了一些了解,现在让我们把这些处理方式系统化。无视各种原因,如果我们尝试与之通信的应用程序无法响应,我们都能对其进行扩展。然后,我们将能够提供更大的流量,并在发生故障时保持弹性。然而有时候,我们资源有限,也无法扩展我们的应用程序。下一步应作何处理?为了不减慢整个系统的速度,我们应该设置超时。随机备份(Random backoff)策略也是一个不错的选择,这样我们就不会用重试重载目标应用程序。我们还应该考虑通过诸如熔断机制(Circuit breaker)这样的机制来隔离故障,以防止故障级联到客户端。我的目的不是描述所有故障的可能性,而是想强调处理故障是多么的困难,尤其是当故障无法避免的时候。

几乎所有关于微服务的文章(特别是关于数据库的文章)都包含了关于 CAP(Consistency,Availability 以及 Partition tolerance) 定理的几句话。本文也同样。众所周知,软件开发是需要考虑权衡的。在我们的日常工作中,我们决定牺牲某些事物来获得另一些事物 —— 那些在给定的背景下对我们来说更有价值的事物。我们所面对的是一致性,可用性和分区容错。例如,如果由于某些网络故障,我们无法在数据库节点间执行数据复制(Data replication),这时就需要决定要牺牲什么 —— 可用性或一致性。如果尽管发生了故障,我们仍想要处理请求(保持可用性),我们就必须清楚,我们正工作在不一致(最终是一致的)的环境中,直到问题解决为止。如果我们无法接受任何不一致的情况,我们需要拒绝所有请求 —— 放弃可用性。CAP 定理的重要之处在于,我们系统中的某些部分可能是 AP 和一些 CP 组成的 —— 我们可以在组件级别上做出决策。

监控

当我们的系统由驻留在一个或几个节点上的单个应用程序组成时,您可以非常轻松地找到日志文件并在任何问题出现时查看这些文件。如果没有错误,但是用户的响应时间非常长,那么您可以(根据日志)对应用程序进行概要分析,并在一个地方查找瓶颈。在分布式环境中,事情变得非常复杂。首先,你有几十个应用程序,每个应用程序在不同的节点上运行,这些应用程序通常是动态分配的,要找到出错的位置是一项非常艰巨的任务。其次,如果不是在日志中可以找到的错误,而是关于响应性的问题,那么要发现问题点更加艰难。所以,对于微服务架构,我们必须接受并处理系统监控方面的复杂性。

现在,首先您要提供的是一个中央系统,它可以汇总所有应用程序实例中的日志,并使您能够在一个地方分析它们。ELK 栈(Elasticsearch,Logstash,Kibana)就是一个很好的例子。其次,您还应该提供一个跟踪解决方案,以便您可以找出实际上是哪个请求导致了问题的出现。在这一方面,有一个极佳的解决方案的案例,那就是 Spring Cloud Sleuth,它可以通过 Zipkin 轻松地进行增强,帮助您分析和可视化基础架构和延迟中的服务之间的依赖关系。使用这套工具,您可以轻易找到系统中造成瓶颈的部分。当我们谈论应用程序日志时,我们思考的是找出已经发生的错误的源头。在微服务架构中,实时监控主机、CPU、内存、网络延迟、响应时间等也是无价的。使用诸如 Grafana + Graphite 等这类工具,并正确配置您的应用程序,您就可以轻松查看所有的这些指标。设置适当的阈值,您可以触发警报并在出现真正的糟糕事件之前对其做出反应。

在微服务环境中,这似乎是可选的。有人可能会说:“我可以在每个实例中搜索日志,虽然这需要花费一些时间,但我可以处理好它”。如果你有 3 个应用程序,那这样做是没有问题的,但如果你有几十个这样的应用程序,那么你花在查找问题上的时间最终会抹杀你在微服务架构中获得的所有其他好处,因为它是完全不可维护的。我们需要承认,在系统监控的环境中微服务带来了很多复杂性,但这是我们真正需要提供的。

持续交付

微服务体系结构的另一个特点是,当您拥有小型独立应用程序时,您可以更快地提供更改,并且比起整体式的方法,它们对整个系统的影响要小得多。这意味着您应该做好相应准备,在开发这些功能时尽快部署它们。实现这一目标的第一步是使用所谓的持续集成(Continuous Integration),以便您在代码库中提供的每一项更改都将通过已有版本的完整性进行自动验证 —— 您的代码是否已编译?您的测试成功通过了吗?静态代码分析结果是否正面?这些,以及可能还有更多的条件,它们在你的构建管道(Build pipeline)中被检查。这是持续交付的基础。通过交付,我的意思是生产一些工件(Artiface),这是一个潜在的候选版本,它可以安全地部署到产品上。例如,它可能是一个 .jar 文件,或者更多平台特定的东西,比如 Docker 映像。其原因是在微服务架构中,我们需要快速响应变化,并做好在将代码推送到仓库后进行就部署的准备。

当然,有些时候将变更部署到产品中并不那么容易。这其中可能有一些包含手动处理的规则和流程,比如用户验收测试,或者只是点击某主管人员的 “部署” 按钮,但这不是它应该的样子,因为开发团队应该是公司的一部分并负责服务的整个生命周期。在开发人员,测试人员和负责人之间划分界限是不好的,但在交付方面,我们,即开发人员,应该做好准备。

总结

微服务已经火了好几年,这是有充分的原因的。涉及到组织文化、技术、部署、数据管理,以及可扩展性和弹性的自治性为技术和业务人员带来了很大的价值,但与此同时,它需要付出很多努力才能得以实现。服务发现、负载平衡、未雨绸缪的设计、监控,以及持续交付是我们需要的基础,而且这也并不便宜。在我们开始使用微服务之前,我们需要注意所有这些事情。我希望您在阅读本文之后,能够有信心地说出您的基础架构是否为您提供了完整的微服务自治性,或者您是否拥有其他分布式系统。请多注意这些话,并且在您日常工作中注重实用性。

本文的版权归 StoneDemo 所有,如需转载请联系作者。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏马哥教育

零基础到精通Linux,从这篇文章开始

正好在最近,看到了一篇不错的资料,其中对于Linux入门学习的描述极其详尽,因此特别摘抄其中段落,制作成思维导图分享给大家。

53110
来自专栏微服务生态

Martin Fowler关于微服务的原文翻译(一)

原文如下:http://martinfowler.com/articles/microservices.html

574
来自专栏企鹅号快讯

值得关注的5个Python开源项目

Python领域优秀的开源软件层出不穷。关注一个好的开源软件,对一个开发者来说可以带来很多益处。或者你可以很好地运用这个开源软件,成为它的一个用户;或者你可以阅...

3307
来自专栏Java呓语

架构·微服务架构(一)

微服务架构模式作为替代单体应用和面向服务架构的一个可行的选择,在业内迅速取得进展。由于这个架构模式仍然在不断的发展中,在业界存在很多困惑——这种模式关乎什么?它...

1402
来自专栏数据和云

杨廷琨Oracle Code大会分享:如何编写高效SQL(含PPT)

2018 Oracle Code 于5月17日在新加坡拉开帷幕。作为全球开发者交流分享的年度盛会,为吸引所有领域的开发者,Oracle今年将自1996年开始的J...

782
来自专栏java思维导图

什么是REST架构?

REST架构风格是全新的针对Web应用的开发风格,是当今世界最成功的互联网超媒体分布式系统架构,它使得人们真正理解了Http协议本来面貌。随着 REST架构成为...

772
来自专栏重庆的技术分享区

2018年微服务的5个发展趋势

原文地址:https://medium.com/memory-leak/5-microservices-trends-to-watch-in-2018-aed1...

1422
来自专栏喵了个咪的博客空间

phalapi-入门篇1(简单介绍以及环境搭建)

#phalapi-入门篇1(简单介绍以及环境搭建)# ? ##前言## 先在这里感谢phalapi框架创始人@dogstar,为我们提供了这样一个优秀的开源框架...

3387
来自专栏智能计算时代

2017年终奉献:微服务最佳实践

关键需求 最大限度地提高团队的自主性:创建一个团队可以完成更多工作而不必与其他团队协调的环境。 优化开发速度:硬件便宜,人不是。 使团队能够轻松快捷地构建强大的...

2945
来自专栏即时通讯技术

微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

对于IM系统来说,如何做到IM聊天消息离线差异拉取(差异拉取是为了节省流量)、消息多端同步、消息顺序保证等,是典型的IM技术难点。

772

扫码关注云+社区