本篇文章一共分为三个部分,分别是微服务架构的演进过程、具体实践微服务的应用技术和领域驱动设计的意识转变。微服务架构已经渗透到互联网应用的方方面面,而领域驱动设计也逐渐被业界所接收。
微服务架构几乎都是从 ALL IN ONE 的单体架构演进而来,中间又经历了分布式架构、面向服务架构的演进过程。
单体架构往往以烟筒式方式发展,往往存在两个主要问题:中心化和耦合度高。所谓中心化,就是数据集中存储在单个数据库中,业务系统集中部署在单台服务器上,通过集群部署方式提供服务能力,然而中心化的问题,也就是单点问题。而耦合度高,主要是指其中一个功能模块升级,其它的模块都得一起升级。这里要说明下,模块依赖度高不是单体架构的错,是因为本来架构可能就没有设计好,但是,在实际场景中,随着快速迭代开发,研发换了一波又一波,产品走了一茬又一茬,难免系统架构腐化严重。
看到了单体架构的诸多问题,系统开始通过按功能或模块进行拆分,拆分成多个独立的子系统,系统间通过 RPC、MQ 方式调用,由此逐渐演变为分布式的服务架构。分布式服务架构主要通过服务化和层次化进行解耦拆分,《架构整洁之道》书中提到一点,系统可以降解为策略和层次,而架构设计就是把相同的策略分到同一个组件中,反之,分属于不同的组件。所以,架构设计可以通过分层设计,由高层次服务调用低层次服务,低层次服务通过接口向上提供服务,以实现系统之间的解耦。也正是在这一阶段,单体架构一下子被拆分成几个或几十个系统。
但是随着架构的发展,问题又接踵而来,在没有做好边界的划分之前,系统拆分的服务往往是松散的。正如《架构整洁之道》所讲:软件架构设计本身就是一门划分边界的艺术。所以,很多系统在这一阶段,又往往进行了服务内聚和去层次化的演变过程。所谓服务内聚,就是以领域驱动设计为原则,重新界定领域边界,对模块进行服务整合,对系统进行合并。而去层次化,则是去除服务层次高低之分,按服务调用最优链路提供服务请求,降低深度,以此保证系统的稳定性能。
系统如何从 0 到 1,从 1 到 2,从 2 到 100,在具体实践微服务的过程并不容易。首先,考虑的第一个问题是:微服务是什么?其实,微服务是一个动作:拆。说到拆,就涉及到两个问题,第一,怎么拆?第二,拆到什么程度。
第一个问题,关于怎么拆,需要关注两个层面,一是发版速度,如商品、交易、促销等不同领域的迭代速度和开发速度是不一致的,所以,应该拆分到不同的领域,否则,就可能耦合在一起,A 上线 B 必须跟着一起上,这可能就应该合并到一起。另一个是能源协同,每个系统后面对应不同的研发团队,一个项目往往需要几个团队分工协作,那么系统拆分必然导致团队协作的成本,所以拆分系统同样也是拆分团队,要考虑协作沟通所带来的成本。
第二个问题,拆到什么程度。其实,已经有很多人对微服务进行了定义,其中我认为最重要的两点:一是微服务或一组微服务,应该是一套独立部署运行的服务,二是微服务所依赖的数据资源应该是彼此间相互独立隔离部署的。
其实,拆只是实践微服务的开始,要搭建整体的微服务框架还要进行四部曲:拆、服务化、高可用、隔离。
1,服务拆分
微服务讲究拆分,那么多微才是微,以及,怎么才是比较合理的拆分。在架构设计领域,有一个大家都在讲的著名定律:康为定律,可以理解为:一个系统架构的组织关系是和团队的组织关系相匹配的。如交易系统会对应一个交易系统的研发团队,商品系统会对应一个商品系统的研发团队,这种职责明确的组织结构会使得系统开发更高效。
(图片来自网络)
《未来架构》一书中讲过一个 AKF 立方体模型,从三个纬度讲述功能拆分、水平扩展、数据分区所产生的复杂度,如果只有一个系统,单台部署,单点存储,那么它就应该只是一个点,因为随着量级的增大,系统在每个纬度不断延长,逐渐成为一个庞大的立方体。
(图片来自网络)
拆分可以分为系统拆分、功能拆分和读写拆分。如一个单体系统中,按照用户的交互场景看,门户首页可以拆分为前台系统,类目、商品、搜索等功能可以拆分到商品中心,订单、结算、发票等功能则可以拆分到交易中心,这就是简单的系统拆分和功能拆分。而有些功能较为特殊,如商详页,在读取时需要聚合读取,所以又可以进行读写拆分。
2,服务化
微服务首先需要有微服务基础设施,没有微服务基础设施,实践微服务就是一场灾难。以 SOA 落地方式为例,SOA 落地方式主要有:分布式服务化和集中式管理(ESB),分布式服务化的技术手段有 dubbo 或 spring cloud 等等,必须有整套的如服务发现、服务订阅、服务监控、服务追踪、服务日志等微服务基础设置,才能进行微服务架构。不能简单只有 p2p 的服务调用就开始服务化,那是不现实的。
3,服务治理
当服务拆分的设计方案确认完毕,而服务化的基础设施也部署到位,那么系统往往一下子就会突然发布出成百上千个服务接口,就像一个网络一样,每个服务或微服务就像网中的一个节点,彼此之间关联和联系。但是,服务网络应该被设计成为一个有向无环图,否则,就是一团麻。如开始的时候,只是 A 调用 B,但随着业务发展,需要修改,但是因为对 A 不了解,就加个环节 B 调用 A,如此形成了环形调用,不仅逻辑复杂了,还降低了稳定和性能。
这里的服务治理不是讲中间件团队的服务治理,如超时优化、启动优化等等,而是作为应用平台对服务的治理。服务治理又分为三个阶段:服务梳理、服务界定和服务编排。所谓服务梳理就是梳理系统对外开放的服务化接口,包括服务的 provider 和 consumer,以及服务分组、动态路由等依赖的梳理,然后对拆散的服务进行归类、界定,确定服务领域从属性,依据领域模型重新界定服务边界,最后通过服务迁移、切换,对同一领域的服务接口进行服务整合,提供统一的服务出口,实现服务编排。
为什么要进行服务治理?那先来看看不进行服务治理的坏处。
微服务化拆分必然会在初期产生代码到处拷贝,没有一套代码,必然会造成复杂的扩散,如同样语意的 A、B 两个接口,如果 A 接口存在性能问题需要加缓存,那么 B 接口也会存在同样的问题,并需要同样的改造,这样复杂度就会到处蔓延,同时也无法实现统一的服务层架构,还有,如果同样语意或相似语意的接口太多,那么接口就是混乱的,无法实现有限接口的治理,如果系统出了问题,可能很难定位问题。业务逻辑是依赖于数据的,如果接口是混乱的,那么慢 SQL 等质量差的 SQL 就无法进行有效收口改造,同样就更加难以实现数据库拆分和解耦。综上,服务治理的过程就是从无序到有序的过程。
总结一下看从拆分到服务化的过程,就是将原来一个整体的服务打碎,碎成一块一块的零碎服务,然后再对服务重新归类、整合,形成一个一个新领域的、独立的服务。如单体架构就像一个宏伟的城堡,如果相对其中一个点进行调整和改造,那么可能面临着牵一发而动全身的风险。
微服务化就是以一系列小的服务去支撑一个应用的方法论。简明扼要的说,就是:分而治之。
4,服务高可用
服务拆分并服务化之后,是不是就完事了,不!真正的微服务实践才刚刚开始,简单提供了一个接口,是没有意义的,它必须具备高可用、高性能、高并发的三高特性,尤以高可用最为重要。保障服务高可用的方法有很多,如数据异构、多级缓存、超时与重试、熔断、异步并发、降级、限流、消息队列、压测与预案等 。
重点说下数据异构,所谓数据 异构,就是将通过顺序消费或并发消费的方式,订阅 MySQL 数据库的 binlog,然后通过消息,如 Kafka 等 MQ 方式,异地存储到缓存或其它数据源中,如 Redis、Elasticsearch 等。数据异构现在被普通使用,实现数据聚合,形成数据闭环。
在进行数据异构的过程中,需要关注几个关键点,一是 CAP 原则,即强一致性往往被舍弃,而采用最终一致性的设计。二是缓存,缓存在微服务架构中,不能被当成银弹来使用,使用缓存必须正视的问题有:热点缓存 & 大 Value 缓存、缓存穿透等问题。三是消息,消息现在还存在的问题有:延迟问题、消息的不稳定性(如丢消息)、消息的不确定性(如 timeout)、消息补偿、柔性事务等问题。而这些是微服务高可用架构实践中,不能不面对的问题。
5,服务隔离
隔离也是服务拆分的一种,为什么单独讲?因为服务拆分、服务化、服务治理和服务高可用都是在事前或事中可以做的,然而,有些系统已经成了某个样子,那么我们接收时,首先能做到的就是迅速对系统进行隔离,保证业务之间不互相影响,具体实践包括:进程线程隔离、集群/机房隔离、读写隔离、动静隔离、爬虫/热点隔离等。
从单体架构开始,到分布式架构设计、微服务架构,再到现在领域驱动设计,作为实践微服务的架构师,思想上又有什么变化?
我觉得,程序员都是很闷骚的,但每个程序员心里都住一个大侠,金庸《笑傲江湖》里有气宗和剑宗之分,什么是剑宗,我理解就像是剑招、招数,如 spring、spring boot、spring cloud,还有 tomcat、netty、serviceless 等,这些技术、中间件、框架等就像脚手架一样可以帮助我们提高效率,但是,我们是否需要出现一个新技术就学习一个技术呢?而这就让我想到了气宗,它就像是通过对内在规律的掌握,打通任督二脉,实现融会贯通,正如《九阳神功》所说:他强任他强,清风拂山岗,我自一口真气足。如 Kakfa、Flink、Hbase 等分区可用性保障的设计思想都是基于 BigTable 的分区复制策略。
正是重心由外到内的转变,让我认知到,现在系统架构的发展,很多系统架构的复杂度,已经不是简简单单通过引入一些新技术或框架,就能降低系统复杂的。它需要大大的提前对风险、问题、隐患的预知,做到未雨绸缪。《从零开始学架构》书中提到:架构设计的发展历程就是在于降低软件系统复杂的历程。
《架构整洁之道》书中提到:一个软件系统存在的意义,是系统用来赚钱或省钱的那部分代码,那才是整个系统的皇冠明珠。所以,当拿到一个需求的时候,首先考虑的是要解决什么问题,它的问题域是什么,而不是先考虑用哪种技术去实现或解决这个问题,这是本末倒置的处理方法。
从领域驱动设计的角度来看,需求首先是问题域,属于业务边界的事情,梳理了解要实现什么功能和需求,然后在外延到工作边界,确认团队合作的方式,因为很多需求都可能需要跨团队合作的,最后才会到应用边界,即技术实现的解决问题领域。
上图是一个典型的洋葱头模型,它的思想就是阐述了从内到外的思考过程。然后,现实工作中,经常有人从外往内去考虑问题,从我过往的经历举例,一次是数据异构的方案,之前是采用 Storm,后来 flink 开始流行,我就将业务切换到了 flink,但是对于 flink 的调优不到位,从而影响了系统稳定性,还有一次是之前搜索用 solr,后来 es 开始流行,就像切到 es,但是 es 和 solr 两套搜索引擎的搜索排序结果是不一样的,最后,我们只能强奸了业务。其实,这都是不太对的。《架构整洁之道》也提到:良好的架构设计应该尽可能地允许用户推迟和延后决定采用什么框架、数据库、Web 服务以及其他与环境相关的工具。
理解领域驱动设计,需求或问题它开始于一个领域意愿,由领域专家和交付团队一起,输出统一语言和领域知识,统一语言可以是敏捷看板,因为一个团队是由产品、运营、研发、测试组成的,彼此之间是基于统一语言进行沟通的,然后定义领域边界,区分核心领域、普通领域、支持领域,其中核心领域就是由核心团队开发的,支持领域可以是外包等实现的,在通过映射上下文,确认限界上下文,最终确认系统应用边界。
(图片来自网络)
采用领域驱动设计的六边形架构绘制了一张交易系统的领域边界图,不同颜色表明不同领域,U/D 代表上游和下游。这个图还不算完整,因为领域业务逻辑是要为端服务的,而通过对领域的梳理,可以通用的支持很多端。即表达为领域有界,端无界。最 终,每个限界上下文即是微服务。
其实,每个系统的微服务架构演进道路各不相同,所以,实践微服务不能一板一眼原封照抄,而是要根据自己的业务特征,合理的裁剪,找到合适落地方案。总结一句话,那就是:因需而变。虽然微服务的路径是不一样的,但方向是相同的。再次回顾微服务的架构演进历程,你会发现,从单体架构开始,进行拆分,形成微服务之后,又因为各种这样的原因,很多微服务架构又在进行合并,现在领域驱动设计来了,又开始重新拆分。真可谓:话说天下大势,分久必合,合久必分。
原文 https://mp.weixin.qq.com/s/ZxUB6fCZbfT9souesQmMNA