SpringCloud微服务简介
SpringCloud是基于Spring平台构建的一个能够快速搭建分布式微服务的全套框架,SpringCloud整合了许多可替换的开源组件,并基于SpringBoot提供了友好的起步依赖封装,能够轻松实现服务发现、断路器、消息总线、配置中心、链路跟踪等功能。SpringCloud是目前搭建微服务工程的首选框架之一。
https://spring.io/projects/spring-cloud
微服务架构简介
微服务架构适用场景
微服务架构是分布式系统,分布式系统会引入不可避免的复杂性,如非必要否则不应选用。微服务架构适用于团队规模大、业务复杂度高且可拆分为子模块的场景,绝不适用于单人全栈开发等简单场景。
同类技术对比
Spring是一个“胶水”框架,许多其它开源或商业组件都会尽力向Spring靠拢,SpringCloud本身也是吸收了众多来自不同团队的组件而构成的一个大而全的“全家桶”式平台,其实也没有什么同级别技术可对比,SpringCloud就是最简单、成本最低的微服务解决方案,我们自研的组件也应向Spring框架靠拢。
因此,很多云厂商推出的中间件选择主动和SpringCloud体系进行集成,比如SpringCloudAlibaba等,其中包含的一些组件功能和官方组件有重叠,另一些组件则可能是独有的。具体技术选型时,我们可以根据自己的需要和具体部署的云平台进行选择。
除了SpringCloud,搭建微服务工程的另一种方式是自行选择组合开源组件。这种开发模式在微服务架构的早期阶段比较常见,然而由于配置复杂、依赖管理复杂等原因,会在业务代码之外的技术上耗费大量精力,除非有专门的架构团队造了一个和SpringCloud类似的轮子,否则可以说没有这样做的必要。毕竟付出了如此多的额外精力,为什么不直接用SpringCloud呢?
此外,现在容器化部署和Kubernetes等容器编排技术越来越成为主流,熟悉Kubernetes我们就会发现实际上k8s和SpringCloud在很多方面有重叠,比如服务发现(Service)、配置中心(ConfigMap)等。最近Spring推出了SpringCloud Kubernetes项目看起来很值得我们持续关注,该项目试图利用k8s提供的基础设施功能将其接入SpringCloud体系,替换掉SpringCloud中和Kubernetes的重叠功能。
传统微服务和服务网格
SpringCloud属于「传统微服务」,而最新一代的微服务架构是「Service Mesh」,即服务网格。服务网格中整个服务治理功能都下沉到了基础设施,整个服务程序只需要包含最基本的HTTP通信功能即可,业务代码中几乎是100%的业务逻辑,再也不需要引入服务治理相关的依赖和配置了,我们的工程也完全可以脱离开臃肿的SpringCloud体系,只使用SpringBoot甚至混合使用不同的框架、语言来开发不同的微服务,从架构上来看这无疑更加优美。
然而目前来看,服务网格还处于快速发展阶段,技术可以说还并不成熟,可借鉴的资料不多。服务网格要求容器化部署,有资源要求高、代理链路过长、延迟高、性能差等诸多缺点,最重要的是落地成本极高,不仅基础设施要额外耗费不少计算、存储资源,还需要有专门架构团队负责基础设施的规划,私有云部署更是需要专业的高级运维团队协助搭建和维护,否则是很难投产使用的。新项目如此,更不用说老项目迁移了。不过从长远来看,随着相关的框架和各个公有云平台的发力,服务网格还是很有机会吃下一部分传统微服务份额的。
有关服务网格的介绍可以参考istio
相关章节。
服务拆分原则
微服务拆分遵循一定原则,如果初期没有规划好,中后期很有可能造成架构混乱和性能瓶颈。服务拆分是极为重要的,因为开发团队水平通常是参差不齐的,如果没有规划出一个猴子都能看懂的拆分规则,后期整个微服务系统就会积重难返。
永不重构(商业项目):如果你正考虑将公司的单体应用或传统SOA架构应用重构为微服务方式,或是从一个上古时期的微服务架构迁移到最新版本SpringCloud,除非存在不可抗因素,否则立刻放弃这种不切实际的想法!你应该要求团队产品经理重新设计、推倒原来的工程重新开发,商业项目的重构是绝不可能做到的,因为重构的人力成本通常比重新开发更高,而且风险不可控!
高内聚 低耦合:服务要尽量按单一职责拆分,一个服务专注做好一件事。通常无法用一个名词描述的服务可能就不是一个高内聚低耦合的拆分。
正交性:服务之间互相独立,服务间依赖越小越好,一个服务的迁移、替换、重构,对其他服务影响应尽可能小,如果无法做到这一点,你只是写了一个巨大的“分布式单体应用”而已,它除了性能比原来还差以外没有什么优势。
避免业务逻辑调用链过深:一个业务功能的调用链深度如果非常深,就要考虑拆分的合理性了。例如电子商城系统,我们应将订单管理和支付功能进行拆分,而非将下单的几十个流程拆分。
避免循环依赖:服务间避免形成循环依赖,无论开发什么东西循环依赖永远是个复杂的问题,而且通常表明架构设计的不合理性。
设计通用接口:通用的服务间通信方案使得我们服务的可插拔性更强,也更便于通信链路上的调试和日志收集等操作,例如JSON和ProtoBuf支持大多数主流语言,是好的方案,而RMI仅用于Java,是个相对不好的方案。接口的格式也要尽量遵循统一的报文结构,比如状态码、错误信息,数据的字段名最好相同。除了微服务之间,服务治理的中间件选择也应如此。
数据库也应考虑拆分:多个微服务使用同一个数据库又会在数据库这一层造成强耦合性,数据表和服务会产生复杂的多对多关系,如果一个服务需要替换、迁移,你还要耗费精力从一个巨大的数据库中拆出该服务使用的数据表(这通常不可能)。大多数应用的瓶颈都在数据库这一层,使用单独的一个数据库也不利于这一层的伸缩和扩容。
代码库拆分最佳实践
微服务工程通常有两种代码库组织形式:
MonoRepo:单代码仓库,不同的工程按Maven模块进行划分
MultiRepo:每个微服务采用一个自己的代码库
实际上两种组织形式均可选择,第一种从日常开发来说操作十分方便,因为只需要操作一个代码库就行了;后者操作麻烦,一个需求开发可能涉及操作多个代码库,会成倍的拖慢开发效率,不过其优点是可以保持每个代码库的规模不至于过大,避免人员操作冲突、IDE卡顿、持续集成服务器上下载太慢等问题。
如果服务数量不多(7个左右),强烈建议采用第一种方式;而服务数量更多或者代码规模过大,那么我们应该首先考虑是不是存在微服务划分不合理等问题,如果实在不能将工程缩减到一个精简的规模,就不得不采用第二种方式了。
SpringCloud环境搭建
SpringCloud框架本身是基于SpringBoot的,不过这里要注意的是SpringCloud工程引入了一套单独的依赖管理,我们需要在pom.xml
文件中添加如下内容:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
其中spring-cloud.version
是在<properties>
标签中进行配置的,具体版本选择兼容SpringBoot版本的当前最新版本即可。引入上述依赖配置文件后,我们就可以增加其它SpringCloud的起步依赖了。
我这里使用的版本是SpringBoot2.7.18和SpringCloud2021.0.9。