最近重读了Sam Newman的《微服务设计》(Building Microservices),这是我第三次读这本书了。

第一次读是在2016年,那时候微服务刚火起来,马丁·福勒的那篇微服务文章被到处转发,各大技术会议都在讲微服务,好像不做微服务就落伍了。我读完这本书之后热血沸腾,觉得微服务就是银弹,什么问题都能解决,单体架构的所有痛点,微服务都能搞定。那时候的我,看山是山,觉得微服务就是好,就是先进,就是未来。

第二次读是在2017年,那时候我们团队刚开始做微服务拆分,从一个大的单体应用拆成几个服务。读完之后有了一些实践的感受,知道了微服务不是那么简单,有很多坑要踩,很多问题要解决。但还是很多地方一知半解,书里讲的很多原则和方法,知道是对的,但为什么要这么做,不这么做会有什么后果,体会还不深。那时候的我,看山不是山,开始怀疑微服务是不是真的那么好。

这次是第三次读,我们团队已经做了两年多的微服务,从最初的几个服务,到现在的几十个服务,踩了很多坑,也积累了一些经验,有成功的地方,也有失败的教训。再读这本书,有了完全不同的理解,很多以前觉得抽象、遥远的原则和方法,现在都有了切身的体会,很多以前不理解的地方,现在都豁然开朗了。那时候的我,看山还是山,但已经不是最初的那个山了。

今天来聊聊重读之后的一些新感悟,关于微服务,关于架构设计,关于技术成长。这不是一篇读书笔记,而是一个有了几年微服务实践经验的工程师,重读经典之后的思考和感悟,希望能给正在做微服务或者准备做微服务的朋友一些参考。

一、微服务不是银弹,而是一种权衡

第一次读这本书的时候,我觉得微服务就是银弹,能解决所有问题。单体架构难维护、难扩展、难部署、团队协作困难,微服务都能解决。那时候的我,把微服务神化了,觉得只要用了微服务,一切问题就迎刃而解了。

但实践了两年多,踩了很多坑之后,我才明白,微服务不是银弹,而是一种权衡。微服务解决了单体架构的一些问题,但也引入了很多新的问题,这些问题甚至比单体架构的问题更难解决。

比如,微服务拆分之后,服务之间的调用变成了网络调用,网络延迟、网络故障、序列化反序列化的开销,这些都是单体架构没有的问题。再比如,分布式事务,单体架构里一个数据库事务就能搞定的事情,微服务里要跨多个服务、多个数据库,分布式事务的复杂度和难度,比单体事务高了好几个量级。再比如,服务治理,服务发现、负载均衡、熔断降级、限流、链路追踪、监控告警,这些在单体架构里不需要考虑或者很简单的事情,在微服务架构里都变成了必须解决的复杂问题。

所以,微服务不是没有代价的,它的代价是系统复杂度的大幅提升,是运维成本的大幅增加,是对团队技术能力的更高要求。微服务带来的好处,比如独立部署、独立扩展、技术栈灵活、团队自治,这些都是真实的,但这些好处是有代价的,你需要付出复杂度和成本的代价来换取这些好处。

Sam Newman在书里也反复强调,微服务不是适合所有场景的,你要根据自己的业务场景、团队规模、技术能力来决定要不要做微服务,不要为了微服务而微服务。很多团队,业务不复杂,团队规模不大,单体架构完全够用,这时候强行上微服务,只会增加复杂度,降低效率,得不偿失。

重读之后,我对这一点的理解更深了。微服务不是目的,而是手段,是为了解决特定问题的手段。如果你的问题不需要微服务来解决,那就不要用微服务。架构设计的核心,不是用了多么先进的技术,而是用最合适的方案解决了实际的问题。

二、服务拆分的粒度,是艺术不是科学

第一次读这本书的时候,我对服务拆分的理解很简单,就是按照业务领域拆分,一个领域一个服务,觉得拆分得越细越好,越细越"微服务"。那时候的我,觉得微服务就是要小,一个服务最好就做一件事,越小越纯粹。

但实践之后才发现,服务拆分的粒度,是艺术不是科学,没有标准答案,不是越细越好,也不是越粗越好,而是要根据业务场景、团队规模、变更频率等多种因素来权衡。

拆分太粗,就失去了微服务的意义,跟单体差不多,独立部署、独立扩展的好处都享受不到。拆分太细,服务数量太多,服务之间的调用关系太复杂,运维成本和治理难度急剧上升,一个简单的业务流程可能要调用十几个服务,出了问题排查起来非常困难,性能也会因为网络调用的叠加而下降。

我们团队就踩过这个坑。一开始拆分的时候,追求"微",把一个业务领域拆成了五六个服务,结果服务之间的调用关系非常复杂,一个用户下单的流程,要调用用户服务、商品服务、库存服务、订单服务、支付服务、物流服务、通知服务,七八个服务串在一起,出了问题要查半天,而且因为网络调用太多,性能也上不去。后来我们做了服务合并,把一些关系紧密、变更频率一致的服务合并成一个服务,服务数量减少了,系统反而更稳定、更好维护了。

Sam Newman在书里讲服务拆分的时候,强调了"高内聚、低耦合"的原则,强调了按照业务领域和限界上下文来拆分,强调了服务应该可以独立变更、独立部署。这些原则我第一次读的时候就知道,但只有实践之后,才真正理解了这些原则的含义和重要性。

服务拆分的关键,不是大小,而是"高内聚、低耦合"。一个服务内部的功能应该是紧密相关的,变更的时候应该是一起变更的;服务之间的依赖应该是松散的,一个服务的变更不应该影响其他服务。如果两个服务总是一起变更、一起部署,那它们可能就应该合并成一个服务。如果一个服务内部的不同部分变更频率差异很大,那可能就应该拆开。

而且,服务拆分不是一次性的,不是一开始就设计好的,而是随着业务的发展和对业务理解的深入,不断调整和演化的。一开始拆分得不合理没关系,后面可以调整,可以合并,可以再拆分,架构是演化出来的,不是设计出来的。

重读之后,我对服务拆分的理解,从"越细越好"变成了"合适就好",从追求"微"变成了追求"高内聚、低耦合"。这是实践给我的教训,也是重读这本书给我的印证。

三、数据管理,是微服务最难的部分

第一次读这本书的时候,我对微服务的数据管理部分没有太在意,觉得不就是每个服务有自己的数据库嘛,很简单。但实践之后才发现,数据管理,是微服务最难的部分,没有之一。

微服务要求每个服务有自己独立的数据库,服务之间不能直接访问对方的数据库,只能通过API来交互。这个原则说起来简单,但做起来非常难,因为很多业务数据是跨服务的,很多查询需要关联多个服务的数据,很多事务需要跨多个服务保证一致性。

首先是查询的问题。单体架构里,一个SQL join就能搞定的多表关联查询,在微服务里变成了跨服务的调用,你需要调用A服务拿数据,再调用B服务拿数据,然后在内存里做关联,不仅性能差,而且实现复杂。如果数据量大,还会有分页、排序、过滤的问题,非常麻烦。

我们团队为了解决这个问题,尝试过很多方案。一开始是服务之间互相调用,在内存里做关联,后来发现性能太差,就引入了CQRS模式,写操作走服务接口,读操作走专门的查询服务,把需要关联的数据同步到一个专门的读库里,直接SQL查询。这个方案解决了查询性能的问题,但也增加了系统的复杂度,需要维护数据同步,需要处理数据一致性的问题。

然后是事务的问题。单体架构里,一个数据库事务就能保证ACID,简单可靠。微服务里,一个业务流程可能涉及多个服务、多个数据库,分布式事务的保证非常困难。2PC(两阶段提交)性能差,可用性低,不适合高并发场景;TCC(Try-Confirm-Cancel)实现复杂,对业务侵入大;Saga模式适合长事务,但需要处理补偿和回滚的问题,也很复杂。

我们团队在分布式事务上踩了很多坑,一开始用2PC,发现性能太差,经常超时,后来改成了最终一致性,用消息队列来保证数据的最终一致。但最终一致性也有很多问题,比如消息重复消费、消息丢失、消费失败重试、幂等性处理、对账补偿,这些都需要仔细设计和处理,稍有不慎就会出现数据不一致的问题。

Sam Newman在书里用了整整一章来讲数据管理,讲了每个服务的独立数据库、讲了跨服务查询的方案、讲了分布式事务的各种模式、讲了数据一致性的问题。第一次读的时候,我觉得这部分太理论了,离我很远。实践之后再读,才发现每一句话都是经验之谈,每一个方案都是踩过坑之后的总结,这一章是整本书里最有价值的部分之一。

重读之后,我深刻地认识到,微服务的难点,不是服务拆分,不是API设计,不是服务发现和负载均衡,这些都有成熟的框架和方案可以用。真正的难点是数据管理,是跨服务的查询和事务,是数据一致性的保证。这些问题没有银弹,没有完美的解决方案,只有根据业务场景做权衡,在一致性、性能、复杂度之间找到平衡点。

四、运维和监控,是微服务的生命线

第一次读这本书的时候,我对运维和监控的部分也没有太在意,觉得那是运维的事情,开发只要把代码写好就行了。但实践之后才发现,运维和监控,是微服务的生命线,没有好的运维和监控,微服务就是一场灾难。

单体架构的时候,就一个应用,一台机器或者几台机器,出了问题登录上去看日志,很容易定位。微服务就不一样了,几十个服务,几百台机器,一个请求可能经过十几个服务,出了问题,你根本不知道是哪个服务出了问题,根本不知道该去哪台机器上看日志。没有好的监控和链路追踪,排查问题就像大海捞针。

我们团队就经历过这样的噩梦。有一次,线上出现了一个偶发的报错,概率很低,但影响用户体验。我们排查了整整三天,因为这个请求要经过七八个服务,每个服务都有可能出问题,我们只能一个服务一个服务地看日志,加日志,重新发布,然后等问题复现。整整三天,团队几个人轮流熬夜排查,最后才发现是某个服务的一个超时设置不合理,在高并发下偶发超时。这个问题如果有好的链路追踪和监控,可能几个小时就能定位,但我们花了三天。

从那以后,我们团队就把监控和链路追踪当成了头等大事来做。我们引入了SkyWalking做分布式链路追踪,引入了Prometheus+Grafana做指标监控,引入了ELK做日志聚合,引入了告警系统,服务出了问题第一时间告警。现在,出了问题,我们能很快定位到是哪个服务、哪个接口、哪台机器出了问题,排查效率提升了很多。

Sam Newman在书里也强调了监控的重要性,他说微服务架构下,监控不是可选的,而是必须的,你需要有集中的日志、指标监控、链路追踪、告警,否则你根本无法管理这么多服务。第一次读的时候,我对这些没有概念,实践之后再读,才发现这些都是血泪教训,是必须要做的事情,不是可选项。

而且,微服务对运维的要求也高了很多。单体架构的时候,部署就是把一个包扔到服务器上,很简单。微服务的时候,几十个服务,每个服务可能有多个实例,部署、升级、回滚、扩容、缩容,这些操作如果靠人工来做,根本忙不过来,而且很容易出错。所以,微服务必须要有自动化的部署和运维,CI/CD、容器化、编排系统,这些都是必须的。

我们团队一开始也是手动部署,每次发版都要忙大半天,还经常出问题。后来我们引入了Docker容器化,用Jenkins做CI/CD,用Kubernetes做容器编排,实现了自动化的构建、部署、扩容、回滚,发版变成了一件很简单的事情,点一下按钮就搞定了,效率提升了很多,故障率也下降了很多。

重读之后,我深刻地认识到,微服务不只是架构的变化,更是开发模式和运维模式的变化。没有对应的运维能力和监控体系,不要轻易上微服务,否则你会被复杂度淹没。微服务的成功,三分靠架构,七分靠运维,这句话一点都不夸张。

五、团队和组织,比技术更重要

第一次读这本书的时候,我关注的都是技术问题,服务怎么拆分、API怎么设计、数据怎么管理、监控怎么做,觉得微服务就是一个技术问题,技术搞定了,微服务就成功了。

但实践之后才发现,微服务不只是一个技术问题,更是一个团队和组织的问题,团队和组织,比技术更重要。技术问题都有解决方案,但团队和组织的问题,往往是微服务失败的根本原因。

马丁·福勒在讲微服务的时候,提到了"康威定律",也就是系统的架构会反映出组织的沟通结构。你有什么样的团队结构,就会有什么样的系统架构。如果你的团队是按技术职能划分的,前端团队、后端团队、数据库团队、运维团队,那你的系统很可能就是按技术层划分的,而不是按业务领域划分的。如果你的团队是按业务领域划分的,每个团队负责一个业务领域的全栈开发,那你的系统就很容易按业务领域拆分成微服务。

我们团队就有过这样的教训。一开始做微服务的时候,我们的团队还是按技术职能划分的,前端组、后端组、测试组、运维组,服务拆分是按业务领域拆的,但开发的时候,还是前端做前端的,后端做后端的,一个服务要多个组协作,沟通成本很高,交付效率很低,而且出了问题互相推诿,都觉得是对方的问题。后来我们调整了团队结构,按业务领域划分了几个全栈团队,每个团队负责一个业务领域的所有事情,从前端到后端到测试到运维,团队自治,沟通成本大幅下降,交付效率大幅提升,微服务的好处才真正体现出来。

Sam Newman在书里也提到了团队和组织的问题,他说微服务的一个重要好处是团队自治,每个团队负责一个或几个服务,独立开发、独立部署、独立运维,这样可以减少团队之间的沟通成本,提升交付效率。但要实现团队自治,就需要调整组织架构,按业务领域来划分团队,而不是按技术职能来划分。

而且,微服务对团队的技术能力要求也更高了。单体架构的时候,团队只要懂自己那一层的技术就行了,前端懂前端,后端懂后端,运维懂运维。微服务的时候,每个团队都是全栈团队,需要懂前端、后端、数据库、运维、监控,技术栈更广,对团队成员的能力要求更高。如果团队的技术能力跟不上,微服务只会带来混乱和灾难。

还有,微服务需要团队有更强的自动化能力和工程文化。代码规范、单元测试、集成测试、代码审查、CI/CD、文档,这些工程实践在微服务架构下更加重要,因为服务多了,依赖复杂了,如果没有好的工程实践,代码质量会快速下降,系统会变得难以维护。

重读之后,我深刻地认识到,微服务的成功,技术只是基础,团队和组织才是关键。很多团队微服务失败,不是技术不行,而是团队和组织没有跟上,还是用老的团队结构和工作方式来做微服务,结果肯定是失败的。在做微服务之前,先想想你的团队和组织准备好了没有,这比技术选型更重要。

六、写在最后

重读《微服务设计》,最大的感受是:经典之所以是经典,是因为它经得起时间的检验,也经得起实践的检验。第一次读的时候,你看到的是知识;第二次读的时候,你看到的是方法;第三次读的时候,你看到的是智慧。每一次读,都会有不同的理解和收获,因为你自己在成长,你的实践在积累,你对问题的理解在加深。

微服务这些年经历了从热捧到质疑的过程,一开始大家都觉得微服务是银弹,什么都能解决;后来踩了很多坑,又开始质疑微服务,觉得微服务是坑,不如单体;现在大家慢慢理性了,知道微服务不是银弹,也不是洪水猛兽,它只是一种架构风格,有它的适用场景,也有它的代价和挑战。用对了,能带来很大的好处;用错了,会带来很大的灾难。

对我个人来说,这几年做微服务的经历,是一次非常宝贵的成长。从一开始的盲目崇拜,到后来的踩坑质疑,再到现在的理性看待,我对架构、对技术、对软件工程的理解都深入了很多。而《微服务设计》这本书,就像一位老师,在我不同的阶段,给我不同的指引,让我在实践中不断印证、不断反思、不断成长。

最后,想对正在做微服务或者准备做微服务的朋友说几句话:

第一,不要为了微服务而微服务,先想清楚你要解决什么问题,微服务是不是最合适的方案,不要盲目跟风。

第二,微服务是一种权衡,它带来好处的同时,也带来了复杂度和成本,要做好心理准备,要有对应的技术能力和运维能力。

第三,服务拆分没有标准答案,要根据业务场景和团队情况来权衡,高内聚、低耦合是核心原则,架构是演化出来的,不是设计出来的。

第四,数据管理是微服务最难的部分,要提前想好跨服务查询和分布式事务的方案,不要等出了问题再补救。

第五,运维和监控是微服务的生命线,没有好的监控和自动化运维,不要轻易上微服务。

第六,团队和组织比技术更重要,做微服务之前,先调整好团队结构和工作方式,让团队能真正自治。

希望大家都能在微服务的道路上少踩坑,多收获,用合适的架构,解决实际的问题,做出优秀的系统。也希望大家有空的时候,能重读经典,每一次重读,都会有新的收获。