工作中使用了微服务架构,接下来的一段时间里,我会写一系列的文章来介绍微服务架构,同时我也会在github上写一个microservices的应用框架(地址会在后续文章给出)。

这篇文章是对 

http://microservices.io/patterns/monolithic.html 和 http://microservices.io/patterns/microservices.html 的翻译, 内容是比较单一应用架构与微服务架构。

一、单一应用架构

1.上下文

你正在开发一个服务器端的企业级应用,它必须支持多种不同的客户端,包括桌面浏览器、手机浏览器、原生手机APP。这个应用也许还会暴露一个API,给第三方程序去调用。它也许还会与其他应用集成,通过web services或者一个消息代理。 这个应用通过运行业务逻辑、访问数据库、与别的系统交换消息、然后返回一个HTML/JSON/XML响应的方式来处理HTTP或者消息请求。对于这个应用的不同功能块, 有不同的逻辑组件与之对应。

2.问题与强制条件

问题: 这个应用的部署架构是怎样的?

强制条件:

这个项目有一个开发者团队

新的团队成员必须快速变得高效工作

这个项目必须易于理解和修改

你想要对这个项目实施持续部署

你必须在多台机器上运行多个这个项目的复制品,从而达到期望的可伸缩性、可用性

你想要利用新技术(框架、编程语言等)的优势

3.解决方案与例子

解决方案: 

构建一个单一应用架构的应用,比如一个单一的Java War文件, 或者一个单一的Rails或者NodeJS代码的目录结构

例子:

假设你正在构建一个电子商务应用, 这个应用从客户那里收到订单,检验存货和客户的余额,然后发货。这个应用由多个组件构成,包括:前端UI,实现了用户接口; 以及一些后台服务,用来检查余额,维护存货和发货。

这个应用以一个单一应用架构部署, 比如是在Tomcat上运行的一个Java web应用。你可以在一个load balance后面运行多个应用实例,以达到提高可用性和可伸缩性的目的:

应用单体架构de缺陷 单一应用架构_解决方案

4. 这种解决方案的结果

这种解决方案有很多好处:

  • 易于开发:现在很多开发工具和IDE的目标都是支持开发单一应用结构的应用
  • 易于部署:你只要在合适的运行时环境上简单的部署一个WAR文件(或者目录结构)
  • 易于伸缩:你可以在一个load balance后面运行多个应用实例,以达到提高可用性和可伸缩性的目的

但是,一旦这个应用变得庞大,团队规模变大,这种解决方案的缺点变得越来越重大:

  • 巨大的单一应用结构的代码吓坏了开发者,尤其是团队里的新人。这个应用变得难以理解和维护。结果是,开发工作变得缓慢,而且,因为没有严格的模块界限,模块化常常会被打破。此外,因为很难去理解如何正确地实现一次变动,代码质量下降。
  • IDE负载严重:代码越多,IDE越慢,开发效率越低。
  • web容器负载严重: 项目越大,启动时间越长。这对于开发效率有很大影响,因为有很多时间都浪费在了等待web容器启动上面。这也影响了部署。
  • 持续部署很困难:一个庞大的单一应用架构的应用是频繁部署的障碍。为了更新一个组件,你需要重新部署一整个应用。这会中断后台的任务,比如java里的Quartz job,这可能会导致问题。还有一种可能是,未更新的组件会在启动时失败,导致的结果是,重新部署的风险升高,不利于频繁更新。 这对UI开发者来说尤其是个问题,因为他们经常需要快速迭代和频繁的重新部署。
  • 伸缩这个项目变得困难:一个单一应用架构的应用只能在一个维度上伸缩。一反面,成交量上升时,可以通过运行更多实例的方法来伸缩,一些云服务甚至可以按需调节实例个数。但是在另一方面,这种架构难以根据数据量来调节。每个应用实例都会访问数据,使得缓存变得不那么有效,提高了内存消耗和I/O传输。同时,不同的组件有不同的资源需求,一个组件可能是CPU密集型,而另一个可能是内存密集型。单一应用架构下,我们很难对每个组件分别进行伸缩。
  • 对于调节开发规模是个障碍: 一旦一个单一应用架构的应用达到了一个具体的规模,把团队分别几个更小的团队,专注与不同的功能块上是有效的。比如说,我们也许想要UI团队、审计团队、仓储团队等。但是单一应用架构阻碍了各个团队之间的独立工作。团队之间必须共同合作。
  • 需要对一个技术栈有一个长期的承诺: 一个单一应用架构逼迫你与一开始的技术栈捆绑(甚至与某一个技术的具体版本)。在单一应用架构下,很难增量应用新的技术。比如说,你选择了JVM技术,以后那些用非JVM语言写的组件在你的项目里就没有一席之地了。再比如,你选择了一个平台框架,后来这个框架过时了,那就很难去迁移到一个新的更好的框架了。很可能你为了采用新的框架,需要重写一整个应用,而这是很有风险的。

二、微服务架构

1.上下文

与上文单一应用架构相同

2.问题与强制条件

与上文单一应用架构相同

3.解决方案与例子

解决方案:

定义一个架构,使得这个应用的结构是一系列松耦合、互相合作的服务(services)。这个方法对应了 伸缩立方体的Y轴。每个服务实现了一系列互相关联的功能。比如说,一个应用可能由订单管理服务、客户管理服务等构成。

服务之间用同步协议比如HTTP/REST或者异步协议比如AMQP(译者注: 实现如RabbitMQ等)来进行通信。 服务可以被独立的开发和部署。每个服务有 自己独立的数据库,从而与别的服务解耦。服务之间的数据一致性通过使用 事件驱动架构来维护。

例子:一个虚构的电子商务应用

假设你正在构建一个电子商务应用, 这个应用从客户那里收到订单,检验存货和客户的余额,然后发货。这个应用由多个组件构成,包括:前端UI,实现了用户接口; 以及一些后台服务,用来检查余额,维护存货和发货。这个应用由一系列服务构成:

应用单体架构de缺陷 单一应用架构_UI_02

4. 这种解决方案的结果

优点

这个解决方案有很多优点:

  • 每个微服务相对都比较小
  • 易于开发者理解
  • IDE更快
  • 应用启动更快,利于开发和部署
  • 每个服务可以独立于其他服务单独部署,利于频繁部署新版本
  • 更容易伸缩开发资源。使你可以把开发资源分成多个团队。每个团队负责一个或多个服务。
  • 提高了错误的隔离。比如说,在一个服务中产生了内容泄露,只有那一个服务会受影响。
  • 不会对一个技术栈产生长期的依赖。在开发一个新服务或者重写服务时,可以选择新的技术栈。

缺点

  • 开发者需要处理创建一个分布式系统时的额外的复杂度
  • 测试更复杂
  • 开发者必须实现服务间的通信机制
  • 如果不使用分布式事务,会很难实现跨多个服务的用例
  • 实现跨多个服务的用例需要团队间的更认真的合作
  • 部署复杂性。在生产环境下,部署或者管理一个微服务系统有运维方面的复杂性。
  • 更高的内存消耗。微服务架构需要N*M个实例,而单一架构只需要N个实例。如果每个服务在它自己的JVM上运行, 就需要M倍的JVM运行时。

5.问题

你有很多问题需要解决。

什么时候使用微服务架构?

使用这种方法的一个挑战是判断什么时候用它是有意义的。当开发一个应用的第一个版本时,你经常不会碰到这个方法所解决的问题。此外,使用一个精细的分布式的架构会拖慢开发进度。这对于一些初创公司来说可能是一个大问题,因为他们的挑战经常是如何快速的把业务模型实现到应用中。使用Y轴划分(译者注:这里应该指的是采用微服务架构)也许会使得快速迭代更难。但是过一段时间,当挑战变成了如何伸缩,以及你需要拆分功能块,这种紧密的依赖也许会使得拆分一个单一应用架构到一系列服务变得困难。

如何把应用拆解成多个服务?

另一个挑战是决定如何分离系统,成为一系列服务。这更像是一种艺术,但是也有很多策略可以帮到你:

  • 根据业务能力分离, 然后定义一个个服务
  • 根据子域来拆解
  • 根据用例/动词来拆解。比如说Shipping Service负责订单的输送。
  • 根据资源/名词来拆解。比如说,Account Service负责管理用户的账号。

理想情况下,每个服务应该只有一小部分的职责。(Uncle) Bob Martin 谈论过使用单一职责原则(SRP)来设计类,使用SRP来设计Service同样也是讲得通的。

另一个帮助设计Service的类比是Unix工具包的设计。Unix提供了很多工具,比如说grep, cat和find。每个工具只做一件事,然后可以和其他工具混合在一起,去做复杂的事情。

如何保持数据一致性?

为了保证松耦合,每个service有它自己的数据库。维护service之间的数据一致性是一个挑战,因为二阶段提交事务/分布式事务并不是很多应用的一个选项。相反的,一个应用必须使用事件驱动架构。一个服务在它的数据变化时,会发布一个事件。别的服务消费这个事件,然后更新自己的数据。有很多种可靠的数据更新和事件发布的方法,包括事件源和事务日志跟踪。

如何实现查询?

另一个挑战是实现需要从多个服务那里获取数据的查询。一个普遍的做法是使用命令查询职责分离,维护一个或多个view,它们通过订阅事件流的方式一直保持最新,事件流中的事件是别的service在数据变化时发布的。

6.相关的模式

有很多模式与微服务架构相关。这些模式解决了你在采用微服务架构时会碰到的问题。

  • 拆分方面的模式
  • 根据业务能力拆分
  • 根据子域拆分
  • 数据方面的: 每个service一个数据库模式 
  • API Gateway模式 定义了微服务结构中客户端如何访问service
  • 客户端发现模式和服务端发现模式用于把客户端请求路由到对应的service实例
  • 消息模式和远程方法调用模式是服务间通信的两种方法
  • 每台主机一个Service模式和每台主机多个Service模式是两种不同的部署策略
  • 横切关注模式: 微服务底架模式和外部配置模式
  • 测试模式: 服务组件测试 和 服务集成测试
  • 断路器
  • Access Token
  • 对整个系统的监控方面:
  • 日志聚合
  • Application metrics 应用的指标(CPU, Memory这些东西)
  • 审计日志
  • Distributed tracing 分布式追踪
  • 异常跟踪
  • 健康检查API
  • UI 方面的模式:
  •  服务端页面片段组成
  • 客户端UI组成

(PS: 再加上从http://microservices.io/ 上拉下来的一段, 这段就不翻译了:

How to apply the patterns

Core patterns

  • Monolithic architecture
  • Microservice architecture

Decomposition

  • Decompose by business capabilitynew
  • Decompose by subdomainnew

Deployment patterns

  • Multiple service instances per host
  • Service instance per host
  • Service instance per VM
  • Service instance per Container
  • Serverless deployment
  • Service deployment platformnew

Cross cutting concerns

  • Microservice chassis
  • Externalized configurationnew

Communication style

  • Remote Procedure Invocation
  • Messaging
  • Domain-specific protocol

External API

  • API gateway
  • Backend for front-end

Service discovery

  • Client-side discovery
  • Server-side discovery
  • Service registry
  • Self registration
  • 3rd party registration

Reliability

  • Circuit Breaker

Data management

  • Database per Service
  • Shared database
  • Event-driven architecture
  • Event sourcing
  • Transaction log tailing
  • Database triggers
  • Application events
  • CQRS

Security

  • Access Tokennew

Testing

  • Service Component Testnew
  • Service Integration Contract Testnew

Observability

  • Log aggregationnew
  • Application metricsnew
  • Audit loggingnew
  • Distributed tracingnew
  • Exception trackingnew
  • Health check APInew

UI patterns

  • Server-side page fragment compositionnew
  • Client-side UI compositionnew