《微服务设计(Building Microservices)》是 Sam Newman 所著的一本动物书。这本书介绍了作者在 ThoughtWorks 的工作过程中一些对于微服务的认识和实践,还不错,就读了读。微服务大家肯定都不陌生了,在 2015 年的时候,我曾经在实验室的一个移动应用项目中尝试过微服务的架构,但是并不是太成功。那时并没有太多书籍和最佳实践可以参考。在读了这本书后,发现确实很多问题是所有第一次试水微服务架构的开发者都会遇到的。
由来
先说微服务的由来,微服务概念是 Peter Rodgers 博士在 2006 年提出的,而真正火起来,是在 2014 年左右。国内在 2015 年左右,就可以在各种业界的开发者会议上听到微服务的名字。
任何技术或者应用模式,乃至架构方式大行其道的背后,一定是现在的一些基础设施或大环境的变化日积月累造成的。Docker 之所以能够在 2013 年左右火起来,是因为 Linux Containers 等等内核 feature 的出现。微服务的火爆,在我看来,最大的原因就是 Docker 的出现。
Docker 的轻量容器,使得微服务有了很好的载体,两者简直是一个是阆苑仙葩,一个是美玉无瑕。除此之外,RESTful 的 API 构造方式和网速的提高也使得微服务默认以 HTTP 请求来进行交互,而不是效率更高但难以管理的 RPC。而应用逻辑的日益复杂,使得各个公司和开源组织有动力为服务实现需要的底层基础设施,比如服务发现和 API 网关。各种因素综合起来,导致了现在微服务概念的火爆。
实践
服务交互
服务之间的交互是微服务实现中非常关键的一环。交互的方式有同步与异步,以及混合三种。同步交互的代表是 RPC 和 RESTful API,是基于请求/相应的模式。而异步的代表是基于事务的模式。如果是同步的方式,编程相对而言会简单一些,代码的可读性也会高很多,很多逻辑是天然适合同步机制的。但是与此同时,整个系统的请求响应时间会延长,因为有些时间花费在了等待同步返回上。而且同步的方式要求有一个服务要管理请求的生命周期,要驱动和指导所有的服务来完成请求,就像乐队的指挥一样。这样会使得服务网络中的有些节点成为了中心节点,他们的可用性对整个系统影响特别大,也就是书中所谓的上帝节点。
而异步的交互方式,会降低系统的请求响应时间,但是也会引入额外的编程和管理上的成本。异步会使得业务逻辑变得分散,异常处理也会是一场灾难。因此在进行微服务的选型时,建议是如果同步的方式可以满足要求,那同步优先。如果需要更好的请求响应时间,再考虑使用异步的方式。
在一般现实的系统中,往往都是同步与异步混合存在的态势,真正需要长时间处理的任务,往往通过异步的方式,而相对简单的任务,同步是更好的选择。
数据库
数据库在微服务的落地过程中,是大魔王级别的存在。如何能够根据服务重新组织数据库,是非常困难的。这本书从很多方面讨论了这个问题。
在我第一次进行微服务的尝试时,采取了通过数据库来进行服务之间的集成。也就是所有的服务都访问同一个数据库,所有的服务拥有对所有表的访问权限。但是这样的方式就破坏了服务的高内聚低耦合。这是非常不可取的,它使得你的微服务本质上只是不同的服务进程,做不同的事情而已,没有实现低耦合高内聚,在管理上反而更加麻烦。不过这样的做法有一个好处,就是从 monolithic 应用迁移过来的时候数据库可能不需要多大的改动就可以直接使用。
尽管如此,所有服务使用同一个数据库的方式并不是最好的选择。比较好的方式是每个服务只访问自己的数据库,因此在从 monolithic 应用迁移到微服务的过程中,难免会涉及到对数据库的重构。在这个过程中比较好的实践是,先进行数据库的分割,再进行服务的分割。因为数据库的分割是风险更大的,先完成风险大的任务会降低在微服务化过程中潜在的风险。
在数据库分割的过程中,需要注意的问题主要有两点,一点是分割的边界,一点是一致性。分割的边界是指应该以什么尺度来分割数据库,这是跟服务的分割息息相关的,往往是按照服务的维度来进行。需要注意的是,在分割的过程中,往往会使得很多数据库的外键没办法继续使用,这也是一种取舍。
除此之外,一致性问题也是微服务中一个非常常见的问题,这是在分布式系统领域一个亘古不变的话题。如果一个事务,需要涉及到多个数据库实例中表的操作,那如何能够保证这个事务的事务性(ACID),是一个值得考虑的问题。数据库只能保证在单个数据库中事务的事务性,但如果是跨数据库的操作,问题就会复杂很多。一般来说业界比较常用的方法是使用两阶段或者三阶段提交来解决这个问题。也就是有一个中心节点,来协调不同数据库的工作,如果所有的操作都完成了,事务成功,否则会进行重试,或者 abort,来保证 all or nothing。
但是两阶段或者三阶段提交会为系统引入很大的复杂度,而且一致性的错误变得更加难以定位。这时候可以考虑能不能牺牲严格的一致性,用一些简单的方式来实现数据的最终一致性,比如把失败的请求放入一个队列,后台进行处理等等方式。
持续集成与部署
这个部分应该是 ThoughtWorks 擅长的领域,但是我本身对其并不是太感兴趣。因为我觉得相比于迁移,这部分是非常简单的工作。在 2016 年的时候我也协助 caicloud 实现了其 CI 工具 Cyclone。这一类的工具需要考虑的其实并不多,因此觉得并没有很大的花样可以玩,因此略过不谈。
测试
微服务场景下的测试,跟寻常的测试并无二致。常规的单元测试和端到端测试是不可少的。但因为引入了服务的抽象,因此服务级别的测试也是必不可少的。服务级别的测试需要 mock 其他的服务,因此有额外的复杂度。
监控
之前写过一篇博客:Google SRE 阅读笔记(1)-监控,是在读了 Google SRE 之后写的读后感。而在这本书中,从不同的角度写了对监控的看法。这本书主要讲了微服务场景下的一些监控挑战。
首先,手动监控肯定是不行的。随着规模的上升,手动监控查看服务运行状态的方式无疑是自寻死路。所以必然要引入工具来监控。书中比较有意思的一点是,建议暴露出服务内部的一些监控数据。举个例子,如果是订单服务,可以暴露每分钟处理的订单量,这样有利于定位问题。
安全
安全问题,首当其冲的是身份验证和授权问题。为了实现单点登录,粗粒度的身份验证可以放在 API 网关来实现,但并不建议细粒度的授权也放在其中,尽管这也是可以实现的。
在服务之间调用的安全保证上,可以选择使用同一个 API 网关来鉴权。除此之外,HTTPS,SMAL OPENID,客户端证书(TLS),HMAC on HTTP(JWT)等等都是可以选择,要看对安全的要求以及实施的难易程度而定。
结
差不多书中核心的部分就是这些,后面还有很多内容,但是作为低级玩家,这些已经足够了。如果你想从零入门,那可能需要先看看 Eric Evans 的《领域驱动模型设计》,再看这本书,会有更好的感觉。
License
- This article is licensed under CC BY-NC-SA 3.0.
- Please contact me for commercial use.