微服务架构下的核心话题 (二):微服务架构的设计原则和核心话题

it2024-11-21  13

 

前期回顾:

微服务架构下的核心话题 (一):微服务架构下各类项目的顺势崛起

一、前言

     毫无疑问,微服务架构的设计原则和核心话题是本文要讨论的重点,也是打算从零基础开始构建微服务架构需要事先考虑、规划的。一个好的产品、应用能否稳定运行,持续开发,满足业务需求,能否经得起现实的考验,就需要在设计阶段考虑很多、很多,以确保它的健壮性。

    当我们从单体架构的应用走向基于微服务的架构时,首先会面临一个很棘手的问题是如何进行服务的拆分,服务的拆分粒度应该如何衡量,怎样拆分的服务才算是“微”。接着将又会面临,这么多的服务又如何关联起来呢?如何有效的相互间通信呢?如何高效的部署呢……

     本文我将从微服务架构的设计原则、核心话题两大方面展开讨论,希望能够对你构建一个微服务架构的应用有所帮助。

 

二、微服务架构的设计原则

      软件架构的设计原则、方法论,在很大程度上能够指导、提醒我们应该遵循什么的原则、规范,能让软件架构更加健壮、稳固,并易于开发、扩展、维护等。

1.拆分足够微

     在解决复杂的问题时,我们倾向于将问题划分成若干个小问题来解决,所谓“大事化小,小事化了”。单体架构的应用,随着时间的推移,会变得越来越臃肿,越来越难以维护,适当的做“减法”,可以解决单体架构存在的这些问题。

     将单体架构的应用拆分为微服务架构的应用时,服务的拆分粒度问题,成为了重中考虑的问题。粒度太大,拆分的不够充分,便和单体架构没有太大的区别,更不能发挥出微服务的优势。如果拆分的太细,又将会面临着服务数量太多而引发的服务管理、服务间调用的问题。对于如何“微”才算是足够的“微”,是没有标准的衡量计算方法的。

    微服务不是说越小越好。服务越小,微服务架构的优点和缺点也就会越来越明显。服务越小,微服务的独立性就越高,但同时微服务的数量也会增加,管理就会存在很大的问题,成为一个新的挑战,这也就是常常所被提到的“这么多的服务,该服务管理啊?”问题。

    服务的拆分足够微,可以按照某种方式、规则拆分,通常可以按照业务模块、业务场景等进行拆分,尽量避免服务间的相互依赖,做到高内聚低耦合。紧密关联的处理,放在一个服务内,但避免在服务与服务之间共享数据。

2.轻量级通信

      在单体架构的应用中,可直接通过简单的方法调用就能进行通信,但在微服务架构中,由于服务都是跨域进程,甚至是跨主机的,组件只能通过REST、Web服务或RPC类似的机制在网络上进行通信。

      因为服务划分的已经足够小了,服务间的通信可能会比较频繁,考虑到性能、响应时间等方面,则服务间通信应采用轻量级的通信协议,如:同步的REST,异步的AMQP、STOMP、MQTT等。在实时性要求不高的场景下,采用REST通信是不错的选择,REST是基于HTTP协议,可方便进行跨域访问或跨防火墙的设置,并且消息格式可以统一为XML或JSON格式,方便开发人员阅读和理解。如果服务间通信比较频繁,有比较高的要求,可采用消息通信的方式,如:Kafka、MQ等类似的消息中间件。如果不考虑对外提供访问的话,可采用gRPC的通信方式,因为gRPC是基于Netty的,通信效率更高。

3.单一职责原则

     当服务粒度过粗时,服务内部很容易产生耦合。如果多人开发同一个服务,很容易因为耦合过大造成代码修改重合,不利于后期维护。确保每个服务职责单一,这也是用来确定服务拆分边界的一个原则,遵循“高内聚、低耦合”。

    必须要对自己的产品和业务的了解,才能更准确的确定服务边界,让各个服务满足单一的业务职责,避免职责交叉。

4.领域驱动原则

     领域驱动设计(Domain Driven Design),是一套综合软件系统分析和设计的面向对象建模方法。一个微服务,就应该能够反映出某个业务的领域模型,使用领域驱动设计,不但可以降低微服务环境中通用语言的复杂度,而且可以帮助团队搞清楚领域的边界,理清上下文边界。

    建议将每个微服务都设计成一个DDD限界上下文,为微服务提供了一个逻辑边界。每个独立的团队负责一个逻辑上定义好的系统切片,负责与一个领域或业务功能相关的全部开发,最终团队开发出的代码更加易于理解和维护。

(关于DDD更详细的了解,可参考https://www.cnblogs.com/edisonchou/p/edc_ddd_foundation_study_part1.html)

 

三、微服务架构的核心话题

      基于微服务架构的应用,将面临着许多选择、争议等讨论的核心话题,这些核心话题将会在你接下来的微服务架构生涯里不断出现,并成为讨论的焦点。在此,我觉得有必要进行汇总整理,让你觉得它存在的必要性,能为你之所用。

1.服务拆分

       服务拆分首先关注的就是服务的颗粒度,可遵循设计原则——拆分足够微,通过DDD(领域驱动设计)的指导,将某个领域的功能进行聚合成为一个服务。

       对于一个大型复杂的单体应用而言,选择先拆分哪个模块,是一个问题。一般考虑先从容易、简单被拆分的模块开始,在拆分简单模块过程中,不断积累微服务的经验,逐步拆分掉复杂、繁重业务的核心模块。同时,寻找那些和其他功能业务重合度低、耦合度低,且自身变化较为缓慢的基础服务,将它们拆分为微服务。

      决定了拆分哪些模块,要拆分成多个微服务后,接下来就要划清服务拆分的边界,将服务边界和接口顺理清楚。确定哪些应该包含进来,哪些不应该包含进来,哪些接口需要重新设计,哪些可以重复利用。

      如下图所示,展示了一个单体应用拆分为多个微服务的过程。一旦拆分完后,各个服务就可以独立开发、部署和扩展。

     服务的拆分,不单单指功能的拆分,如上图所示,还得考虑数据库的拆分 ,确保降低功能逻辑层、数据访问层的耦合度。

2.服务注册与发现

      微服务架构的特点是服务的数量众多,这些众多的服务需要一个统一的服务注册平台来进行服务的管理。每个微服务实例在启动后,将自己的实例信息注册到服务注册表或服务注册中心。服务的调用方若想获取可用服务实例的列表,则需要从服务注册表中去获取相关的信息。

      当服务实例失效或down掉以后,服务实例的信息就要从服务注册表中移除,即:服务注销。服务注册表是用于维护所有可用的服务实例的地方,服务注册表一方面要接收微服务实例的接入,另一方面当服务实例不可用时,需要及时将服务实例从注册表中清楚。下图展示了服务注册与服务实例的关系。

       服务注册与发现组件或框架,有很多,如:Eureka、Consul、etcd等,都提供了服务注册表的功能,可供大家进行选择。

3.负载均衡

       在微服务架构中,负载均衡是必须使用的技术,通过它来实现系统的高可用、集群扩容等功能。负载均衡通常分为两种:服务端负载均衡和客户端负载均衡。通常所说的负载均衡均指服务器端的负载均衡,可通过软件或硬件设备来实现,软件如:Nginx、LVS等,硬件如:F5、A10等,硬件负载均衡设备成本较高,大部分采用的是软件方式。架构图如下:

       通过软件或硬件实现负载均衡都会维护一个服务端清单,利用心跳检测等手段进行清单维护,保证清单中都是可以正常访问的服务节点。当用户发送请求时,会先到达负载均衡器(一般作为一个服务),负载均衡器根据负载均衡算法(轮训、随机、加权轮训)从可用的服务端列表中取出一台服务端的地址,接着进行转发,降低系统的压力。

4.API网关

       考虑到微服务架构中服务的数量很多,为了便于服务对外统一的管理,API网关的引入是必不可少的。API网关旨在提供统一的API入口点,来管理多个服务内部API,可方便实现对平台众多服务接口进行管控,如对访问服务的身份认证、业务鉴权、流量并发控制、API调用的计量或计费等。

      API网关常用于以下场景:

黑白名单:实现例如通过IP地址来禁止访问某些服务的某些功能。日志:实现日志访问的记录,用于分析访问、处理性能指标,并将分析结果提供给其他模块使用,如:运维平台的统计功能。协议适配:实现通信协议校验、适配转换的功能。身份认证:负责外部系统的访问身份认证。计流限流:实现微服务访问流量计算,基于流量计算分析进行限流等。路由:API网关的核心功能,实现请求的转发。

     API网关的引入为微服务架构应用带来诸多的好处,如下:

避免将内部信息/接口泄露给外部: 能够将对外发布的API与微服务内部的API区分开来,使得各个微服务在添加或变更时,能有明确的安全边界,避免多过的对外暴露。为微服务添加额外的安全层:能够提供一套额外的保护层,用以应对SQL注入、Dos攻击等,其中系统的权限控制可以再这一层来实施。支持多种混合通信协议:考虑到微服务架构中,各个微服务的平台与语言的多样性,通常将对外提供基于HTTP或REST的API接口,而内部微服务将根据自身服务情况采用不同的通信协议(如:ProtoBuf、RPC等)。API网关则跨域这些内部不同协议的微服务,提供一个基于REST的统一外部API。减低构建微服务的复杂性:基于微服务架构应用的复杂性,如API令牌、访问控制、限速限流等,每一项功能的添加,对会额外对各个服务带来影响,从而影响微服务的开发周期。这些功能如果在API网关上统一处理,则会从代码层面进行了有效的隔离,使得不会影响其他微服务,这样更有利于其他微服务只需关注于实际的业务开发。

    常见的API网关实现方式很多,如:Nginx、Kong、Spring Cloud Zuul、Træfɪk等。

5.服务部署与发布

     单体应用被拆分为微服务后,随着微服务的数量增多,部署就成了问题,使得部署的复杂性提高了不少。所以,微服务的部署更加倾向于使用具有相互之间隔离的主机/虚拟机来实现服务的部署,使得服务能够独立的部署、测试、发布、升级。

    目前比较好的服务部署方式就是把各个微服务打包成Docker镜像,这样就保障避免了不同主机环境对部署产生的影响。使用Docker部署,并结合Jenkins进行CI/CD,使得构建、发布、启动变得更加快捷。

    下图就是服务部署、发布流程。

 

四、总结

       一个微服务架构的应用,从最初的设计到逐步成型,是需要经过不断的迭代开发、摸索来完善的,上述只是列举出了我个人认为需要重点关注的点,以供大家参考,如有遗漏不足,望大家建议补充、完善。

 

欢迎微信扫码下面二维码,关注微信公众号【程序猿技术大咖】,进行更多交流学习!

xcbeyond 认证博客专家 架构 Spring Boot Java 疯狂源自梦想,技术成就辉煌!微信公众号:《程序猿技术大咖》号主,专注后端开发多年,拥有丰富的研发经验,乐于技术输出、分享,现阶段从事微服务架构项目的研发工作,涉及架构设计、技术选型、业务研发等工作。对于Java、微服务、数据库、Docker有深入了解,并有大量的调优经验。
最新回复(0)