写点什么

架构设计:微服务架构如何划分?这 6 个标准原则让你一目了然

发布于: 2020 年 09 月 21 日
架构设计:微服务架构如何划分?这6个标准原则让你一目了然

前几天在 InfoQ 看到一篇文章,讲微服务架构如何设计,结合笔者用 3 年多摸索出来的经验,看了之后很有感受,文章地址如下:


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


以前写过一篇文章,关于 SOLID 原则《架构收藏必备:架构设计的六大原则》,里面讲的就是面向对象设计的时候,如果做到 6 个原则,但是在工作中发现这种原则面对微服务设计的时候,出现了局限性。


现在的互联网发展太快,各类工具,框架,运用平台都围绕着微服务去建立一个极其丰富的技术体系,对于很多老一辈的人来说,在面向业务责任划分,技术责任划分的时候会有一定经验,但是如果是一个新手去做,此时面对这些服务边界的时候会存在很多疑惑,无法自行进行服务边界的划分,或者无法判断自己划分的服务边界是否是可用的,有没有存在什么藕断丝连的问题,而这些东西,技术上的能力并不能帮我们去很好解决这个问题,IDEALS 原则笔者认为,这个核心思想可以帮助我们架构师或者开发人员,更好去理解划分基于微服务的解决方案。



IDEALS 原则

在 IDEALS 原则中,作者提出了 6 个原则,目前这些原则虽然笔者无法保证可以适用于所有的微服务体系的东西,但是却是关键点与成功点。


1、Interface Segregation(接口分离)


2、Deployability(可部署性)


3、EventDriven(事件驱动)


4、Availability Over Consistency(可用性高于一致性)


5、Loose Coupling(松耦合)


6、Single Responsibility(单一责任)


Interface Segregation(接口分离)

最初进行接口设计的时候想着复用,随着项目的推进,发现这个接口越来越臃肿,这里的臃肿不单单是代码量上的增加,更多的是一个接口出现了很多的调用方,后台、小程序、苹果端、安卓端、第三方等,这就导致了同一个服务逻辑里面需要对接很多客户端,后期在进行代码修改的时候经常是牵一发而动全身,所有的客户端不可用的情况会越来越常见,这就是为什么我们要将接口进行隔离。



接口隔离有个最重要的目的,就是让不同类型的客户端只能看到自己所对应的接口(服务契约),在框架的设计上,按照不同类型的客户端设计不同的接口,可以参考《springboot2.2.X手册:构建多元化的API接口,我们这样子设计 》,在这里面分别用 application,view,external 分别对应小程序接口,后端管理接口,第三方管理接口,从而实现接口隔离。



Deployability(可部署性)

从软件发展至今,从来没有一个时期像微服务这样子它的部署会成为重中之重,笔者认为,这一切都是互联网快速发展的产物,快,轻,持续才能满足现在高速变化的 C 端需求,特别是像我们人口基数这么大的国家来说。


在微服务的应用中,强调的是每一个服务可独立部署,独立拓展,在应用上是隔离的,这里要区分上面的接口隔离,接口隔离更加注重某一个应用对外的服务提供策略,这里更趋向于业务边界、性能边界、技术边界三者。在设计里面,设计模式及其他设计策略,主要还是集中于每一个独立应用,应尽可能避免过度依赖的同时一些通用的“组织”应该形成连锁依赖,从而避免过渡设计与过渡开发,又可以实现独立服务运行,这个考验每一个架构师的技术功底及业务功底。


连锁依赖

微服务的可部署性不单单体现在能够独立部署,它更加趋向于部署运维体系的一体化,从而形成标准化,流程化的一套章程。一个软件如果采用微服务思想来进行设计,那它从一开始就注定要实现非常高效的可部署性,如何做到可以考虑以下策略


1、容器化


目前很多微服务的应用都离不开 Docker+Kubernetes,Docker 是 DotCloud 开发的一个基于 LXC 的高级容器引擎,基于 Apache2.0 协议开源,没有使用过的同学可以参考一下《收藏手册:Docker安装RabbitMQ,只需3步 》第二章 。Kubernetes 我们一般叫做 K8S,是用来部署,管理 Docker 的,可以实现 Docker 的 WEB 操作。微服务容器化指的是把我们的服务打包成 docker 文件后部署上去,从而实现跨平台及云服务提供商的复制跟部署,K8S 同步提供了共享机制跟资源,目前来说,Docker 跟 Kubernetes 是目前业界用的最普遍的一套组合。



2、网格化


这个名词相对于很多同学比较陌生跟抽象,可能听的最多的是服务网格,它处于基础设施层,使服务与服务之间的通信更加便捷,通过服务里复杂的技术组成的云基础设施层,来保证请求的可靠性。目前开源的软件包含 Istio、Linkerd 和 Consul Connect,很遗憾,笔者目前只接触过 Istio。


Istio 提供一种简单的方式来为已部署的服务建立网络,该网络具有负载均衡、服务间认证、监控等功能,而不需要对服务的代码做任何改动。想要让服务支持 Istio,只需要在您的环境中部署一个特殊的 sidecar 代理,使用 Istio 控制平面功能配置和管理代理,拦截微服务之间的所有网络通信:

1、HTTP、gRPC、WebSocket 和 TCP 流量的自动负载均衡。

2、通过丰富的路由规则、重试、故障转移和故障注入,可以对流量行为进行细粒度控制。

3、可插入的策略层和配置 API,支持访问控制、速率限制和配额。

4、对出入集群入口和出口中所有流量的自动度量指标、日志记录和跟踪。

5、通过强大的基于身份的验证和授权,在集群中实现安全的服务间通信。

6、Istio 旨在实现可扩展性,满足各种部署需求。



3、网关化


网关在微服务中起着一个非常重要的作用,包含但不限于以下内容:路由,安全稽查,请求限流,熔断,缓存,协议消息转换,请求流量分配等,目前国内用得最多的是 zuul,kong,spring gateway 等,目前在应用中,主要集中两点


企业级应用:面对一些企业级开发的时候,这时候的业务流程是非常复杂的,采用微服务如果划分的不好,很容易导致开发量剧增,目前笔者采用一种方式聚合网关的方式来丰富网关的应用,让网关可以满足企业级的应用《微服务网关实战01-网关介绍 》


互联网应用:互联网讲究快速反应,快速迭代,业务相对比较单一化,此时我们的目的是不能把网关做的太重,尽可能轻量级来满足拓展性强的特点,当出现数据聚合的时候,笔者采用 ES 来做数据聚合底层,这方面的方案比较开放,可以有不同的设计策略。



4、日志集中化


应用微服务化能非常轻松的扩容后,日志输出的分散程度会成线性增加从而增加运维的压力,我们需要一个工具来把日志整合起来,目前采用比较多的事 ELK,Graylog,Fluentd 等,笔者前段时间发现了一个私人开发的工具类 EasyLog《springboot2.2.X手册:抛弃ELK,百亿日志+调用链的Easylog很放心 》,整合了调用链的轻量级应用,可以试试。


但是对于普通的只有 2-3 个服务的系统,是否考虑有必要性去加个日志服务器?


5、服务追踪化


这里的服务追踪指的是服务调用链的追踪,每一个服务从发起,流经哪些服务,最终结束于哪个服务,可以采用工具来进行检测、收集、跟踪这些数据并可视化出来,在 spring cloud 里面提供了 Zipkin 来做,但是这个界面并不是很友善,经常使用的工具可以有 Cat,PinPoint 等。



6、配置化


微服务的数量级别的增加导致配置也成了一个难题,目前笔者采用阿里的 Nacos 来做注册中心兼配置中心《springboot2.2.X手册:Eureka不更,Consul被禁,启用Nacos 》,已经集成了两种功能的东西就不再自己添加另外的组件。


7、持续交付部署化


从刚开始的手动发布部署到现在全自动化的操作,微服务在部署上是很需要自动化的,但是这种前提也是建立在当你的服务已经成为标准化之后,传统的发布工具 Jenkins、Gitlab CI/CD 等都很好用,如果采用阿里云的话可以直接使用云效来做,个人想玩的话玩一下瓦力《发现开源:替换Jenkins,支持多用户多语言部署平台Walle很震撼 》。


在发布中,经常会使用蓝绿部署和金丝雀发布来保证微服务在上线的时候几乎接近 0 的停机空档,实现问题服务的快速切换。



EventDriven(事件驱动)

提到微服务的事件驱动就不得不讲一下微服务的调用方式,通常我们采用以下三种调用方式


1、Rest 风格的 Http 调用


2、RPC 调用,做过 dobbo 的同学应该知道 dubbo 里面的 RPC 调用,开源的有 gRPC 可以使用


3、消息中间件方式进行调用


这三种方式没有绝对的好坏,需要根据实际场景来做决策,用得最多的还是采用 http 的方式进行调用,RPC 最少。相对于消息中间件,其他两种方式都是同步阻塞式,当然现在很多应用都是这种方式,很多应用也需要这种方式,异步处理不好容易出现服务灾难。


在消息中间件的选择上,目前最多的有三个 kafka,rabbitmq,rocketmq,笔者目前在处理大文件的导出方面采用 mq 跟批处理的方式来做,避免等待,避免服务器同时处理太多数据从而内存跟 CPU 撑不住。从事件角度说,事件驱动基本组织是由一个事件集合器、一个事件发送器、一个事件监听处理器,这里也要区分消息驱动。


事件驱动构建的微服务效果是非常明显的,在伸缩性与吞吐量上具有非常明显的性能优势,像我们在做接口服务的时候,面对一些外部接口的数据接入,这类做法是非常值得借鉴的。



Availability Over Consistency(可用性高于一致性)

CAPD 定理告诉我们,在一个分布式的系统中一致性、可用性、分区容错性这三者不能同时兼顾到,最多只能同时实现其中两者。一致性在微服务中一直是一个非常棘手的问题,目前业界采用最多的是可用性优先,最终实现一致性,这么做考虑的问题点是强一致性需要加入网络之间的性能及通信损耗,这点上在用户体验至上的社会里很可能让用户爆炸。


但是也并不是说任何场景都是可用性优先,这里应该是大部分场景是可用性优先,特别是面向 C 端用户的时候,但是像面对一个业务逻辑非常复杂的操作并且允许比较长的等待时长的时候,一致性是第一优先考虑的,目前我们在解决强一致性的时候可以用阿里的 Seata《微服务难点:阿里的分布式事务中间件,seata能上生产吗 》。



Loose Coupling(松耦合)

一直在强调高内聚低耦合,但是怎样的程度才算高内聚低耦合?都没有一个很标准化或者可量化的东西来说明,也许很多人说的就真的就是随口一说的概念。在微服务中,服务与服务之间往往存在调用关系,采用约定的方式来对服务之间进行解耦,实现服务交互,这种我们称之为服务契约。


服务契约的形成往往通过约定的技术手段来实现,约定好输入输出的规范,约定好输入输出的方式(HTTP 或者 MQ),约定好数据聚合方式,这种约定俗成的规矩可以帮我们减少服务与服务之间调用错乱的问题,成功的对每个服务进行解耦,同时每个服务在自身应用中可以做到高内聚,不过这里面笔者认为最重要的一点就是服务的划分,下面会通过服务的职责问题来讲解。


具体落地到如果去实现高内聚低耦合,笔者目前通过以下 4 点来实现


1、契约精神优先,所有的服务的输入输出全部约定好,不允许更改。


2、限定数据聚合方法,搭建数据聚合服务,做单向聚合不做服务之间的互相调用


3、发布-订阅模式开启,针对一些特殊应用启用消息投递模式进行异步解耦


4、指定一个服务一个库,从数据进行隔离,实现服务的底层解耦,增加可移植化。



Single Responsibility(单一责任)

微服务的职责问题,笔者目前觉得是最难的,划分的颗粒度在哪?这是笔者从几年前接触微服务以来经常要思考的一个问题。服务的划分包含具备太多的内聚力的话会导致服务膨胀,在持续部署与持续交付上变得越来越慢;如果服务的划分颗粒度太细又很容易陷入开发人员疲于奔命的场景,服务越多一致性问题越严重,如何做一个颗粒度适中的微服务确实头疼,目前笔者发现最好用的方式还是基于领域驱动设计与建模


1、根据业务边界划分服务职责


2、根据性能问题划分服务


3、切记不要为了微服务而拼命划分



结论

在面对微服务设计的时候 IDEALS 原则可以应付大多数的场景,但是这个并不是银弹,好比经常说微服务并不是银弹一样的,架构设计是一个不断演化不断适配的过程,它并不是一种技术或解决方案,笔者相信 IDEALS 原则可以帮我们更好实现微服务的落地,微服务的工具,平台,框架不断的出现,但是设计原则或者思想还是不变,工具或者框架并不能成为设计思想的拦路虎,万变不离其宗,应用原文作者最后的一句话:


I believe a good understanding of microservice IDEALS will help you navigate the technology space with more clarity.


--END--


作者:@互联网应用架构


原创作品,抄袭必究


如需要源码,转发,关注后私信我


部分图片或代码来源网络,如侵权请联系删除,谢谢!


发布于: 2020 年 09 月 21 日阅读数: 275
用户头像

喜欢奋战在一线的架构师 2020.09.21 加入

专注架构,微服务,机器学习,JAVA,Python等领域教程,欢迎关注

评论 (1 条评论)

发布
用户头像
都是瞎几把扯淡,consul被禁个毛,企业版在国内有几个人用,你询过价吗就在这瞎说,根本用不起的好吧。还不都是用开源版的。
2020 年 12 月 12 日 22:21
回复
没有更多了
架构设计:微服务架构如何划分?这6个标准原则让你一目了然