10 微服务落地的技术实践

如今,做一个优秀的程序员越来越难。激烈的市场竞争、互联网快速的迭代、软件系统规模化发展,无疑都大大增加了软件设计的难度。因此,对于架构师的能力要求也越来越高,就像我的一本书里写道的:

作为顶级架构师应当具备这样两个核心能力:
(1)能够将业务转换为技术;
(2)能合理利用技术支撑业务。

“不想当将军的士兵不是好士兵”,因此作为普通开发人员的你,也应当树立成为顶级架构师的目标,并不断努力。

能够将业务转换为技术,意味着需要将更多的精力放到对业务的理解中。技术本身并不能产生价值,你必须具备超强的业务落地能力,能够将用户的业务需求落地到技术方案,开发出用户乐于使用的产品和功能,用户才能为之买单,企业才能挣钱。具备这样的能力,才能够强力地帮助企业产生效益,才能体现你的价值。学习 DDD 就能让你掌握快速学习业务领域知识的能力。

能合理利用技术支撑业务,意味着你必须具备广博的知识与开阔的视野,能将用户的业务痛点,快速落地形成合理的,甚至是最优的技术方案。做出用户需要的功能,让用户为之买单,从而为企业产生效益。然而,如今是一个技术快速更迭的时代,各种高新技术层出不穷。每次新产品的开发不是将原有的技术拿来炒冷饭,而是运用更多的新技术解决新问题,让产品更有竞争力与生命力。因此,你必须有广博的技术知识与超强的技术落地能力。

上一讲谈到 DDD 落地微服务的分析设计过程,然后将这些设计最终落实到每个微服务的设计开发中。微服务的落地其实并没有那么简单,需要解决诸多设计与实现的技术难题,这一讲我们就来探讨一下吧。

如何发挥微服务的优势

微服务也不是银弹,它有很多的“坑”。开篇词中提到,当我们将一个庞大的业务系统拆分为一个个简单的微服务时,就是希望通过合理的微服务设计,尽量让每次的需求变更都交给某个小团队独立完成,让需求变更落到某个微服务上进行变更。唯有这样,每次变更只需独立地修改这个微服务,独立打包、独立升级,新需求就实现啦,才能发挥微服务的优势。

然而,过去很多系统都是这样设计的(如上图所示),多个模块都需要读取商品信息表,因此都通过 JDBC 直接读取。现在要转型微服务了,起初采用数据共享的微服务设计,就是数据库不变,然后简单粗暴地直接按照功能模块进行微服务拆分。这时,多个微服务都需要读取商品信息表,都通过 SQL 直接访问。这样的设计,一旦商品信息表发生变更,那么多个微服务都需要变更。这样的设计就使得微服务的变更与发布变得复杂,微服务的优势无法发挥

通过前面 DDD 的指导,是希望做“小而专”的微服务设计。按照这样的思路设计微服务,对商品信息表的读写只有“商品维护”微服务。当其他微服务需要读写商品信息时,就不能直接读取商品信息表,而是通过 API 接口去调用“商品维护”微服务。这样,日后因商品信息变更而修改的代码就只限于“商品维护”微服务。只要“商品维护”微服务对外的 API 接口不变,这个变更则与其他微服务无关。只有这样的设计,才能真正发挥微服务的优势。

为了规范“小而专”的微服务设计,在微服务转型之初,先按照 DDD 对数据库表按照用户权限进行划分。每个微服务只能通过自己的账号访问自己的表。当需要访问其他的表时,只能通过接口访问相应的微服务。这样的划分,就为日后真正的数据库拆分做好了准备,微服务转型将更加平稳。

怎样提供微服务接口

因此,微服务的设计彼此之间不是孤立的,它们需要相互调用接口实现高内聚。然而,当一个微服务团队向另一个微服务团队提出接口调用需求时,另一个微服务团队该如何设计呢?

首先第一个问题,当多个团队都在向你提出 API 接口时,你怎么提供接口。如果每个团队给你提需求,你就必须要做一个新接口,那么你的微服务将变得非常不稳定。因此,当多个团队向你提需求时,必须要对这些接口进行规划,通过复用用尽可能少的接口满足他们的需求;当有新的接口提出时,要尽量通过现有接口解决问题。这样做,你就能用更低的维护成本,更好地维护自己的微服务。

接着,当调用方需要接口变更时怎么办?变更现有接口应当尽可能向前兼容,即接口的名称与参数都不变,只是在内部增加新的功能。这样做是为了不影响其他微服务的调用。如果确实需要更改现有的接口怎么办?宁愿增加一个新的接口也最好不要去变更原有的接口

此外,调用双方传递的值对象需要完全一致吗?当然不用。当被调方因为某些变更对值对象增加了字段,而这些字段调用方不使用时,那么调用方不需要跟着变更值对象。因为微服务间的调用是采用RESTful 接口,以 JSON 的形成传递数据,是一种松耦合的调用。因此调用双方的值对象可以不一致,从而降低了需求变更的微服务更新范围。

最后,调用方如何调用接口呢?这里分为同步调用异步调用

  • 第 09 讲谈到“用户接单 Service”在完成下单以后,用消息队列通知“饭店接单 Service”,就是异步调用。

  • 接着,“用户接单Service”常常要查找用户表信息,但前面说了,它没有查询用户表权限,因为用户表在“用户注册”微服务中。这时,“用户接单 Service”通过同步调用“用户注册 Service”的相关接口。

具体设计实现上,就是在“用户接单”微服务的本地,增加一个“用户注册 Service”的 feign 接口。这样,“用户接单 Service”就像本地调用一样调用“用户注册 Service”,再通过这个 feign 接口实现远程调用。这样的设计叫作“防腐层”的设计。如下图所示:

微服务的拆分与防腐层的设计图

譬如,大家想象这样一个场景。过去,“用户注册 Service”是在“用户下单”微服务中的。后来,随着微服务设计的不断深入,需要将“用户注册 Service”拆分到另外一个微服务中。这时,“用户下单Service”与“取消订单 Service”,以及其他对“用户注册 Service”的调用都会报错,都需要修改,维护成本就很高。这时,在微服务的本地放一个“用户注册 Service”的 feign 接口,那么其他的 Service 都不需要修改了,维护成本将得以降低。这就是“防腐层”的作用,即接口变更时降低维护成本

去中心化的数据管理

按照前面 DDD 的设计,已经将数据库按照微服务划分为用户库、下单库、接单库、派送库与饭店库。这时候,如何来落地这些数据库的设计呢?微服务系统最大的设计难题就是要面对互联网的高并发与大数据。因此,可以按照去中心化数据管理的思想根据数据量与用户访问特点选用不同的数据存储方案存储数据

  • 微服务“用户注册”与“饭店管理”分别对应的用户库与饭店库,它们的共同特点是数据量小但频繁读取,可以选用小型的 MySQL 数据库并在前面架设 Redis 来提高查询性能;

  • 微服务“用户下单”“饭店接单”“骑士派送”分别对应的下单库、接单库、派送库,其特点是数据量大并且高并发写,选用一个数据库显然扛不住这样的压力,因此可以选用了 TiDB 这样的 NewSQL 数据库进行分布式存储,将数据压力分散到多个数据节点中,从而解决 I/O 瓶颈;

  • 微服务“经营分析”与“订单查询”这样的查询分析业务,则选用 NoSQL 数据库或大数据平台,通过读写分离将生产库上的数据同步过来进行分布式存储,然后经过一系列的预处理,就能应对海量历史数据的决策分析与秒级查询。

基于以上这些设计就能完美地应对互联网应用的高并发与大数据有效提高系统性能。设计如下图所示:

在线订餐系统的去中心化数据管理图

数据关联查询的难题

此外,各个微服务在业务进行过程需要进行的各种查询,由于数据库的拆分,就不能像以前那样进行 join 操作了,而是通过接口调用的方式进行数据补填。比如“用户下单”“饭店接单”“骑士派送”等微服务,由于数据库的拆分,它们已经没有访问用户表与饭店表的权限,就不能像以往那样进行 join 操作了。这时,需要重构查询的过程。如下图所示:

查询的过程分为 2 个步骤。

  1. 查询订单数据,但不执行 join 操作。这样的查询结果可能有 1 万条,但通过翻页,返回给微服务的只是那一页的 20 条数据。

  2. 再通过调用“用户注册”与“饭店管理”微服务的相关接口,实现对用户与饭店数据的补填。

这种方式既解决了跨库关联查询的问题又提高了海量数据下的查询效率。注意,传统的数据库设计之所以在数据量越来越大时,查询速度越来越慢,就是因为存在 join 操作。因而,在面对海量数据的查询时,干掉 join 操作,改为分页后的数据补填,就能有效地提高查询性能。

然而,在查询订单时,如果要通过用户姓名、联系电话进行过滤,然后再查询时,又该如何设计呢?这里千万不能先过滤用户数据再去查询订单这是一个非常糟糕的设计。我们过去的数据库设计采用的都是3NF(第 3 范式),它能够帮助我们减少数据冗余,然而却带来了频繁的 join 操作降低了查询性能。因此,为了提升海量数据的查询性能,适当增加冗余,即在订单表中增加用户姓名、联系电话等字段。这样,在查询时直接过滤订单表就好了,查询性能就得到了提高。

最后,当系统要在某些查询模块进行订单查询时,可能对各个字段都需要进行过滤查询。这时就不再采用数据补填的方式而是利用 NoSQL 的特性采用“宽表”的设计。按照这种设计思路,当系统通过读写分离从生产库批量导入查询库时,提前进行 join 操作,然后将 join 以后的数据,直接写入查询库的一个表中。由于这个表比一般的表字段更多,因此被称为“宽表”。

由于 NoSQL 独有的特性,为空的字段是不占用空间的,因此字段再多都不影响查询性能。这样,在日后的查询时,就不再需要 join 操作,而是直接在这个单表中进行各种过滤、各种查询,从而在海量历史数据中实现秒级查询。因此,“订单查询”微服务在数据库设计时,就可以通过NoSQL 数据库建立宽表,从而实现高效的数据查询。

总结

基于 DDD 的微服务设计,既强调对业务的分析理解,又强调对业务的技术落地。只有把这两个事情都做好了,产品才能被用户认可,我们才能体现出价值。在这个过程中,微服务间要通过 feign 接口相互调用,数据要通过补填关联查询。此外,还有聚合的实现、仓库和工厂的设计。所有这些内容都需要在 DDD 设计思想的基础上,落地实现。

然而,如果每个模块都要反复地写代码去实现这些功能,DDD 的设计将显得异常烦琐,因此迫切需要有一个既支持 DDD,又支持微服务的技术中台,封装这些代码,简化微服务的设计。

下一讲我将开始讲解支持 DDD 与微服务的技术中台的设计实践。


11 解决技术改造困局的钥匙:整洁架构

有个“大师与太空”的梗是这样说的:大师通常都站得很高很高、高瞻远瞩,站得有多高呢?都是站在太空里的,所以很多人追随大师容易缺氧。因此,我们学习大师的理论,就要脚踏实地、踏踏实实地将其落地到项目应用中,重新落地到“地面”上,这些理论才是有用的,落地 DDD 同样是这样。

如何落地 DDD 呢?除了在项目中实践 DDD,领域建模,按照 DDD 的思想设计开发以外,还需要一个支持 DDD 与微服务的技术中台。在 DDD 实现过程中,这个技术中台应当能够封装那些烦琐的聚合操作、仓库与工厂的设计,以及相关的各种技术。有了这个技术中台的支持,开发团队就可以把更多的精力放到对用户业务的理解,对业务痛点的理解,快速开发用户满意的功能并快速交付,而不再受限于那些烦琐的技术细节,从而降本增效。这样,不仅编写代码减少了,技术门槛降低了,还使得日后的变更更加容易,技术更迭也更加方便。

那么,如何设计这样一个技术中台呢?首先应当从现有系统的设计痛点开始分析。

底层技术的更迭

这些年,随着互联网、大数据、人工智能等新技术层出不穷,整个 IT 产业的技术架构也在快速迭代。

  • 过去,我们说“架构是软件系统中最稳定不变的部分”;

  • 而现在,我们说“好的架构源于不停地演变”。

因此,如今的架构设计需要思考如何让底层的架构更易于技术更迭、易于架构调整,以应对不断演进的新技术、新框架,从而获得行业竞争的技术优势。

然而,在实际项目中,特别是很多运行了七八年、十多年的老项目,要做一次技术升级,那叫一个费劲,就像脱一层皮那么痛苦。为什么技术升级那么费劲呢?究其原因,是在系统设计开发时,大量的业务代码依赖于底层的技术框架,形成了耦合。

譬如,过去采用 hibernate 进行数据持久化,每个模块的 DAO 都要继承自 HibernateDaoSupport。这样,所有的 DAO 都与 Hibernate 形成了一种依赖。当系统架构由 Hibernate2 升级成 Hibernate3,甚至升级成 MyBatis,就不是改换一个 jar 包那么简单了。

技术框架一换,底层的类、接口、包名都变了,就意味着上层的所有模块的 DAO 都需要改,改完了还要测试。这样的技术升级成本极高,风险极大,需要我们认真去思考解决方案。

总之,老系统技术架构升级成本极高的根源,在于业务代码与底层技术框架的耦合。因此,解决思路就是对它们进行解耦。如何解耦呢?就是在上层业务代码与底层技术框架之间建立“接口层”。

如何在业务代码与底层框架之间建立“接口层”呢?如上图所示,上层业务代码在进行持久化时,各个模块的 DAO 不再去调用底层框架,而是对接口层的 DaoSupport 进行调用。DaoSupport 接口是我们自己设计的,它应当满足上层的所有业务需求,比如各种类型的 insert、 update、delete、get、load、find,并让这个接口保持稳定。上层业务代码的设计实现都依赖于 DaoSupport 接口,只要它稳定了,上层业务代码就稳定了。

接着,在 DaoSupport 接口的基础上编写实现类,由实现类去调用底层技术框架,实现真正的持久化。

  • 起初使用 Hibernate2 作为底层框架,所以为 Hibernate2 编写了一个实现类。

  • 当 Hibernate2 升级成 Hibernate3 时,为 Hibernate3 写一个实现类。

  • 当底层框架要升级成MyBatis 时,再为 MyBatis 写一个实现类。

这样的设计,当系统进行技术架构升级时,其影响就不再扩展到业务层代码,而仅仅局限于调整接口层的实现类,技术升级的成本将得到大幅度的降低。

整洁架构的设计

通过前面对问题的分析与接口层的设计,可以得出一个非常重要的结论:如何既能轻松地实现技术架构演化,又能保证开发团队的快速交付呢,关键的思路是将业务代码与技术框架解耦。如上图所示,在系统分层时,基于领域驱动的设计,将业务代码都整合在业务领域层中去实现。这里的业务领域层包括了 BUS 层中的 Service,以及与它们相关的业务实体与值对象。

业务领域层设计的实质,就是将领域模型通过贫血模型与充血模型的设计最终落实到对代码的设计。在此基础上,通过分层将业务领域层与其他各个层次的技术框架进行解耦,这就是“整洁架构”的核心设计思路。

整洁架构(The Clean Architecture)是 Robot C. Martin 在《架构整洁之道》中提出来的架构设计思想。如上图所示,它以圆环的形式把系统分成了几个不同的层次,因此又称为“洋葱头架构(The Onion Architecture)”。在整洁架构的中心是业务实体(黄色部分)与业务应用(红色部分),业务实体就是那些核心业务逻辑,而业务应用就是面向用户的那些服务(Service)。它们合起来组成了业务领域层,也就是通过领域模型形成的业务代码的实现。

整洁架构的最外层是各种技术框架,包括:

  • 与用户 UI 的交互;

  • 客户端与服务器的网络交互;

  • 与硬件设备和数据库的交互;

  • 与其他外部系统的交互。

整洁架构的精华在于其中间的适配器层,它通过适配器将核心的业务代码,与外围的技术框架进行解耦。因此,如何设计适配层,让业务代码与技术框架解耦,让业务开发团队与技术架构团队各自独立地工作,成了整洁架构落地的核心。

整洁架构设计的细化图,图片来自《软件架构编年史》

如图,进一步细化整洁架构,将其划分为 2 个部分:主动适配器被动适配器

  • 主动适配器,又称为“北向适配器”,就是由前端用户以不同的形式发起业务请求,然后交由应用层去接收请求,交由领域层去处理业务。用户可以用浏览器、客户端、移动 App、微信端、物联网专用设备等各种不同形式发起请求。然而,通过北向适配器,最后以同样的形式调用应用层。

  • 被动适配器,又称为“南向适配器”,就是在业务领域层完成各种业务处理以后,以某种形式持久化存储最终的结果数据。最终的数据可以存储到关系型数据库、NoSQL 数据库、NewSQL 数据库、Redis 缓存中,或者以消息队列的形式发送给其他应用系统。但不论采用什么形式,业务领域层只有一套,但持久化存储可以有各种不同形式。南向适配器将业务逻辑与存储技术解耦

整洁架构的落地

按照整洁架构的思想如何落地架构设计呢?如上图所示,在这个架构中,将适配器层通过数据接入层、数据访问层与接口层等几个部分的设计,实现与业务的解耦。

首先,用户可以用浏览器、客户端、移动 App、微信端、物联网专用设备等不同的前端形式,多渠道地接入到系统中,不同的渠道的接入形式是不同的。通过数据接入层进行解耦,然后以同样的方式去调用上层业务代码,就能将前端的多渠道接入,与后台的业务逻辑实现了解耦。这样,前端不管怎么变,有多少种渠道形式,后台业务只需要编写一套,维护成本将大幅度降低。

接着,通过数据访问层将业务逻辑与数据库解耦。前面说了,在未来三五年时间里,我们又将经历一轮大数据转型。转型成大数据以后,数据存储的设计可能不再仅限于关系型数据库与 3NF的思路设计,而是通过 JSON、增加冗余、设计宽表等设计思路,将其存储到 NoSQL 数据库中,设计思想将发生巨大的转变。但无论怎么转变,都只是存储形式的转变,不变的是业务逻辑层中的业务实体。因此,通过数据访问层的解耦,今后系统向大数据转型的时候,业务逻辑层不需要做任何修改,只需要重新编写数据访问层的实现,就可以转型成大数据技术。转型成本将大大降低,转型将更加容易。

最后,就是底层的技术架构。现在我们谈架构,越来越多地是在谈架构演化,也就是底层技术架构要不断地随着市场和技术的更迭而更迭。但是,话虽如此,很多系统的技术架构更迭,是一个非常痛苦的过程。为什么呢?究其原因,是软件在设计时,将太多业务代码与底层框架耦合,底层框架一旦变更,就会导致大量业务代码的变更,各个业务模块的都要更迭,导致架构调整的成本巨大、风险高昂。

既然这里的问题是耦合,解决的思路就是解耦。在平台建设的过程中,除了通过技术选型将各种技术整合到系统中以外,还应通过封装,在其上建立接口层。通过接口层的封装,封装许多技术的实现,以更加简便的接口开放给上层的业务开发人员。这样,既可以降低业务开发的技术门槛,让他们更加专注于业务,提高开发速度,又让业务代码与技术框架解耦。有了这种解耦,就使得未来可以用更低的成本技术更迭,加速技术架构演进,跟上这个快速变化的时代。

总结

整洁架构的中心是基于 DDD 的业务实现,即那些通过领域模型指导设计与开发的 Service、Entity 与 Value Object。整洁架构的最外层是各种硬件、设备与技术框架。而整洁架构最核心的思想,是通过适配器层,将业务实现与技术框架解耦,这也是 DDD 落地到架构设计的最佳实践。

因此,支持 DDD 与微服务的技术中台,就是基于整洁架构的思想,将 DDD 底层的那些烦琐的聚合操作、仓库与工厂的设计,与微服务的技术框架,以及整洁架构中的适配器,统统封装在技术中台中。有了这个技术中台,就能让上层的业务开发人员,更加轻松地运用 DDD 的思想,更加快捷地更迭与交付用户需求,从而在激烈的市场竞争中获得优势。

下一讲将进一步讲解这样的技术中台是如何设计的。

点击 GitHub 链接,查看源码。


DDD微服务架构设计第四课 微服务落地实践的技术中台相关推荐

  1. DDD微服务架构设计第四课 DDD指导微服拆分和落地实现

    07 在线订餐场景中是如何开事件风暴会议的? 微服务设计最核心的难题是微服务的拆分,不合理的微服务拆分不仅不能提高研发效率,反倒还使得研发效率更低,因此要讲究"小而专"的设计.&q ...

  2. DDD微服务架构设计第三课 DDD领域中的聚合、工厂和仓库、限界上下文

    05 聚合.仓库与工厂:傻傻分不清楚 上一讲,我们知道了,要将领域模型最终转换为程序设计,可以落实到 3 种类型的对象设计,即服务.实体与值对象,然后进行一些贫血模型与充血模型的设计思路.但这远远不够 ...

  3. 基于DDD的微服务架构设计

    基于DDD的微服务架构设计 1. DDD摘要&概述 每个公司都希望研发的系统具备高扩展性,以便做产品和业务迭代时,成本降到最低,效率提到最高:当下流行的微服务架构.中台架构的目标都是在不同层面 ...

  4. 微服务架构设计总结实践

    -     目录    - 一.微服务架构介绍 二.出现和发展 三.传统开发模式和微服务的区别 四.微服务的具体特征 五.SOA和微服务的区别 六.如何具体实践微服务 七.常见的微服务设计模式和应用 ...

  5. Java高并发、分布式框架,从无到有微服务架构设计

    微服务架构模式(Microservice Architect Pattern).近两年在服务的疯狂增长与云计算技术的进步,让微服务架构受到重点关注 微服务架构是一种架构模式,它提倡将单一应用程序划分成 ...

  6. 微服务架构设计总结实践篇,10 步搭建微服务

    一.微服务架构介绍 二.出现和发展 三.传统开发模式和微服务的区别 四.微服务的具体特征 五.SOA和微服务的区别 六.如何具体实践微服务 七.常见的微服务设计模式和应用 八.微服务的优点和缺点 九. ...

  7. 微服务架构设计实践之七:业务架构

    微服务架构设计实践之七:业务架构 原文:微服务架构设计实践之七:业务架构 版权声明: https://blog.csdn.net/beyondself_77/article/details/79842 ...

  8. 北达软微服务架构设计与实践圆满结束

    2016年10月26-27日,为期两天的微服务架构设计与实践在北京大学成功举办.随着新一代信息技术的快速发展,业务需要快速变化.快速创新,互联网的连接带来了大的并发量和数据量.从单机架构走向分布式架构 ...

  9. 一张图搞懂微服务架构设计

    前言 当前,微服务架构在很多公司都已经落地实施了,下面用一张图简要概述下微服务架构设计中常用组件.不能说已经使用微服务好几年了,结果对微服务架构没有一个整体的认知,一个只懂搬砖的程序员不是一个好码农! ...

最新文章

  1. 创建SSH keys
  2. 2020-11-16(深入理解计算机系统2.4节选)
  3. Win10开启Administrator超级管理员账户
  4. Hibernate 多对多关联查询条件使用
  5. linux命令之awk终极系列
  6. python安装包,使用镜像安装
  7. oracle关闭多余游标,关闭结果集后,Oracle不删除游标
  8. Java多线程进阶(三六)—— J.U.C之collections框架:DelayQueue
  9. 【编程之美】java二进制实现重建
  10. 中国重点流域已实现休禁渔制度全覆盖
  11. 如何使用串口调试助手(调试串口)
  12. 腾飞之势,搏击苍穹:网页设计
  13. python生成简单二维码_使用Python生成个性二维码
  14. 大家敏捷,才是真的敏捷——记敏捷培训
  15. android图片处理方法(不断收集中)
  16. python实现工具exe自动化
  17. Android : Broadcast
  18. bzoj 4585 烟火表演 - 动态规划 - 可并堆
  19. Java的优缺点是什么?具体是怎样运行的?
  20. 解决网站因调用谷歌字体库打开慢(适用wordpress和调用谷歌内容的网站)

热门文章

  1. Nature Communications: MOGONET使用图卷积网络集成多组学数据,允许患者分类和生物标志物识别
  2. 考研 计算机网络第一章计算机网络体系结构 知识点总结
  3. 如何设计USB TYPE-C接口的便携显示器和电视机,形态二讲述:Type-c桌面台式显示器
  4. The 15th Heilongjiang Provincial Collegiate Programming Contest(A,C,F,G,H,L)
  5. Mac查看附近WiFi信道及路由器信道的选择
  6. JAVA 实现数据对比
  7. PM进阶之路 | 产品设计规范
  8. 一歌一故事之西海情歌
  9. (8) 支持向量机(下)(模型评估指标、ROC曲线)
  10. vue_案例_点击div的时候,改变背景色