您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

微服务设计的原则:IDEALS,而不是SOLID

时间:2020-09-11 11:12:18  来源:  作者:
微服务设计的原则:IDEALS,而不是SOLID

微服务架构

作者:DevOps亮哥

来自:DevOps探路者

一、关键点:

对于面向对象的设计,我们遵循SOLID原则。对于微服务设计,我们建议开发人员遵循IDEALS原则接口分离(Interface segregation),可部署性(deployability),事件驱动(event-driven),可用性胜于一致性(Availability over Consistency),松耦合(Loose coupling)和单一责任(single responsibility)

  • 接口分离:指的是不同类型的客户端(移动应用程序,web应用程序,CLI程序)应该能够通过适合其需求的协议与服务端交互。
  • 可部署性:指的是在微服务时代,也就是DevOps时代,开发人员需要在打包、部署和运行微服务方面做出关键的设计决策和技术选择。
  • 事件驱动:指的是在任何时候,都应该对服务进行建模,通过异步消息或事件而不是同步调用。
  • 可用性胜于一致性:指的是最终用户更看重系统的可用性而不是强一致性,它们对最终一致性也很满意。
  • 松耦合:仍然是一个重要的设计问题,涉及传入和传出的耦合。
  • 单一责任:是一种思想,它支持对不太大或太细的微服务进行建模,因为他们包含了适当数量的内聚功能。

 

在2000年,Robert C. Martin编写了面向对象设计的五个原则。Michael Feathers后来将这五个原则的首字母组成了缩略词,也就是SOLID。从那时起,用于OO设计的SOLID原则就在业界被广为人知。这五个原则是:

  • 单一责任原则(Single responsibility principle)
  • 开闭原则(Open/closed principle)
  • 里氏代换原则(Liskov substitution principle)
  • 界面分离原则(Interface segregation principle)
  • 依赖倒置原则(Dependency inversion principle)

几年前,我在给其他人讲授微服务设计时,一个学生问:“SOLID原则适用于微服务吗”,经过一番思考,我的回答是“部分”。后面的几个月,我发现我一直在寻找微服务的基本设计原则(包含一些缩略词)。为什么这样的问题会如此重要呢?

作为一个行业,我们已经设计和实施基于微服务的解决方案已经超过六年了。在此期间,越来越多的工具、框架、平台和支撑产品围绕微服务建立了一个极其丰富的技术环境。在这种情况下,一个新手微服务开发人员在面对一个微服务项目中许多设计决策和技术选择时会感到迷惑。在这个领域,一组核心原则可以帮助开发人员更好的理解基于微服务的解决方案。

虽然有些SOLID原则适用于微服务,但面向对象是一种设计范式,它处理的元素有类、接口和继承等,与一般分布式系统中的元素(特别是微服务)有着根本的区别。

因此,我们提出以下一套微服务设计的核心原则:

  • 接口分离(Interface segregation)
  • 可部署性(Deployability (is on you))
  • 事件驱动(Event-driven)
  • 可用性胜于一致性(Availability over consistency)
  • 松耦合(Loose coupling)
  • 单一职责(Single responsibility)

这些原则并没有覆盖基于微服务的解决方案的所有设计决策,但他们涉及到创建现代基于服务的系统的关键关注点和成功因素。下面对微服务的“IDEALS”的原则进行详细的解释。

二、接口分离

最初的接口分离原则是指防止面向对象中类使用“胖”接口。换句话说,就是每种类型的客户端应该有单独的接口,而不是提供一个满足所有客户端需要的所有可能方法的接口。

微服务体系结构风格是面向服务体系结构的一种特殊化,其中接口(即服务契约)的设计一直是最重要的。从21世纪初开始,SOA文档就规定了所有客户端都应该遵守的规范模型和规范模式。然而,自从SOA的旧时代以来,我们处理服务契约设计的方式发生了改变。在微服务时代,同一个服务逻辑通常有许多客户端程序。这就是将接口分离应用于微服务的主要目的。

实现微服务的接口分离

微服务接口分离的目标是每种类型的前端都能看到最适合其需求的服务契约。例如,一个移动应用程序系统调用端点,这些端点返回简短的JSON格式数据作为响应。当另一个web应用程序需要返回完整的JSON格式数据作为响应。与此同时,还有一个桌面应用程序调用同一个服务,但需要返回完整的XML格式数据。不同的客户端也可能使用不同的协议。例如,外部的客户端希望使用HTTP来调用gRPC服务。

我们没有试图在所有类型的服务客户机上强加相同的服务契约(使用规范模型),而是通过“分离接口”,以便每种类型的客户机都能看到它需要的服务接口。我们怎么做?一个突出的选择是使用API网关,它可以进行消息格式转换(message format transformation),消息结构转换(message structure transformation),协议桥接(protocol bridging),消息路由(message routing)等。一个流行的替代方案是BFF(Backend for Frontends)模式。在这种情况下,我们为每种不同类型的客户机提供了一个API网关,也就是通常说的,为每种客户机提供不同的BFF,如下图所示:

微服务设计的原则:IDEALS,而不是SOLID

 

三、可部署性

几乎在整个软件历史上,设计工作都集中在与实现单元(模块)如何组织以及运行时元素如何交互相关的设计决策上。架构策略、设计模式和其他设计策略为在层中组织软件元素、避免过度依赖、为特定类型的组件分配特定的角色或关注点以及软件空间中的其他设计决策提供了指导。对于微服务开发人员来说,有一些关键的设计决策超出了软件元素。

作为开发人员,我们早就意识到将软件正确打包并部署到适当的运行时环境中的重要性。然而,我们从没有像今天的微服务那样关注部署和运行时监控。在这里称为“可部署性”的技术和设计决策领域已经成为微服务成功的关键。主要原因是一个简单的事实,即微服务显著增加了部署单元的数量。

因此,IDEALS中的D代表微服务开发者有责任确保软件及其新版本随时可以部署到环境中供用户使用。总之,可部署性包括:

  • 配置运行时基础设施,包括容器、pods、集群、持久性、安全性和网络。
  • 微服务的扩缩容,或者将他们从一个运行时环境迁移到另一个运行时环境。
  • 加速提交+构建+测试+部署过程
  • 减少版本升级时的停机时间
  • 同步相关软件的版本更改
  • 监控微服务运行状况,以快速识别和修复故障。

 

实现良好的可部署性

自动化是实现高效部署的关键。自动化包括明智的使用工具和技术,这是自微服务出现以来不断看到最大变化的领域。因此,微服务开发人员应该在工具和平台方面寻找新的方向。但总是质疑每一个新选择的好处和挑战。(这里可参考Thoughtworks技术雷达和软件架构与设计趋势报告)

以下是开发人员在任何基于微服务的解决方案中为提高可部署性而应该考虑的策略和技术列表:

  • 容器化和容器编排:容器化的微服务更容易实现跨平台和云提供商进行复制和部署,而编排平台为路由、扩展、复制、负载均衡等提供了共享资源和机制。Docker和Kubernetes是当今容器和容器编排的事实标准。
  • 服务网格:这种工具可以用于流量监控,策略执行,身份验证,RBAC,路由,断路器、消息转换等,以帮助容器编排平台中的服务进行通信。流行的服务网格包括Istio、Linkerd和Consul Connect。
  • API网关:通过拦截对微服务的调用,API网关产品提供了丰富的功能集,包括消息转换和协议桥接、流量监控、安全控制、路由、缓存、请求限制和API配额以及熔断。这一领域的主要参与者是Ambassador、Kong、Apiman、WSO2 API Manager、Apigee和Amazon API Gateway。
  • 无服务器架构:通过将服务部署到遵循FaaS范式的无服务器平台,可以避免容器编排的复杂性和操作成本。AWS Lambda、Azure函数和google云函数都是无服务器平台的示例.
  • 日志整合工具:微服务可以轻松的将部署单元的数量增加一个数量级。我们需要工具来整合这些组件的日志输出,以及搜索、分析和生成告警的能力。这个领域流行的工具有Fluentd、Graylog、Splunk和ELK(Elasticsearch、Logstash、Kibana)。
  • 链路跟踪工具:这些工具可用于检测您的微服务,然后生成、收集和可视化运行时跟踪数据,以显示跨服务的调用。它可以帮助您发现性能问题。跟踪工具的例子有Zipkin, Jaeger, and AWS X-Ray,OpenTraceing。
  • DevOps:当开发人员和运维人员可以进行更紧密的沟通和协作时,微服务的工作会更加容易,从基础设施配置到事件处理。
  • 蓝绿部署和金丝雀发布:这些部署策略允许在发布新版本的微服务时实现零或接近零的停机时间,并在出现问题时进行快速切换。
  • 基础设施即代码:这种做法使得构建-部署周期中的交互更少,从而变得更快,更不容易出错,更易于审计。
  • 持续交付:这是缩短从提交到部署间隔并保持代码质量的必要实践。传统的CICD工具有Jenkins、Gitlab CI/CD、Bamboo、GoCD、CircleCI和Spinnaker。最近,Weaveworks和Flux等GitOps工具被添加到这个领域,将CD和IaC结合起来。
  • 配置管理:将配置属性存储在微服务部署单元之外,并且易于管理。

 

四、事件驱动

微服务架构风格用于创建后端服务,这些服务通常使用以下三种类型的方式进行调用:

  • HTTP调用(REST服务)
  • 使用特定于平台的组件技术进行类RPC调用,如gRPC或GraphQL
  • 通过消息中间件处理异步消息

前两个通常是同步的,HTTP调用也是最常见的方式。通常,服务需要调用其他服务进行组合,太多时候,组合中的服务调用是同步的。如果异步,需要连接和接收Queue/Topic里的消息,那么我们将创建一个事件驱动的体系结构。(我们可以讨论消息驱动和事件驱动的区别,但都可以表示网络上的异步通信,使用消息中间件产品(Apache Kafka、RabbitMQ和Amazon SNS)提供的Queue和Topic)

 

事件驱动体系结构的一个重要好处是提高了可伸缩性和吞吐量。这是因为:消息发送者在等待响应时不会被阻塞,并且同一个消息/事件可以由多个接收者以发布-订阅的方式并行使用。

事件驱动的微服务

IDEALS中的E就表示使用事件驱动对微服务进行建模。因为他们更能满足当今软件解决方案的可伸缩性和性能要求,这种设计还促进了松耦合,因为消息发送方和接收方——微服务——是对立的,彼此不了解。可靠性也得到了提升,因为这个设计可以处理微服务的临时中断,当微服务恢复后可以处理排队中的消息。

但事件驱动的微服务,也称为反应式微服务,也会带来挑战,比如异步处理和并行执行,可能需要同步点和相关标识符。设计需要考虑错误和丢失的消息——校正事件和撤销数据更改的机制(如Saga模式)通常是必须的。对于事件驱动体系结构带来的面向用户的事务,应仔细考虑用户体验,以使最终用户了解进度和事故。

 

五、可用性胜于一致性

CAP理论本质上给了我们两个选择:可用性或者一致性。我们看到业界为了让我们选择可用性而付出了巨大努力,从而最终实现一致性。原因很简单:今天的最终用户不会容忍服务不可用。假如一个网络商店,如果我们在浏览产品时显示的库存量和购买时更新的实际库存量之间强制执行强一致,那么数据变更将会带来巨大的开销,如何任何更新库存的服务暂时无法访问,那么页面无法显示库存信息,结账将停止服务。相反,如果选择可用性,用户浏览产品时显示的库存量和购买时更新的实际库存量之间会有偶尔的不一致。当用户在下单买时,然后再去查询真实的库存量,如果没有库存,再提示用户没有库存。从用户的角度来看,这个场景比由于系统要实现强一致而让整个系统不可用或超级慢对所有用户来说要好很多。

 

有些业务操作确实需要很强的一致性。然而,正如Pat Helland指出,当你面对你是想要正确?还是想要现在?的问题时,人们通常想要的是现在而不是正确的答案时,就需要考虑强一致。

 

最终一致性的可用性

对于微服务来说,保证可用性选择的主要策略是数据复制。可以采用不同的设计模式,有时可以组合使用。

  • 服务数据复制模式:当微服务需要访问属于其他应用程序的数据(而API调用不适合获取数据)时,使用此基本模式。我们创建该数据的副本,并使其随时可供微服务使用。该解决方案还需要一种数据同步机制(如ETL工具/程序、发布-订阅消息传递、物化视图),该机制将定期或基于触发器使副本与主数据库保持一致。
  • 命令查询责任分离(CQRS)模式:这里我们将更改数据(Command)的操作设计与实现和只读数据(Query)的操作分开。CQRS通常建立在服务数据复制的基础上,用于提高查询的效率。
  • 事件源(Event Source)模式:我们不在数据库中存储对象的当前状态,而是存储影响该对象的仅附加的、不可变的事件序列。当前状态是通过回放事件获得,这样做是为了提供数据的“查询视图”。因此,事件源通常建立在CQRS设计的基础上。

 

我们经常使用的CQRS模式通常如下图所示:一个可以更改数据的HTTP请求由后台一个REST服务处理,该服务可以操作一个集中式的Oracle数据库。其他只读的HTTP请求转到另一个后台服务,该服务可以从基于文本的Elasticsearch数据存储中获取数据。一个Spring Batch Kubernetes cron任务定期将在Oracle数据库中的变更同步到ES中,这个设计使用两个数据存储之间的最终一致性。即使Oracle DB和cron任务不起作用,查询服务也是可用的。

微服务设计的原则:IDEALS,而不是SOLID

 

六、松耦合

在软件工程中,耦合是指两个软件元素之间相互依赖的程度。对于基于服务的系统来说,传入耦合是指服务用户如何与服务交互。我们知道这种交互应该通过服务契约来实现,并且该服务契约不应该与实现细节和特定技术紧密结合。服务是可以由不同程序调用的分布式组件。有时候,服务提供方不知道所有服务用户在哪里(公共API服务通常就是这样)。因此,服务契约应该避免变更。如何服务契约与服务逻辑或技术紧密耦合,那么当逻辑或技术需要演化时,它也需要同时发生变化。

服务通常需要与其他服务或其他类型的组件交互,从而产生传出耦合。这种交互建立了直接影响服务自治的运行时依赖关系。如果一个服务的自治性较低,它的行为就不那么可预测:最好的情况就是,该服务将与他需要调用的最慢的,最不可靠的和最不可用的组件一样快速、可靠和可用

 

微服务的松耦合策略

IDEALS中的L表示要关注服务及微服务的耦合。可以使用并组合多种策略来改进传入和传出的松散耦合。这些策略包括:

  • 点对点和发布-订阅(Point-to-point and Publish-subscribe):这种通过消息传递的模式改进了耦合性,因为发送方和接收方彼此不知道对方。响应式微服务(如Kafka消费方)的契约将成为消息队列的名称和消息的结构体。
  • API网关和BFF:这些解决方案规定了一个中间组件,该组件处理服务契约与客户端需要的消息格式和协议之间的差异,从而有助于分离他们。
  • 契约优先设计(Contract-first design):通过设计与任何现有代码相关的契约,从而避免创建与技术和实现紧密耦合的api。
  • 超媒体(Hypermedia):对于REST服务,超媒体帮助前端更加独立于服务端点。
  • Facade和Adapter/WrApper模式:这些GoF模式的变体在微服务架构中可以规定内部的组件和服务,可以防止在微服务实现中传播不良的耦合性。
  • 每个微服务一个数据库模式:该模式使得微服务不仅获得了自治性,而且避免了与数据库共享带来的直接耦合。

 

七、单一职责

最初的单一职责原则( Single Responsibility Principle,SRP)是关于在OO类中具有内聚功能。在一个类中拥有多个职责自然会导致紧耦合,并导致脆弱的设计,在变更时会发生意想不到的结果。SRP这个想法很简单,也很容易理解,但要用好并不容易。

单一职责的概念可以扩展到微服务中服务的内聚性。微服务体系结构风格部署单元应该包含一个服务或几个内聚服务。如果一个微服务包含太多的职责,也就是说,有太多不太具有内聚力的服务,那么它就可能会承受一个巨大的痛苦。膨胀的微服务在功能和技术栈方面变得更难发展。而且,持续交付将会变得繁重,因为他们将会使许多开发人员在同一个部署单元开发不同的内容。

 

另一方面,如果微服务过于细粒度,则其中的几个服务可能需要交互来满足用户请求。在最坏的情况下,数据变更可能会跨多个微服务,可能会产生分布式事务的场景。

 

粒度适中的微服务

微服务设计成熟度的一个重要方面是创建粒度适中的微服务的能力。这里的解决方案不是任何工具或技术中,而是在适当的领域建模上。为后端服务建模并为其定义微服务边界可以通过多种方式完成。业界流行的一种驱动微服务范围的方法是遵循领域驱动设计(Domain-Driven Design,DDD)原则。简而言之:

 

一个服务(例如:REST服务)可以具体DDD聚合的作用域。一个微服务的作用域可以是DDD限定的上下文。该微服务中的服务将对应于该限定上下文的聚合。

对于微服务间的通信,我们可以使用:当异步消息满足需求时使用领域事件(Domain Event);当请求-响应更适合时,使用某种形式的中间层进行API调用;当一个微服务需要另一个可用区的大量数据时,可以使用数据复制保证最终一致性。

 

八、结论

IDEALS是在大多数典型的微服务设计中要遵循的核心设计原则。然而,遵循IDEALS并不是使我们的微服务设计成功的良药。通常,我们还需要对质量需求有一个很好的理解,并使设计决策意识到他们的权衡。此外,我们还应该学习可以用来帮助实现设计原则的设计模式和架构策略。还应该掌握可用的技术选型。

 

多年来,我一直使用IDEALS设计、实现和部署微服务,在设计研讨会和讲座中,我与来自不同组织的数百名软件开发人员讨论这些核心原则以及每一个原则背后的许多策略。有时候,会让人觉得微服务的工具、框架、平台和模式层出不穷,我相信,对微服务IDEALS更好的理解,能够帮助我们更清晰的了解技术领域。

翻译自:

https://www.infoq.com/articles/microservices-design-ideals/?itm_source=infoq&itm_campaign=user_page&itm_medium=link



Tags:微服务设计   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
微服务架构作者:DevOps亮哥来自:DevOps探路者一、关键点:对于面向对象的设计,我们遵循SOLID原则。对于微服务设计,我们建议开发人员遵循IDEALS原则:接口分离(Interface segregation...【详细内容】
2020-09-11  Tags: 微服务设计  点击:(99)  评论:(0)  加入收藏
▌简易百科推荐
为了构建高并发、高可用的系统架构,压测、容量预估必不可少,在发现系统瓶颈后,需要有针对性地扩容、优化。结合楼主的经验和知识,本文做一个简单的总结,欢迎探讨。1、QPS保障目标...【详细内容】
2021-12-27  大数据架构师    Tags:架构   点击:(3)  评论:(0)  加入收藏
前言 单片机开发中,我们往往首先接触裸机系统,然后到RTOS,那么它们的软件架构是什么?这是我们开发人员必须认真考虑的问题。在实际项目中,首先选择软件架构是非常重要的,接下来我...【详细内容】
2021-12-23  正点原子原子哥    Tags:架构   点击:(7)  评论:(0)  加入收藏
现有数据架构难以支撑现代化应用的实现。 随着云计算产业的快速崛起,带动着各行各业开始自己的基于云的业务创新和信息架构现代化,云计算的可靠性、灵活性、按需计费的高性价...【详细内容】
2021-12-22    CSDN  Tags:数据架构   点击:(10)  评论:(0)  加入收藏
▶ 企业级项目结构封装释义 如果你刚毕业,作为Java新手程序员进入一家企业,拿到代码之后,你有什么感觉呢?如果你没有听过多模块、分布式这类的概念,那么多半会傻眼。为什么一个项...【详细内容】
2021-12-20  蜗牛学苑    Tags:微服务   点击:(8)  评论:(0)  加入收藏
我是一名程序员关注我们吧,我们会多多分享技术和资源。进来的朋友,可以多了解下青锋的产品,已开源多个产品的架构版本。Thymeleaf版(开源)1、采用技术: springboot、layui、Thymel...【详细内容】
2021-12-14  青锋爱编程    Tags:后台架构   点击:(20)  评论:(0)  加入收藏
在了解连接池之前,我们需要对长、短链接建立初步认识。我们都知道,网络通信大部分都是基于TCP/IP协议,数据传输之前,双方通过“三次握手”建立连接,当数据传输完成之后,又通过“四次挥手”释放连接,以下是“三次握手”与“四...【详细内容】
2021-12-14  架构即人生    Tags:连接池   点击:(16)  评论:(0)  加入收藏
随着移动互联网技术的快速发展,在新业务、新领域、新场景的驱动下,基于传统大型机的服务部署方式,不仅难以适应快速增长的业务需求,而且持续耗费高昂的成本,从而使得各大生产厂商...【详细内容】
2021-12-08  架构驿站    Tags:分布式系统   点击:(23)  评论:(0)  加入收藏
本系列为 Netty 学习笔记,本篇介绍总结Java NIO 网络编程。Netty 作为一个异步的、事件驱动的网络应用程序框架,也是基于NIO的客户、服务器端的编程框架。其对 Java NIO 底层...【详细内容】
2021-12-07  大数据架构师    Tags:Netty   点击:(16)  评论:(0)  加入收藏
前面谈过很多关于数字化转型,云原生,微服务方面的文章。虽然自己一直做大集团的SOA集成平台咨询规划和建设项目,但是当前传统企业数字化转型,国产化和自主可控,云原生,微服务是不...【详细内容】
2021-12-06  人月聊IT    Tags:架构   点击:(23)  评论:(0)  加入收藏
微服务看似是完美的解决方案。从理论上来说,微服务提高了开发速度,而且还可以单独扩展应用的某个部分。但实际上,微服务带有一定的隐形成本。我认为,没有亲自动手构建微服务的经历,就无法真正了解其复杂性。...【详细内容】
2021-11-26  GreekDataGuy  CSDN  Tags:单体应用   点击:(35)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条