Teaser Image

mindwind

十日画一水,五日画一石




摘要:
从性能响应延迟的角度解读微服务带来的影响,并提出了几个保证服务低延迟的建议。


概述

微服务是目前的一个热门词汇。它是原创的还是基于最佳实践产生的?虽然在实施微服务的过程中有一些缺点,但这些缺点能被克服么?

组件可测试性和稳定性

一旦你组装好了一个大系统,那么几乎不可能监测到系统的最大延迟来自哪里。你可以监测到平均的延迟和吞吐量,但为了达到稳定的延迟,需要去分析系统的关键部分。这正是一个由简单组件构成并且可以独立运行和测试的系统可以帮到你的地方,它能帮你实现系统端到端的延迟稳定性。

UNIX 哲学

微服务中的许多关键概念已经在分布式系统中使用多年了。

它与Unix哲学有很多共同之处。

引用Mike Gancarz总结的这些原则,如下所示:

  • 小即是美。
  • 一个程序只做好一件事。
  • 尽可能早地创建原型
  • 可移植性比效率更重要。
  • 数据应该保存为文本文件。
  • 尽可能地榨取软件的全部价值。
  • 使用shell脚本来提高效率和可移植性。
  • 避免使用可定制性低下的用户界面。
  • 所有程序都是数据的过滤器。

微服务即是把UNIX哲学应用到分布式系统。

微服务架构哲学与UNIX哲学中“一次做好一件事”本质上是相同的。它描述如下:

  • 服务小且细粒度的,完成单一功能。
  • 企业文化应拥抱部署和测试自动化。这减轻了管理与运维负担。
  • 文化和设计原则应接受失败和错误,类似于高容错性系统。
  • 每个服务都是有弹性的,易恢复的,可组合的,最小化且完备的。

使用微服务架构有一些缺点

  • 服务带来信息屏障。
  • 该架构引入了额外的复杂性和需要处理的新问题,如网络延迟、消息格式、负载均衡和容错,忽略其中任何一点都属于对“分布式计算的误解”。
  • 测试和部署变得更复杂了。
  • 单体应用的复杂性仅仅转移到了服务的网状分布中,但是依然存在。
  • 过于细粒度的微服务已经被批评是反模式。

我们能得到单体应用和微服务都有的最佳特性吗?还是只能二选一?难道我们不该使用最适合我们问题的方法么?微服务的关键方面之一是控制应用的部署。在哪些情况下,我们应该把一个组件部署为一个单体应用或微服务,这样做最有意义。

对于超微服务的建议替代方案包括:

  • 将功能打包为一个库而非服务。
  • 和其他功能组合产生一个更有实质意义和有用的服务。
  • 重构系统,将功能放在其他服务里或者重新设计系统。

我们怎样才能两全其美?让组件可组合

如果你的组件是可组合的,那么其大小就总是合适的。你可以按需把它们组合成一个服务集,或者全部组成一个服务。

这点对于测试和调试特别重要。你需要知道一组组件在隔离基础设施的情况下如何协同工作。为方便单元测试,你可能想要让所有组件运行在一个线程里并且可以直接相互调用。这样的测试不会比测试单体应用组件更复杂,你可以单步调试你的代码,从一个组件到另一个组件看看到底发生了什么。

一旦你的组件在没有基础设施的情况下协作正常,接下来需要测试它们如何和基础设施协同。

让你的基础设施符合应用对性能的需求

低延迟交易系统是一种分布式系统,并且它们也有非常严格的延迟需求。大多数交易系统被设计为高度延迟敏感的,它对速度的要求远超你所能直接看见的。在Java领域,一个交易系统要求99%甚或99.9%的耗时低于100微秒的情况并不少见。这可以使用像Java这样的高级语言在商用硬件上实现。

实现低延迟的关键是:

  • 用于消息和日志的低延迟基础设施。理想情况下,对于短消息大约1微秒。
  • 最小网络跳数。
  • 一个高水平的真实生产负载再现能力,这样你就能研究99%(最差的1%)或99.9%(最差的0.1%)的延迟情况。
  • 将每个CPU核心视为运行一个特定的任务或服务,并使用它自己的缓存数据和代码。焦点在于应用在CPU核心间的分布(而非计算机之间)。

你的二级高速缓存一致性总线是高性能服务之间的消息通道。

你可以在两个不同的CPU核上对同样的数据执行CAS操作,这样一个线程可以很快感知到由其他线程设置的值,其往返时间在Sandy Bridge的处理器上不超过50纳秒,而在新一代型号上会更短。

Java领域中低延迟的基础设施的例子

  • Aeron一个可靠的UDP传输组件。
  • Chronicle Queue一个用于消息和日志的持久化队列。

这些传输组件在处理负载均衡和故障转移方面有各有不同的优势。

使消息格式可配置

对于消息格式有许多互相矛盾的考量。你想要:

  • 对人友好易读,以便你可以验证消息不止表现正常,而且行为符合你的期望。我常常感到惊讶能从导出的存储文件和消息日志中发现了如此多的问题。
  • 对机器友好的高效二进制格式。
  • schema改变的灵活性。灵活性意味着增加冗余,所以软件可以应对未来的字段新增、删除和数据类型变化。如果你不需要这些,这种冗余就是浪费。

理想情况下,你可以在测试或部署时选择最佳选项。

一些能让你写时改变格式并适合你需求的序列化库的例子有:

  • Jackson Speaming API支持JSON、XML、CSV、CBOR(一种二进制格式)。
  • Chronicle Wire支持将对象序列化为YAML,还有许多不同形式的二进制YAML、JSON、CSV、原始数据。

我发现YAML相比JSON更好用,它的语法设计更简洁而且易读。不像JSON是另一门语言的子集,它对数据类型、注释、二进制内容和消息分隔符的支持很自然。

总结

我觉得有很多关于如何使用微服务的好想法,而很多围绕它们的批评是针对该如何去实施落地的,然而我相信这些问题都是可以解决的。


原文:Micro-services for performance
作者:Peter Lawrey
日期:2016/03/22


写点文字,画点画儿,「瞬息之间」一切都变了。觉得不错,可长按或扫描二维码关注。