1.简介:
1.微服务简介
1.什么是加德纳技术成熟度曲线
一是萌芽期(Technology Trigger)又称感知期,人们对新技术产品和概念开始感知,并且表现出兴趣;
二是过热期(Peak of Inflated Expectations),人们一拥而上,纷纷采用这种新技术,讨论这种新技术。典型成功的案例往往会把人们的这种热情加上把催化剂;
三是低谷期(Trough of Disillusionment),又称幻想破灭期。过度的预期,严峻的现实,往往会把人们心理的一把火浇灭;
四是复苏期(Slope of Enlightenment),又称恢复期。人们开始反思问题,并从实际出发考虑技术的价值。相比之前冷静不少;
五是成熟期(Plateau ofProductivity),又称高原期。该技术已经成为一种平常。
2.六边形架构
六边形架构或六角架构是Alistair Cockburn在2005年提出,解决了传统的分层架构所带来的问题,实际上它也是一种分层架构,只不过不是上下或左右,而是变成了内部和外部。
在领域驱动设计(DDD)和微服务架构中都出现了六边形架构的身影,在《实现领域驱动设计》一书中,作者将六边形架构应用到领域驱动设计的实现,六边形的内部代表了application和domain层,
而在Chris Richardson对微服务架构模式的定义中,每个微服务使用六边形架构设计,足见六边形架构的重要性。那么让我们一探究竟,它为何如此受青睐。
1.问题
传统的分层架构具有广泛的应用,例如经典的三层架构,把系统分为表示层、业务逻辑层、数据访问层。在Martin Fowler的《企业应用架构模式》一书中做过深入阐述,
本书04年出版,时至今日分层架构仍然是常用的设计方法,分层架构可以降低耦合、提高复用、分而治之,但同时也还是存在一些问题:
应用逻辑在不同层泄露,导致替换某一层变得困难、难以对核心逻辑完整测试:你是否有过困惑,代码到底应该放在哪个层,虽然定义了各层的职责,
但是总有人不严格遵循层次的分界,对于三层架构,常常会出现业务逻辑写在了表示层,或者业务逻辑直接和数据访问绑定。传统的分层架构是一维的结构,有时应用不光是上下的依赖,
可能是多维的依赖,这时一维的结构就无法适应了。
六边形架构又称为端口-适配器,这个名字更容器理解。六边形架构将系统分为内部(内部六边形)和外部,内部代表了应用的业务逻辑,外部代表应用的驱动逻辑、基础设施或其他应用。
内部通过端口和外部系统通信,端口代表了一定协议,以API呈现。一个端口可能对应多个外部系统,不同的外部系统需要使用不同的适配器,适配器负责对协议进行转换。
这样就使得应用程序能够以一致的方式被用户、程序、自动化测试、批处理脚本所驱动,并且,可以在与实际运行的设备和数据库相隔离的情况下开发和测试。
2.内涵
六边形架构的重点体现在以下几个方面:
关注点
对于分层架构中层次的界定,Martin Fowler给出了一个判定的方法,就是如果把表示层换成其他实现,如果和原来的表示层有重复实现的内容,那么这部分内容就应该放到业务逻辑层。
那么如何让开发人员在系统设计过程中始终保持这种视角,传统的分层架构是难以做到的。六边形架构有一个明确的关注点,从一开始就强调把重心放在业务逻辑上,
外部的驱动逻辑或被驱动逻辑存在可变性、可替换性,依赖具体技术细节。而业务逻辑相对更加稳定,体现应用的核心价值,需要被详尽的测试。
外部可替换
一个端口对应多个适配器,是对一类外部系统的归纳,它体现了对外部的抽象。应用通过端口为外界提供服务,这些端口需要被良好的设计和测试。内部不关心外部如何使用端口,
从一开始就要假定外部使用者是可替换的。六边形的六并没有实质意义,只是为了留足够的空间放置端口和适配器,一般端口数不会超过4个。
适配器可以分为2类,“主”、“从”适配器,也可称为“驱动者”和“被驱动者”。
自动测试
在六边形架构中,自动化测试和用户具有同等的地位,在实现用户界面的同时就需要考虑自动化测试。它们对应相同的端口。六边形架构不仅让自动化测试这件事情成为设计第一要素,
同时自动化测试也保证应用逻辑不会泄露到用户界面,在技术上保证了层次的分界。
依赖倒置
六边形架构必须遵循如下规则:内部相关的代码不能泄露到外部。所谓的泄露是指不能出现内部依赖外部的情况,只能外部依赖内部,这样才能保证外部是可以替换的。
对于驱动者适配器,就是外部依赖内部的。但是对于被驱动者适配器,实际是内部依赖外部,这时需要使用依赖倒置,由驱动者适配器将被驱动者适配器注入到应用内部,
这时端口的定义在应用内部,但是实现是由适配器实现。
1.2、走向单体地狱
单体应用的缺点:
1.成功的应用有一个趋势,随着时间推移而变得越来越臃肿
2.应用的规模也将减缓发展。应用越大,启动时间越长
3.另一个大问题是,复杂的单体应用本身就是持续部署的障碍
4.当不同模块存在资源需求冲突时,单体应用可能会难以扩展
5.单体应用的另一个问题是可靠性。因为所有模块都运行在同一进程中。任何模块的一个 bug,比如内存泄漏,都可能会拖垮整个进程。
6.最后同样重要的是,单体应用使得采用新框架和语言变得非常困难
1.3、微服务 — 解决复杂问题
一个服务通常实现了一组不同的特性或功能,例如订单管理、客户管理等。每个微服务都是一个迷你应用,它自己的六边形架构包括了业务逻辑以及多个适配器。
一些微服务会暴露一个供其他微服务或应用客户端消费的 API。其他微服务可能实现一个 web UI。在运行时,每个实例通常是一个云虚拟机(virtual machine,VM)
或者一个 Docker 容器。
应用的每个功能区域现在都由自己的微服务实现。此外,Web 应用被划分为一组更简单的应用。例如,以我们的出租车为例,一个是乘客的应用,一个是司机的应用。
这样更容易为特定的用户、司机、设备或者专门的用例部署不同的场景。每个后端服务暴露一个 REST API,大部分服务消费的 API 由其他服务提供。
例如,Driver Management 使用了 Notification 服务器来给司机发送一个可选路线通知。UI 服务调用了其他服务来渲染页面。服务也可以使用异步、基于消息的通信。
一些 REST API 也暴露给移动端应用供司机和乘客使用。然而,应用不能直接访问后端服务。相反,他们之间的通信是由一个称为 API 网关(API Gateway)的中介负责。
API 网关负责负载均衡、缓存、访问控制、API 度量和监控,可以使用 NGINX 来实现。
微服务架构模式明显影响到了应用与数据库之间的关系,与其他共享单个数据库模式(schema)的服务有所不同,其每一个服务都有自己的数据库模式。
一方面,这种做法与企业级数据库数据模型的思想相背,此外,它经常导致部分数据冗余。然而,如果你想从微服务中受益,每一个服务都应该有自己的数据库模式,
因为它能实现松耦合。
每个服务都拥有各自的数据库。而且,服务可以使用一种最适合其需求、号称多语言持久架构(polyglot persistence architecture)的数据库。
例如,Driver Management,要找到与潜在乘客接近的司机,就必须使用支持高效地理查询的数据库。
从表面上看,微服务架构模式类似于 SOA。微服务是由一组服务组成。然而,换另一种方式去思考微服务架构模式,它是没有商业化的 SOA,没有集成
Web 服务规范(WS-*)和企业服务总线(Enterprise Service Bus,ESB)。基于微服务的应用支持更简单的轻量级协议,例如,REST,而不是 WS-*。
它们也尽量避免使用 ESB,而是实现微服务本身有类似 ESB 的功能。微服务架构也拒绝了 SOA 的其他部分,例如,数据访问规范模式概念。
1.4、微服务的优点
第一,它解决了复杂问题。它把可能会变得庞大的单体应用分解成一套服务。虽然功能数量不变,但应用已经被分解成可管理的块或者服务。每个服务都有一个明确的边界定义方式,
如远程过程调用(RPC)驱动或消息驱动 API。微服务架构模式强制一定程度的模块化,实际上,使用单体代码来实现是极其困难的。因此,使用微服务架构模式,
个体服务能被更快地开发,并且易于理解和维护。
第二,这种架构使得每个服务都可以由一个团队独立专注开发。开发者可以自由选择任何符合服务 API 契约的技术。当编写一个新服务时,
他们可以选择当前的技术。此外,由于服务较小,使用当前技术重写旧服务将变得更加可行。
第三,微服务架构模式可以实现每个微服务独立部署。
最后,微服务架构模式使得每个服务能够独立扩展。
1.5、微服务的缺点
没有银弹。与其他技术一样,微服务架构模式也存在着缺点。其中一个缺点就是名称本身。微服务这个术语的重点过多偏向于服务的规模。事实上,
有些开发者主张构建极细粒度的 10 至 100 LOC(代码行)服务,虽然这对小型服务来说可能比较好,但重要的是,小型服务只是一种手段,而不是主要目标。
微服务的目标在于充分分解应用以方便应用敏捷开发和部署。
微服务的另一个主要缺点是由于微服务是一个分布式系统,其使得整体变得复杂。开发者需要选择和实现基于消息或者 RPC 的进程间通信机制。
此外,由于目标请求可能很慢或者不可用,他们必须要编写代码来处理局部故障。虽然这些并不是很复杂、高深,但模块间通过语言级方法/过程调用相互调用,
这比单体应用要复杂得多。
微服务的另一个挑战是分区数据库架构。更新多个业务实体的业务事务是相当普遍的。这些事务在单体应用中的实现显得微不足道,因为单体只存在一个单独的数据库。
在基于微服务的应用中,你需要更新不同服务所用的数据库。通常不会选择分布式事务,不仅仅是因为 CAP 定理。他们根本不支持如今高度可扩展的 NoSQL 数据库和消息代理。
你最后不得不使用基于最终一致性的方法,这对于开发人员来说更具挑战性。
测试微服务应用也很复杂。
微服务架构模式的另一个主要挑战是实现了跨越多服务变更。例如,我们假设你正在实现一个修改服务 A、服务 B 和 服务 C 的需求,其中 A 依赖于 B,且 B
依赖于 C。在单体应用中,你可以简单地修改相应的模块、整合变更并一次性部署它们。相反,在微服务中你需要仔细规划和协调变更到每个服务。
例如,你需要更新服务 C,然后更新服务 B,最后更新服务 A。幸运的是,大多数变更只会影响一个服务,需要协调的多服务变更相对较少。
部署基于微服务的应用也是相当复杂的。一个单体应用可以很容易地部署到基于传统负载均衡器的一组相同服务器上。每个应用实例都配置有基础设施服务的位置(主机和端口),
比如数据库和消息代理。相比之下,微服务应用通常由大量的服务组成。例如,据 Adrian Cockcroft 了解到,Hailo 拥有 160 个不同的服务,Netflix 拥有的服务超过 600 个。
每个服务都有多个运行时实例。还有更多的移动部件需要配置、部署、扩展和监控。此外,你还需要实现服务发现机制,使得服务能够发现需要与之通信的任何其他服务
的位置(主机和端口)。相比较,传统的基于票据(ticket-based)和手动的操作方式无法扩展到如此复杂的程度。因此,要成功部署微服务应用,
需要求开发人员能高度控制部署方式和高度自动化。
1.1、构建单体应用
我们假设,你正在开发一个打车应用,打算与 Uber 和 Hailo 竞争。经过初步交流和需求收集,你开始手动或者使用类似 Rails、Spring Boot、Play 或者 Maven 等平台来生成一个新项目。新应用有一个模块化的六边形架构,如图 1-1 所示:
1.2、走向单体地狱
1.4、微服务的优点
1.5、微服务的缺点