前言
当万维网首次出现时,集成不同类型的操作系统是一项主要的挑战。HTTP 的出现使得不同的操作系统之间可以通过超文本使用统一的协议进行通信。
当微服务的架构出现后,系统的集成带来的挑战没有太大的分别:多种实现技术在物理网络上彼此分离,需要相互通信。从最终用户的角度来看,微服务的集成在系统的无缝体验方面起着至关重要的作用。正确集成的系统还有助于实现分布式系统的优势:它们可以在 service 级实现扩容提高效率,并且能够在满足业务需求的同时降低架构成本。
另一方面,错误集成的系统可能会彻底破坏微服务架构的好处:它可能会导致数据丢失和继承问题。这些问题通常很难追踪,同时用户会受到不良的影响。
无缝集成有许多因素需要考虑。我们需要针对不同的场景选择最合适的集成方式。接下来,我们会介绍不同集成方式的优缺点。
数据库集成
在这种模式下,两个或多个服务都读写自同一个中心数据库。所有的服务连接到同一个中心数据库上。我们可以用一个银行系统来解释这种架构。
数据库集成的显著优点之一是简单。事务的管理和别的模式相比更加直观。这可能是使用最广泛的一种模式,同时也可能是最被滥用的。
这个模式下服务耦合度较高,使得微服务较难以变更和扩展。定义数据的所属权以及更新 schema 会很麻烦,因为需要重新编译和部署所有相关的服务。它导致大爆炸形式的集成。这种类型的集成可能在维护微服务的自治性方面存在问题。
在大规模应用的场景下,唯一的选择是在数据库中投入更多硬件,即使这样,也很难避免数据库和行级争用中的死锁。
想情况下,我们不建议将此模式用于服务间通信。它可以用于分阶段微服务部署的早期阶段。换句话说:如果你使用它,很快就会抛弃它。
同步 API 调用
在这种集成模式下,服务之间通过 API 同步的通信。所有的数据访问都是通过 API 以请求响应的方式实现,调用方等待 API 的返回数据做进一步处理。在上面的例子中,如果事务服务需要读取用户信息,可以通过调用用户服务的 API 来实现。
这种方式在直接数据库的调用之上进行了良好的抽象,并在调用技术选择方面提供了极好的灵活性。它隐藏了实现的细节:使得我们可以在不影响客户的前提下自由的变更。比如,用户模块可能使用的是 Java 和 MySQL,而事务模块可能使用的是 SQL Server 和.NET,但是它们彼此之间还是可以通过 API 进行通信。
但是,这个和直接的数据库集成模式没有本质的区别。在数据库调用之上添加另一个网络调用也会 j 降低可扩展性:增加了负载,降低了性能。这种集成模式也使事务管理变得困难并且影响了服务自治,因为服务依赖于彼此的正常运行。在微服务中,如果必须在系统边界之外同步读取数据,那就是 SOA 架构的感觉了。
在某些场景下,这种集成模式是最佳的甚至是不可避免的。API 集成模式中安全令牌是一个主要的应用场景,因为这些 token 生命周期短,而且在使用之前才生成。如果可能的话,应谨慎使用同步 API 调用。如果使用这种模式,它们应该版本化并且应该与熔断机制一起使用。
ETL (Extract, Transform, and Load)
ETL 需要按预定义的时间表通过后台进程同步数据。这些数据可以是推模式或者拉模式。它是异步的,这意味着服务可以在不等待“回调”的情况下执行。
这种集成模式也很好地隐藏了实现细节。它提供了合理的解耦,因为服务不依赖于彼此的正常运行。实时用户不会受到正常运行时间或处理时间的影响。
ETL 过程需要改变源和目标数据库。通过 ETL 集成,数据一致性取决于计划和持续时间。弄清楚变化的可能会使应用变得太复杂。在这种场景下,团队不得不导出全部的数据。这使得流程运行时间非常长,严重影响了它们的实用性。
报告性的服务非常适用于这种集成。这些 ETL 的过程通常会有些耗时,只有在系统中可接受旧数据时才应使用它们。
消息
在这种模式下,服务彼此之间通过指令和事件交换信息它们通过消息中介如 RabbitMQ,MSMQ 等。在上面的例子中,事务服务建立了一个账号余额变更的时间并且放到消息中间件中。奖励服务,积分服务和消息服务分别订阅这个事件,并且进行处理。这是发布订阅模式,还有很多别的消息模式。
只要正确的实现,消息提供了非常好的解耦。它提供了非常高的灵活性,只要能够正确的通信。将数据推送给订阅者使得数据发送的部分非常容易完成。而且发送方完全不会受订阅端处理细节的影响。
不正确的服务或事务边界可能使消息传递实现复杂化。而且,这种模式提供的松耦合影响一致性为。它需要高度的规范化才能正确实现消息传递,而没有经验的团队可能会很挣扎。
两种类型的消息
典型的消息传递解决方案建立在传输属性的基础之上。在较高的层面上,这些传输可以分为两类:消息队列和消息流。
排队解决方案涉及处理实时数据。一旦消息被成功消费,则这条消息会从队列中出队。只要处理能够跟上,队列就不会累积,也不会占用太多空间。但是,在扩展订户的情况下,无法保证消息的顺序性。
在流式传输解决方案中,消息按顺序存储在流中。它发生在消息传输本身。订户在流上的位置保留在传输上。它可以根据需要在流上倒置。这对于故障和新的订阅方案非常有利。但是,这取决于流的长度。从存储角度来看,它需要更多配置,因此需要对流进行归档。
这里的典型用例假设系统能够处理有些陈旧的数据。如果情况并非如此,我们需要更多地分析业务领域并理解原因。在我们的示例中,当事务更新发生时,信用分数更新不一定需要实时发生。消息在这个场景下非常适合。我们可以相应地设置用户的期望,并允许服务管理自己的负载。
总结
每一种微服务架构都不同,没有绝对完美的方案。当我们使用它们时,我们需要结合故障情景,驱动这些集成模式的组合。比如,Netflix 使用消息传递来移动数据,如果消息不可用或数据仍在传输中,它们会回退同步 API。在每种情况下,理想的是实现最灵活和可扩展的微服务架构,但是必须首先考虑实施细节和自己的能力。
这里的关键是在需要扩展和自治时使用异步模式。要实现这一目标,您需要可靠的服务边界和明确的数据所有权。否则,您最终会遇到复杂且不可持续的集成方案。对业务流程建模很重要。这将让您知道哪些用例和进程本质上是异步的并且适合于消息传递。