微服务架构基础——解读六边形架构

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 追溯微服务架构的渊源,一般会涉及到六边形架构。追溯六边形架构的起源,要看始作俑者Alistair Cockburn的这篇文章 http://alistair.cockburn.us/Hexagonal+architecture, 读原文,译重点,记感受, 如下:六边形架构的意图采用同等的方式,应用可以通过用户,程序,自动化测试或批处理脚本来驱动,而独立于最终的运行环境及数据库进行开发和测试。


追溯微服务架构的渊源,一般会涉及到六边形架构。追溯六边形架构的起源,要看始作俑者Alistair Cockburn的这篇文章 http://alistair.cockburn.us/Hexagonal+architecture, 读原文,译重点,记感受, 如下:


六边形架构的意图

采用同等的方式,应用可以通过用户,程序,自动化测试或批处理脚本来驱动,而独立于最终的运行环境及数据库进行开发和测试。

当外部事件到达端口的时候,相应的适配器将其转化成合适的过程调用或者消息,然后传递给应用,而应用对输入设备一无所知。输出内容时,应用通过端口把要传递出去的消息传给适配器,适配器再针对信息接收者的具体实现要求将其转化成合适的信号。从语义上来说,应用跟它周围的适配器有着良好的互动,而对适配器外部的一切无感知。

这是一种设计模式,被Cockburn定义为“端口和适配器模式“,设计模式不仅指导了代码的实现,同时支持结构的实现,又是一种解耦合的技巧。

所需解决的问题场景

不能解决问题的架构,都只是架构师手中的玩具。六边形架构面对的一个典型问题是业务逻辑与用户界面的代码交叉,这是开发中一个非常令人头痛的问题,有三个恶果:

  1. 系统无法方便地进行自动化测试,因为部分逻辑依赖界面元素的,比如输入框的长度或按钮的位置,而这些细节又是易变的;
  2. 同样,无法把一个面向人机交互的系统移植到一个自动化处理的系统
  3. 另外,应用程序之间的相互驱动变得很困难,有时甚至不可能的

“万能的那一层”出现了,可以在架构里增加一个新的层,并承诺不会有业务逻辑被放到着一新层里。然而随着时间的推移,会发现新的层里还是掺杂了业务逻辑,于是老问题又出现了。

怎么办呢?假设一下,如果应用提供的每一个功能都有相应的API会是怎样的结果呢?QA可以通过自动化测试脚本来监测新改的代码,检验是否会破坏已有的功能。业务专家在GUI出来之前就可以创建自动化测试用例,作为程序员们检测是否正确完成工作的依据,同时,这也将成为测试部门所运行的测试的一部分。应用以"headless"模式部署,其它程序通过调用API的方式使用所提供的功能。这种方式使得复杂系统的设计变得容易,面向业务的应用之间不需要人工干预就可以互相调用。最后,回归测试检测到出现问题的地方,并加以修复,而保证业务逻辑不会进入表现层。

另一个典型的问题是,如果应用绑定了外部数据库或其它服务,当数据库宕机或者正在迁移的时候,依赖数据库的程序就无法正常工作。这会导致响应延迟,这是一种相当糟糕的体验。

这两个问题之间没有明显的联系,但它们之间看起来是对称的。这又是一种面向接口的设计么? 可以这样理解,因为接口的概念外延太大了,而在具体的编程语言实现中,interface 有往往太小了。这里把它明确为端口和适配器。

方案

不论用户端交互问题还是服务端数据库编程问题,其同源原因就是在设计和实现过程中出现的业务逻辑混淆,以及与外部实体之间的交互关系。我们要关注的非对称性不是应用的"左边"和"右边",而是它的"内部"和"外部",该属于"内部"的代码就不要泄露到"外部"去,其基础理论还是那些基本的设计原则。

六边形架构的核心理念是:应用通过"端口"跟外部进行交互的。"端口"让人联想到操作系统的端口,任何符合协议的设备都可以被插到相应的端口上。端口的协议是为了两个设备之间能够进行通信而设计的,位于OSI 7层协议模型中的传输层。

对应用来说,API就是协议。对于每一个外部设备,都有对应的适配器把API转换成自己所需要的信号,反之亦然。用户图形界面就是一个很好的例子,它是把用户操作映射到端口API的适配器,还有其它的例子如自动测试套件,批处理驱动器,以及任何需要跨应用程序交互的代码。
对于应用的数据处理层面,应用通过与外部实体交互得到数据,这里用到的协议一般指数据库协议。从应用角度来看,把SQL数据库迁移到普通的文件或者其它类型的数据库,API仍然保持不变。适应于同一个端口的适配器还包括SQL适配器,文件适配器,以及更重要的数据库mock,它可以是驻存在内存里的数据库,不一定是真实的数据库。

很多应用都只有两个端口:用户端端口 和 数据库端端口。这种情况看起来是对称的,很自然地就会用单维度多层次的架构来构建它。在开发应用程序时,就是我们经常看到的三层、四层或五层架构。这种架构有两个问题:

  1. 很容易跨越层间的边界,把业务逻辑渗透到其它层中去。
  2. 有的应用可能不止需要两个端口,所以不能用单维度架构来构建。

这就是六边形架构提出的原因,它着重解决对称性问题,应用通过端口与外部进行交互,而外部的实体也可以用同样的方式来处理。六边形架构强调以下两点:

首先,通过"内外"的不对称性以及端口的特点,摆脱单维度多层次架构的束缚。可以定义不同数量的端口,2个,3个或者4个,这里说的六边形不限于只有六个边, 可以根据需要加入更多的端口和适配器,"六边形架构"只是视觉上的一种叫法。

其次,关注整体架构的结果导向,一个端口对应一个或一组有目的交互行为。但一个端口一般会有多个适配器,可以是无人应答机,语音留言机,按键电话,用户图形界面,测试套件,批处理驱动器,HTTP接口,程序之间的接口,mock的数据库,或者真实的数据库。

从应用层面来看,这一架构的目的是将注意力聚焦在内外非对称性上,让外部的实体从应用视角来看都是一样的。

结构


六边形架构2.jpg

一个典型的六边形架构应用有两个端口,每个端口对应几个适配器,这两个端口分别用于应用控制和数据获取。该应用可以被自动化测试,系统层面的回归测试,用户交互操作,远程HTTP调用或者另外一个本地应用驱动。在数据方面,通过配置使用外部的数据库,可以是Oracle数据库,mock的数据库,测试数据库或生产数据库,从而实现应用和外部数据库的解耦。应用的功能说明是依据六边形内部的接口来编写的,而不是依据外部可能用到的任何一种技术。


六边形架构3.jpg

对于一个典型的三层架构而言,简化起见,每个端口只给出两个适配器。架构的顶层跟底层可以适用多个适配器,这些适配器是可以按照一定顺序来开发。带有数字的箭头展示了一个团队是如何按照一定顺序来开发和使用应用的:

  1. 用测试套件(如FIT)来驱动应用,用mock的内存数据库模拟真实数据库。
  2. 给应用增加GUI,但仍然使用mock数据库。
  3. 在集成测试的时候使用自动化测试脚本(例如,通过Cruise Control触发),数据库换成包含测试数据的真实数据库。
  4. 用户在生产环境使用应用,数据库也是真实的。

代码示例

FIT的文档给出了一个简单的例子来说明端口与适配器模式,它是一个计算折扣价格的应用:
discount(amount) = amount * rate(amount);

在这个应用里,amount来自用户的输入,而rate来自数据库,所以需要两个端口。先用测试代码跟rate常量来测试,然后再使用GUI跟mock数据库,来自IHC的Gyan Sharma提供这个示例的代码,具体参考原文。另一个用Ruby和Rack实现的例子可以看这里https://github.com/totheralistair/SmallerWebHexagon。

六边形架构中的左右非对称性

六边形架构强调端口之间的相似性。在实现的时候一般有两种风格,称之为"主"和"从",或者叫驱动者跟被驱动者,实际上是CS结构的又一体现。

在上面的例子中,FIT被用在左边的端口上,而mock的东西在右边。在三层架构中,FIT在最顶层,mock在最底层。这两者之间的区别在于是谁触发了会话,或者谁在会话中起主导作用。FIT就是"主",这个框架就是被设计用来通过脚本来驱动应用的。Mock数据库就是"从",数据库被设计用来响应来自应用的查询或记录变更事件的。

根据系统用例,把"主"的端口和适配器放在了六边形的左边,而"从"的端口和适配器放在了六边形的右边。它们之间的关系以及它们的实现方式是很有用的,但前提是要用在六边形架构中。端口与适配器模式最大的好处就是可以让应用可以完全独立地运行。

六边形架构的应用边界

六边形架构对用例编写也有强化作用。开发者在编写用例时常犯的错误是把端口外边的技术细节包含在用例里,这样的用例易读性差,乏味,脆弱,难于维护。使用六边形架构后,编写用例应该以应用的边界为准。用例要明确应用能够支持的功能和事件,而不用关心外部的技术是怎么样的。

如何使用端口取决于个人经验。一种极端的情况,每个用例都被赋予一个端口,这样应用里就会有成百上千的端口。另一种情况是,把左右两边的端口分别合并起来,这样就只剩两个端口了。这两种情况都不是最理想的。一些大家所熟知的应用是这样的:

  • 天气预报系统有四个端口:天气预报源,管理员,订阅者,订阅者数据库。
  • 咖啡机控制器有四个端口:用户,包含菜单和价格的数据库,调配师,硬币盒。
  • 医药系统有三个端口:护士,处方数据库,药剂师。

这些实例没有拘泥于端口的数量,一切都是为了业务系统服务的。一般倾向于选择更少的端口,实际上是另一种对分层模型的界定。


六边形架构4.jpg

原文中谈到的一个真实应用是这样的,报警系统从国家天气服务中心获取地震,龙卷风,火灾和洪水的预警,然后通过电话或语音留言通知人们。如果遵循技术与业务目标相关联的原则,可以设计成一个接口用于接收来自预报源的数据,一个用来向语言留言机发送通知,一个GUI管理界面,以及一个获取订阅者数据的数据库接口。当希望向系统里增加一些接口的时候,比如一个来自天气服务中心的http接口,或者一个到订阅者的邮件接口,还要考虑怎么让应用套件满足客户定制化需求。这样就会会陷入维护和测试的恶梦,因为要为不同的定制需求开发不同的版本。

经过系统接口设计的调整,新的六边形架构面向的是业务目标,而不是具体的技术,而且把所有的具体技术换成了适配器。这样,很快就把http和邮件接口加入到了系统中,把应用部署成"headless"模式,并添加了一个适配器,可以按需通过API调用连接到其他应用上。最后,因为应用的独立运行能力和各种适配器的存在,就可以使用单独的自动化脚本进行回归测试了。

一句话体会

六边形架构的初衷是为了解决技术与业务系统的解耦合问题,以及技术与技术间的解耦合问题,这一架构从设计模式中来,从业务的实体服务出发,将面向接口的设计具体化的端口协议和适配器实现,将业务实体实现自服务的完备性,可以看作是微服务的一个理论基础吧。

原文中的参考阅读

FIT, A Framework for Integrating Testing: Cunningham, W., online at http://fit.c2.com, and Mugridge, R. and Cunningham, W., ‘’Fit for Developing Software’’, Prentice-Hall PTR, 2005.
The “Adapter” pattern: in Gamma, E., Helm, R., Johnson, R., Vlissides, J., “Design Patterns”, Addison-Wesley, 1995, pp. 139-150.
The “Pedestal” pattern: in Rubel, B., “Patterns for Generating a Layered Architecture”, in Coplien, J., Schmidt, D., “PatternLanguages of Program Design”, Addison-Wesley, 1995, pp. 119-150.
The “Checks” pattern: by Cunningham, W., online at http://c2.com/ppr/checks.html
The “Dependency Inversion Principle”: Martin, R., in “Agile Software Development Principles Patterns and Practices”, Prentice Hall, 2003, Chapter 11: “The Dependency-Inversion Principle”, and online at http://www.objectmentor.com/resources/articles/dip.pdf
The “Dependency Injection” pattern: Fowler, M., online at http://www.martinfowler.com/articles/injection.html
The “Mock Object” pattern: Freeman, S. online at http://MockObjects.com
The “Loopback” pattern: Cockburn, A., online at http://c2.com/cgi/wiki?LoopBack
“Use cases:” Cockburn, A., “Writing Effective Use Cases”, Addison-Wesley, 2001, and Cockburn, A., “Structuring Use Cases with Goals”, online at http://alistair.cockburn.us/crystal/articles/sucwg/structuringucswithgoals.htm


微信扫一扫关注该公众号


目录
相关文章
|
5天前
|
敏捷开发 监控 数据管理
构建高效微服务架构的五大关键策略
【4月更文挑战第20天】在当今软件开发领域,微服务架构已经成为一种流行的设计模式,它允许开发团队以灵活、可扩展的方式构建应用程序。本文将探讨构建高效微服务架构的五大关键策略,包括服务划分、通信机制、数据管理、安全性考虑以及监控与日志。这些策略对于确保系统的可靠性、可维护性和性能至关重要。
|
6天前
|
消息中间件 监控 持续交付
构建高效微服务架构:后端开发的进阶之路
【4月更文挑战第20天】 随着现代软件开发的复杂性日益增加,传统的单体应用已难以满足快速迭代和灵活部署的需求。微服务架构作为一种新兴的分布式系统设计方式,以其独立部署、易于扩展和维护的特点,成为解决这一问题的关键。本文将深入探讨微服务的核心概念、设计原则以及在后端开发实践中如何构建一个高效的微服务架构。我们将从服务划分、通信机制、数据一致性、服务发现与注册等方面入手,提供一系列实用的策略和建议,帮助开发者优化后端系统的性能和可维护性。
|
2天前
|
持续交付 API 开发者
构建高效微服务架构:后端开发的新范式
【4月更文挑战第24天】 随着现代软件系统的复杂性日益增加,传统的单体应用已难以满足快速迭代与灵活扩展的需求。微服务架构作为一种新兴的软件开发模式,以其服务的细粒度、独立部署和弹性伸缩等优势,正在逐渐成为后端开发的重要趋势。本文将深入探讨微服务架构的设计原则、关键技术以及在实际业务中的应用实践,旨在为后端开发者提供构建和维护高效微服务架构的参考指南。
|
3天前
|
监控 API 持续交付
构建高效微服务架构:后端开发的新趋势
【4月更文挑战第23天】 随着现代软件开发实践的不断演进,微服务架构已经成为企业追求敏捷、可扩展和弹性解决方案的首选。本文深入探讨了如何构建一个高效的微服务架构,涵盖了关键的设计原则、技术选型以及实践建议。通过分析微服务的独立性、分布式特性和容错机制,我们将揭示如何利用容器化、服务网格和API网关等技术手段,来优化后端系统的可维护性和性能。文章旨在为后端开发人员提供一套全面的指南,以应对不断变化的业务需求和技术挑战。
|
8天前
|
机器学习/深度学习 运维 Prometheus
探索微服务架构下的系统监控策略
【4月更文挑战第18天】在当今快速迭代和持续部署盛行的软件工程实践中,微服务架构因其灵活性和可扩展性受到企业青睐。然而,随着服务的细粒度拆分和网络通信的增加,传统的监控手段已不再适用。本文将探讨在微服务环境中实施有效系统监控的策略,包括日志聚合、性能指标收集、分布式追踪以及异常检测等关键技术实践,旨在为读者提供构建稳定、可靠且易于维护的微服务系统的参考指南。
13 0
|
8天前
|
监控 持续交付 开发者
构建高效微服务架构:后端开发的新趋势
【4月更文挑战第18天】在数字化转型的浪潮中,微服务架构已成为企业提升系统灵活性、加速产品迭代的关键。此文深入探讨了构建高效微服务架构的实践方法,包括服务划分原则、容器化部署、持续集成/持续部署(CI/CD)流程以及监控与日志管理等关键技术点。通过分析具体案例,揭示了微服务在提高开发效率、降低维护成本及促进团队协作方面的显著优势。
|
9天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
10天前
|
监控 JavaScript 安全
构建微服务架构下的API网关
【4月更文挑战第15天】在微服务架构中,API网关扮演着至关重要的角色。它作为系统的唯一入口,不仅负责请求的路由、负载均衡和认证授权,还涉及到监控、日志记录和服务熔断等关键功能。本文将探讨如何构建一个高效且可靠的API网关,涵盖其设计原则、核心组件以及实现策略,旨在为后端开发人员提供一套实用的指导方案。
26 4
|
11天前
|
监控 负载均衡 API
构建高性能微服务架构:后端开发的最佳实践
【4月更文挑战第14天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选方法。本文深入探讨了后端开发人员在设计和维护高性能微服务时需要遵循的一系列最佳实践。我们将从服务划分原则、容器化部署、API网关使用、负载均衡、服务监控与故障恢复等方面展开讨论,并结合实际案例分析如何优化微服务性能及可靠性。通过本文的阅读,读者将获得实施高效微服务架构的实用知识与策略。
|
13天前
|
运维 监控 自动驾驶
构建可扩展的应用程序:Apollo与微服务架构的完美结合
构建可扩展的应用程序:Apollo与微服务架构的完美结合
32 10