注:本文是我最近阅读《微服务架构设计模式》的一点感悟。本书的阅读笔记我不会去写详细的记录,而是结合自己的一些微服务架构实践做一些总结和回顾。从单体应用到微服务,任何新的架构模式或方法的出现,必然是由于传统架构模式遇到的问题。对于单体应用,常说的问题主要有以下几点。单体应用规模过大,导致复杂度急剧增加,单体应用开发难以开发和管理,交付和变更周期变长,敏捷跟不上性能和单个应用程序本身的可扩展性。软件架构中的问题在设计中,分而治之一直是解决复杂性的关键思维方法。包括在传统的软件架构设计中,对于大的系统,会进行子系统或组件的划分、界面设计、集成设计等。该架构的核心思想仍然是:分解+集成,但传统架构在分解时并没有做到高度的独立自主和对单个组件的完全解构。这种高度的独立性和自主性,实际上要求组件或模块在整个生命周期中,从开发、设计、测试、上线运行,到后期运维,都具有高度的独立性;同时,还需要合作的开发团队和独立的组织架构设计。也正是因为这个原因,微服务架构得到了进一步的发展。也就是说,微服务架构其实是对传统的基于组件的架构设计思想的进一步增强,增强的核心是独立性和解耦。微服务架构思想的要点之一就是单体应用的拆分。说到微服务,就一定要说到扩容立方体。对于传统的单体应用,一般来说,扩展的方式只能是通过实例的扩展实现X轴的横向扩展。另外两个重要的扩展是功能拆分和数据库拆分。功能拆分其实并不新鲜。在早期的组件架构设计中,我们将不同的应用组件部署到不同的AppServer。对于应用层,只需要考虑会话保存、全局配置和分发等问题的解决方案就足够了,不涉及复杂的数据持久化问题。真正困难的部分是拆分数据库。因为分库引入了两大问题,一是分布式事务问题,二是大量跨库操作带来的分布式事务问题。为什么要拆分数据库?单体应用发展到一定阶段后,出现的性能问题往往很难在DB层面解决。数据库集群难以实现横向弹性扩展。即使是OracleRAC这样的商用集群软件,也存在性能扩展瓶颈。性能扩大到2~3倍后,就不能再扩大了。所以,纯粹从扩展实例的扩展出发,后面衍生出了读写分离集群。读写分离集群在读操作占比较大的业务场景下,其实是最好的扩容方式。这样,数据库本身并没有拆分成多个实例,前面遇到的两大问题就不介绍了。只要在数据库上搭建一个DaaS数据库访问中间件,数据库底层的拆分和变更对上层应用的开发是完全透明的。说到这里,复习一下微服务的一些核心点就会更加清晰。微服务是传统单体的拆分,需要高度独立自主。微服务的拆分不仅仅是功能的拆分,更重要的是数据库的拆分。异步和解耦是微服务架构设计的重要指导思想。微服务协作微服务实践需要开发组织、持续集成、测试和交付方法来支持场景和问题驱动。前面讲微服务的时候也提到过,微服务架构本身就会导致开发、集成、运维的复杂度都增加。即便是常说的高可用,也只是高可用和弹性扩展能力,而降低了高可靠性。那么企业或团队实践微服务的原因是什么?只是微服务是一种架构发展趋势,是技术热点,各大互联网公司都在用吗?其实,对于很多使用微服务的企业,我们可以看到,团队中总有一些技术狂热分子热衷于新架构、新技术,但并不关心这些技术解决的业务场景是什么问题。这直接导致大量技术在不恰当的时候被应用。并不是说微服务不好,而是架构师要综合考虑业务、技术、团队资源、成本等多个维度,什么时候用什么技术最合适。一个类似阿里的电商平台,每天有上亿的访问量,接近百万级的TPS,高峰时每秒有几十万笔交易。在这种业务场景下,每个模块拆分成一个独立的中心,独立的数据库是必然的。不是说一定要用先进的技术,而是一定要实现微服务来满足和支持业务。所以传统企业在实践微服务的时候,一定要思考微服务能解决什么问题。如果你还没有想清楚这个问题,就不要轻率的去搞微服务。二是可以通过其他传统方法解决,不要轻易将微服务架构的实践理想化。例如,业务系统在运行过程中,数据库出现了性能瓶颈。这时可以考虑将历史数据迁移到备库,增加备库的单据查询能力;在数据库上构建二级索引缓存库,解决性能问题。再比如一些业务系统存在瓶颈。不是常规OLTP场景下的问题,而是同时支持OLTP和OLAP能力的单一业务系统数据库。这时候你应该做的是去掉大量的查询统计和数据分析功能,而不是拆分整个业务系统的数据。这方面的例子还有很多。其实我想说明的是,很多业务系统的复杂度和性能问题,还远远没有到需要进行功能拆分和数据库拆分来解决的地步。微服务拆分策略说到微服务拆分,首先要说说传统软件架构设计中的组件拆分。对于组件拆分,可以看出常规的组件拆分其实是在应用层,没有走到具体的数据库。这就导致组件拆分后,底层数据库仍然是一个中心点,是一个紧耦合点。包括组件的拆分,在实际的架构规划中,希望通过接口调用来协调组件,而在后续的开发实现中往往会出现偏差,也就是常说的还是通过接口来解决问题底层数据库的关联SQL查询。所以可以看到组件之间没有接口调用,很多耦合都转移到了底层数据库的耦合上。说到微服务阶段的拆分,其实就变成了两件重要的事情,一是数据库的拆分,二是基于数据库拆分的功能拆分。对于拆分,我之前写过两篇文章,可以参考:领域模型的再思考和上下文边界分析来划分微服务。谈DaaS数据库即服务和微服务下的数据库拆分,是不是传统的结构化分析。该方法仍然类似于DDD领域驱动建模中的子域划分和上下文边界识别。拆分的重点还是在数据库拆分上,但是在DDD方法中理解为核心领域对象的拆分,领域对象拆分的时候自然对应的数据库和功能也随之拆分。先梳理边界,拆分服务,再梳理定义基于业务协同和业务功能实现的API接口。也就是说,微服务的拆分其实包括三个方面。即数据库拆分、功能拆分、API接口识别和定义。对于微服务拆分,真正的难点在于微服务拆分的粒度。当我重新阅读这本书时,我再次庆幸我们没有按照书中理想的方法来实践微服务。微服务的拆分这么细,不然还真是一种思路。因此,这里需要重申一个重点。微服务的拆分不应该按照标准理论的理想粒度,而是应该根据实际业务场景、扩展性需求、开发团队管理需求拆分到合适的粒度。比如你现在做的是外卖订餐系统。如果是和美团APP类似的规模,细粒度的微服务拆分是必然的。但是如果你的系统只是面向社区、园区等私有云部署场景。那么这个系统就不需要在业务场景和并发度上去拆分大的微服务了。在实际架构中,前后端分离,独立缓存库,读写分离基本可以解决问题。因此,微服务拆分的程度与业务场景和业务系统面临的问题有关,而不是使用标准的类似拆分方法策略进行拆分。类似于面向对象的分析与设计,DDD领域驱动和领域对象分析与识别,这些方法都没有问题。这些方法都可以应用到应用程序内部,应用程序也需要拆分成组件。并不是说组件化之后,每个组件都要拆分成独立的、高度自治的微服务。从API接口到异步解耦对于微服务架构中的API接口设计,我写过一篇文章来描述。这里还是想重新说明一下API接口设计和暴露的两个重点。了解HttpRestAPI标准规范,需要知道RestAPI更多的是一种面向资源的API接口设计方式。早期我也坚持一个观点,就是要装上理想化的Rest界面设计方法来设计界面。但目前我更倾向于只使用HttpAPI作为接口实现方式,是否是Rest风格不是重点。即在接口实现上,所有接口都可以实现POST接口方法,而不必区分哪些必须使用GET方法,哪些必须使用PUT方法。虽然POST方式的实现会给部分接口的在线访问和测试带来一些不便,但是可以通过其他方式解决。前后端分离,暴露大量API接口API接口本身应该是粗粒度的,但实际上在很多项目中,往往会把数据库表的所有CRUD方法暴露为API接口方法。一方面,这是领域对象层的贫血。另一方面,前后端分离导致原来数据访问层内部的所有API接口都需要通过HttpAPI接口暴露出来。内部IT系统本身仅供内部LAN使用。仅提供PC端BS浏览界面,对APP端无支持需求。在这种情况下,就没有必要分离前后端了。前后端分离带来了后端API接口暴露、前后端整合协调等诸多问题。在这种场景下,如果把IT系统分成五个微服务模块,那么这五个微服务模块之间的API接口设计和集成才是我们真正需要关注的。在目前的微服务架构设计中,微服务之间的接口协作和微服务本身的前后端接口协作都混合在一起,实际上导致了后续微服务中API管理和治理的完全失控系统。在讲基于Saga分布式事务的分布式事务时,我们讲的更多的是事务补偿或者事务最终一致性保证,但是很多长期事务仍然是通过API接口的同步调用来实现的。也就是说,在同步调用API接口时,微服务并没有完全解耦。解耦一定是基于事件或者消息方式的异步调用方式。Saga是异步消息事件机制下的分布式事务解决方案。Saga由一系列本地事务组成。每个本地事务更新数据库后,会发布消息或事件触发Saga中下一个本地事务的执行。如果一个本地事务因为某些业务规则不能满足而失败,Saga会对该失败事务之前所有成功提交的事务进行补偿操作。Saga的实现方式有很多种,最流行的两种方式是:基于事件的方式。这种方式没有中心协调点,整个花样就像舞蹈一样,各个舞者按照事先编好的动作和动作,各自表演,最后形成舞蹈。基于命令的方法。这种方法的工作形式就像一个乐队,有一个指挥(协调中心)来协调大家的工作。协调中心告诉Saga的参与者应该执行哪个本地事务。当我再次想到分布式事务时,再次印证了我的观点,那就是微服务的拆分太细了,导致引入了很多完全没有必要的分布式事务问题,增加了开发、集成的成本,测试和后续监控以及运行和维护。工作量和复杂性。比如书中提到的一个例子,对于一个在线点餐系统来说提交订单,这本身就是一个很容易实现的功能,但是微服务之后,订单提交涉及到用户验证、账号验证、厨房工单生成多个独立的API服务接口调用。这就涉及到这些API接口的事件编排。同时,在编排中,还需要考虑异常时的补偿和回滚,还要考虑消息执行的顺序等诸多问题。这些极大地增加了功能实现的复杂性。当你自己开发的系统没有海量并发和高性能需求时,我真的很难找到任何理由将微服务拆分成如此精细的细节并引入如此多的复杂性。其实上面的分布式事务场景应该更多的应用于大型系统之间的分布式事务协作。例如,采购系统提交订单时,还涉及工作流平台和预算系统中工作流实例的启动。对于预算核查,此时需要实现分布式事务。上面的分布式事务处理和协作场景是跨系统出现的,这个量是完全可控的。但是,您的微服务划分得太细了。你的应用程序中的任何功能都会引入上面的分布式事务问题、基于事件的编排和补偿问题。可以想象,这不是简单的复杂性问题,而是后期的系统可靠性和可操作性问题。如何彻底解耦?前面说了,如果CUD操作可以异步解耦,查询操作怎么处理。如果一个query搜索本身还涉及到多个微服务API接口的组合,此时如何实现解耦。一般来说,查询操作是一个同步操作。用户点击查询后,会等待数据的返回。这时候,如果需要调用多个微服务API接口数据进行组合,那么某个底层微服务模块出现异常,将直接导致整个查询功能持续异常。也就是说,如果有查询操作,微服务之间还是紧耦合的。我们希望通过消息和异步实现微服务完全解耦的目的没有达到。当然,其实可以考虑两种方法来解决这个问题。一种是缓存常用数据,查询时直接访问缓存库。二是将微服务底层数据库所需的共享数据同步到一个专门提供查询服务能力的大型共享ODS读库。在一些项目的CQRS命令查询职责分离的实践中,基本采用了这种思路。这样就实现了微服务之间的完全解耦。当然还引入了另外两个依赖,一个是消息中间件,一个是缓存库或者分布式ODS读取库。这两点的稳定性直接影响到整个系统的稳定性。使用CQRS模式最大的问题之一就是命令和查询两部分无法做到强一致性保证,也就是很有可能你界面上查询到的数据不是最新的数据在持久数据库中,它本身是与消息管道异步编写的。输入的实时性也相关。其次,在使用CQRS模式时,一个重要的假设是事件和命令发送后,事件接收者必须能够在没有特殊情况的情况下成功接收到事件并处理成功,否则会出现大量的异步回写异常错误信息。增加系统的复杂性。实施CQRS引入的整体复杂性对于普通的小项目来说是无法承受的。同时,CQRS框架的实现并不是简单的设计模式和开发复杂度的问题,更重要的是能否接受最终的一致性要求。同时,在这种需求下,将传统同步请求下的业务功能和逻辑处理机制转变为异步事件取值下的事件链驱动模式。要实现这种转化,需要能够拆分独立自主的命令和事件,同时保证这些事件在发送到后端业务功能和逻辑模块(即,该做的检查一定要提前做好)。微服务网关和API网关关于API网关,也可以先参考我头条发表的相关文章。API网关和微服务网关实际实现的核心功能基本相同,但需要注意的是,微服务网关一般是微服务架构体系中的内容。API网关一般是一个可以独立于微服务架构体系的内容。也就是说,API网关与微服务整体框架体系的耦合度更加松散。API网关一般具有独立的服务注册接入、负载均衡和路由能力,而微服务网关一般通过与服务注册中心集成来实现服务注册发现。这里再次强调负载均衡和路由应用范围的区别,微服务网关和API网关其实在应用范围上有明显的区别。也就是说,微服务网关一般应用在单个微服务应用内部,尤其是在前后端分离的情况下。对于API网关,应用于组织层面,实现多个微服务应用之间的集成协作能力。对于单个微服务应用,根本不需要使用API??网关,直接使用微服务网关。比如当你使用SpringCLoud框架系统时,你可以直接在框架中使用Zuul或者SpringCLoudGateway。但是当你在组织层面集成多个微服务应用时,你需要一个独立于单个微服务框架系统的外部集成中间件,因此它不能与微服务框架绑定得太紧。其次,你会看到跨应用之间的集成管控粒度不是细化到微服务组件,而是细化到各个API接口。所以此时开启API网关是很有必要的。当您在组织级别启用API网关时,会发现一个新问题。也就是说,对于整个组织层面来说,遗留系统和单体应用的微服务本身都有一个流程。在这个过程中,传统架构和微服务架构必须共存。新的RestAPI接口与旧的SOAP接口和消息协议共存。但是API网关本身并不能很好的适配旧的接口协议。虽然对于类似的ESB总线来说比较重,但是完全兼容并且涵盖了API网关的能力。所以,在这种情况下,API网关就显得鸡肋了。API网关是否应该做一些类似于服务编排的事情?简单地说,API网关不应该这样做。传统的ESB总线往往具备这种能力,而API网关不适合承载这种能力。如果做服务编排,一是让API网关本身从一个纯技术中间件变成一个承载业务逻辑的中间件,二是这个能力本身会让API网关变得更重。所以,最好的方式还是用一个独立的微服务来实现服务组合和编排,并发布组合领域服务或复合服务能力,我在上一篇文章中有专门提到过。API网关本身的中心化问题,不能简单的认为中心化的架构就一定不好。集中式架构本身可以通过集中控制和拦截轻松实现API接口安全、日志记录、路由、流量控制等。控制能力。在谈到微服务管理和治理时,中心化的API网关是一个重要的实现思路和手段。当然前面我也提到了,整体的发展趋势应该是ServiceMesh,实现完全去中心化的架构。这个ServiceMesh本身可以和DevOps、容器云等云原生技术形成一个完整的整体。但仍需注意组织管控力度和标准化推进力度。如果很多技术标准在组织层面的推进不一致,在应用系统改造前后有渐进演化,那么你这个时候是很难简单实现ServiceMesh架构的。简单来说:对于传统IT架构的渐进式微服务,ServiceMesh本身并不适用。说起微服务架构,人们常说完全去中心化,却不明白为什么要去中心化。即使使用异步消息集成,消息中间件仍然是中心点。当组织层面的标准和技术要求不统一时,在管控能力达不到的情况下,很难做到完全去中心化。这时候,集中的方式就是一个不错的选择。最后简单总结如下:按照理想化的微服务方式设计和实现微服务,不适用于大多数传统的企业信息化,也不适用于照搬互联网的微服务架构方式。企业IT架构在微服务转型过程中,应该更多地以业务场景和问题为驱动,借鉴微服务拆分解耦的思想,充分考虑敏捷交付、弹性扩展和性能、开发难度和资源成本投入,以及后来管理和控制治理的许多方面都取得了平衡。来源:https://www.toutiao.com/i6925193987997696516/
