微服务

微服务最初是由 Martin Fowler 于 2014 年发表的论文 《MicroServices》 中提出的名词。
  • 所谓 服务,指的是项目中的功能模块,它可以帮助用户解决某一个或一组问题,在开发过程中表现为 IDE(集成开发环境,例如 Eclipse 或 IntelliJ IDEA)中的一个工程或 Moudle。
  • 微小 强调的是单个服务的服务体积小,复杂度低。一个微服务通常只提供单个业务功能的服务,即一个微服务只专注于做好一件事,因此微服务通常代码较少,体积较小,复杂度也较低。

微服务架构

微服务架构是一种系统架构的设计风格。与传统的单体式架构(ALL IN ONE)不同,微服务架构提倡将一个单一的应用程序拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间使用轻量级通信机制(通常是 HTTP RESTFUL API)进行通讯。
通常情况下,这些小型服务都是围绕着某个特定的业务进行构建的,每一个服务只专注于完成一项任务并把它做好 ,即“专业的人做专业的事”。
每个服务都能够独立地部署到各种环境中,例如开发环境、测试环境和生产环境等,每个服务都能独立启动或销毁而不会对其他服务造成影响。
这些服务之间的交互是使用标准的通讯技术进行的,因此不同的服务可以使用不同数据存储技术,甚至使用不同的编程语言。在当今的软件开发领域中,主要有两种系统架构风格,那就是新兴的 微服务架构** 和传统的 单体架构**
单体架构是微服务架构出现之前业界最经典的软件架构类型,许多早期的项目采用的也都是单体架构。单体架构将应用程序中所有业务逻辑都编写在同一个工程中,最终经过编译、打包,部署在一台服务器上运行。
在项目的初期,单体架构无论是在开发速度还是运维难度上都具有明显的优势。但随着业务复杂度的不断提高,单体架构的许多弊端也逐渐凸显出来,主要体现在以下 3 个方面:
  • 随着业务复杂度的提高,单体应用(采用单体架构的应用程序)的代码量也越来越大,导致代码的可读性、可维护性以及扩展性下降。
  • 随着用户越来越多,程序所承受的并发越来越高,而单体应用处理高并发的能力有限。
  • 单体应用将所有的业务都集中在同一个工程中,修改或增加业务都可能会对其他业务造成一定的影响,导致测试难度增加。
由于单体架构存在这些弊端,因此许多公司和组织都开始将将它们的项目从单体架构向微服务架构转型。

微服务的特点

优点:

  • 服务按照业务来划分,每个服务通常只专注于某一个特定的业务、所需代码量小,复杂度低、易于维护。
  • 每个微服都可以独立开发、部署和运行,且代码量较少,因此启动和运行速度较快。
  • 采用单体架构的应用程序只要有任何修改,就需要重新部署整个应用才能生效,而微服务则完美地解决了这一问题。在微服架构中,某个微服务修改后,只需要重新部署这个服务即可,而不需要重新部署整个应用程序。
  • 在微服务架构中,开发人员可以结合项目业务及团队的特点,合理地选择语言和工具进行开发和部署,不同的微服务可以使用不同的语言和工具。
  • 微服务具备良好的可扩展性。随着业务的不断增加,微服务的体积和代码量都会急剧膨胀,此时我们可以根据业务将微服务再次进行拆分;除此之外,当用户量和并发量的增加时,我们还可以将微服务集群化部署,从而增加系统的负载能力。
  • 微服务能够与容器(Docker)配合使用,实现快速迭代、快速构建、快速部署。
  • 微服务具有良好的故障隔离能力,当应用程序中的某个微服发生故障时,该故障会被隔离在当前服务中,而不会波及到其他微服务造成整个系统的瘫痪。
  • 微服务系统具有链路追踪的能力。

缺点:

  • 分布式部署,调用的复杂性高:单体应用的时候,所有模块之前的调用都是在本地进行的,在微服务中,每个模块都是独立部署的,通过 HTTP 来进行通信,这当中会产生很多问题,比如网络问题、容错问题、调用关系等。
  • 独立的数据库,分布式事务的挑战:每个微服务都有自己的数据库,这就是所谓的去中心化的数据管理。这种模式的优点在于不同的服务,可以选择适合自身业务的数据,比如订单服务可以用 MySQL、评论服务可以用 MongoDB、商品搜索服务可以用 Elasticsearch。
  • 测试的难度提升:服务和服务之间通过接口来交互,当接口有改变的时候,对所有的调用方都是有影响的,这时自动化测试就显得非常重要了,如果要靠人工一个个接口去测试,那工作量就太大了。这里要强调一点,就是 API 文档的管理尤为重要。
  • 运维难度的提升:在采用传统的单体应用时,我们可能只需要关注一个 Tomcat 的集群、一个 MySQL 的集群就可以了,但这在微服务架构下是行不通的。当业务增加时,服务也将越来越多,服务的部署、监控将变得非常复杂,这个时候对于运维的要求就高了。

SpringCloud

Spring Cloud 是分布式微服务架构的一站式解决方案,它并不是某一门技术,它是多种微服务架构落地技术的集合体,俗称微服务全家桶。它将市面上成熟的、经过验证的微服务框架整合起来,并通过 Spring Boot 的思想进行再封装,屏蔽调其中复杂的配置和实现原理,最终为开发人员提供了一套简单易懂、易部署和易维护的分布式系统开发工具包。

Spring Cloud 并不是一个拿来即可用的框架,它是一种微服务规范,有以下 2 代实现:
  • 第一代实现:Spring Cloud Netflix
  • 第二代实现:Spring Cloud Alibaba
Spring Cloud 特指 Spring Cloud Netflix 第一代实现。Spring Cloud Netflix 的常用组件如下:
Spring Cloud 组件 描述
Spring Cloud Netflix Eureka Spring Cloud Netflix 中的服务治理组件,包含服务注册中心、服务注册与发现机制的实现。
Spring Cloud Netflix Ribbon Spring Cloud Netflix 中的服务调用和客户端负载均衡组件。
Spring Cloud Netflix Hystrix 人称“豪猪哥”,Spring Cloud Netflix 的容错管理组件,为服务中出现的延迟和故障提供强大的容错能力。
Spring Cloud Netflix Feign 基于 Ribbon 和 Hystrix 的声明式服务调用组件。
Spring Cloud Netflix Zuul Spring Cloud Netflix 中的网关组件,提供了智能路由、访问过滤等功能。
Spring Cloud Gateway 一个基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关框架,它使用 Filter 链的方式提供了网关的基本功能,例如安全、监控/指标和限流等。
Spring Cloud Config Spring Cloud 的配置管理工具,支持使用 Git 存储配置内容,实现应用配置的外部化存储,并支持在客户端对配置进行刷新、加密、解密等操作。
Spring Cloud Bus Spring Cloud 的事件和消息总线,主要用于在集群中传播事件或状态变化,以触发后续的处理,例如动态刷新配置。
Spring Cloud Stream Spring Cloud 的消息中间件组件,它集成了 Apache Kafka 和 RabbitMQ 等消息中间件,并通过定义绑定器作为中间层,完美地实现了应用程序与消息中间件之间的隔离。通过向应用程序暴露统一的 Channel 通道,使得应用程序不需要再考虑各种不同的消息中间件实现,就能轻松地发送和接收消息。
Spring Cloud Sleuth Spring Cloud 分布式链路跟踪组件,能够完美的整合 Twitter 的 Zipkin。
随着 Spring Cloud 版本的升级,很多东西会被弃用或者更换为其它的具体技术支持,我们可以在 Spring Cloud 中文网查看 Spring Cloud 中的常用组件信息。

SpringCloud命名

老版本命名

为了避免 Spring Cloud 的版本号与其子项目的版本号混淆,Spring Cloud 没有采用常见的数字版本号,而是通过以下方式定义
  1. {version.name} .{version.number}
Spring Cloud 版本信息说明如下:
  • version.name版本名,采用英国伦敦地铁站的站名来命名,并按照字母表的顺序(即从 A 到 Z)来对应 Spring Cloud 的版本发布顺序,例如第一个版本为 Angel,第二个版本为 Brixton(英国地名),然后依次是 <font style="color:rgb(68, 68, 68);">Camden、Dalston、Edgware、Finchley、Greenwich、Hoxton</font> 等。
  • version.number:版本号,每一个版本的 Spring Cloud 在更新内容积累到一定的量级或有重大 BUG 修复时,就会发布一个<font style="color:rgb(68, 68, 68);">service releases</font>版本,简称 <font style="color:rgb(68, 68, 68);">SRX</font> 版本,其中 <font style="color:rgb(68, 68, 68);">X</font> 为一个递增的数字,例如 Hoxton.SR8 就表示 Hoxton 的第 8 个 Release 版本。

新版本命名

在2020年4月17日官方就发布了 <font style="color:rgb(0, 0, 0);">Spring Cloud 2020.0.0-M1</font>,可能是考虑到Spring Cloud发展太迅速地铁站名不够用😂,另外用日期命名也可读性更高。

从发布信息可以看出,从 Spring Cloud 2020.0.0-M1 开始,Spring Cloud将不再使用英国伦敦地铁站的命名方式,而使用了全新的 “日历化”(Calendar Versioning或简称CalVer) 版本命名方式。

Spring Cloud版本号将使用了 <font style="color:black;">YYYY.MINOR.MICRO</font>的命名规则,其中:
  • YYYY:代表4 位年份
  • MINOR:表示一个每年以 0 开始递增的数字
  • MICRO:表示版本号的后缀, <font style="color:rgb(68, 68, 68);">.0</font> 类似于 <font style="color:rgb(68, 68, 68);">.RELEASE</font> 一样,<font style="color:rgb(68, 68, 68);">.2</font> 类似于 <font style="color:rgb(68, 68, 68);">.SR2</font>
  • 预发布版本的后缀分隔符也由<font style="color:rgb(68, 68, 68);">.</font>变为 <font style="color:rgb(68, 68, 68);">-</font>,如:<font style="color:rgb(68, 68, 68);">2020.0.0-M1</font><font style="color:rgb(68, 68, 68);">2020.0.0-RC2</font> 命名所示

版本选择

用 Spring Boot + Spring Cloud 进行微服务开发时,我们需要根据项目中 Spring Boot 的版本来决定 Spring Cloud 版本,否则会出现许多意想不到的错误。 Spring Boot 与 Spring Cloud 的版本对应关系如下表(参考自 Spring Cloud 官网
<font style="color:rgb(51, 51, 51);">Cloud </font>Version <font style="color:rgb(51, 51, 51);">Boot</font> Version
2021.0.x aka Jubilee 2.6.x, 2.7.x (Starting with 2021.0.3)
2020.0.x aka Ilford 2.4.x, 2.5.x (Starting with 2020.0.3)
Hoxton 2.2.x, 2.3.x (Starting with SR5)
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x
注意:Spring Cloud 官方已经停止对 Dalston、Edgware、Finchley 和 Greenwich 的版本更新。

查看官网,现在正在支持 CURRENT&GA 的版本是 <font style="color:rgb(51, 51, 51);">2021.0.4</font>(旧版的叫法都是伦敦地铁站名例如 Hoxton霍克斯顿,简称 H 版,2020年年底发布的新版,就是以年份开头的)

微服务概念 - 图1

点击查看 参考文档(Reference Doc) 可以看到版本信息

Spring Cloud 版本: 2021.0.4 Spring Boot 版本: 2.6.11

查看详细兼容版本地址

地址:https://start.spring.io/actuator/info

微服务概念 - 图2

请求地址,对json格式化后可以看出,Spring Cloud 2021.0.4对应Spring Boot >=2.6.1 and <3.0.0-M1

Spring Cloud Alibaba 版本对账官方参考:

版本说明 · alibaba/spring-cloud-alibaba Wiki

父项目搭建

我们先来了解下父工程是什么意思?

我们通过上方描述,可以得知微服务项目,会有很多个小服务,都是可以独立运行的。那么实际开发中我们通常会创建一个父工程,然后父工程中创建一个个的子模块,然后进行必要的配置,以及引入及处理依赖。(管理依赖很重要)

父项目创建步骤

  1. New Project - maven工程
  2. 删除不需要的文件。只保留 pom.xml
  3. 修改 pom.xml

packaging标签选择 pom而不是 jar或者 war,因为这是父工程,这个位置一般也不写代码,只是聚合(子)工程或者传递依赖使用

  1. <packaging>pom</packaging>
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <!-- 聚合工程或者传递依赖使用-->
  7. <packaging>pom</packaging>
  8. <groupId>org.chen</groupId>
  9. <artifactId>spring-cloud-demo</artifactId>
  10. <version>1.0-SNAPSHOT</version>
  11. <!-- 统一管理依赖版本-->
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. <junit.version>4.13.1</junit.version>
  16. <mysql.version>8.0.29</mysql.version>
  17. <!-- 下面这两个的兼容问题要注意 -->
  18. <spring.boot.version>2.6.11</spring.boot.version>
  19. <spring.cloud.version>2021.0.4</spring.cloud.version>
  20. </properties>
  21. <!--1. dependencyManagement的作用相当于一个对所依赖jar包进行版本管理的管理器
  22. 2. 子模块继承之后,作用:锁定版本 + 子module 不用声明groupId和version -->
  23. <dependencyManagement>
  24. <dependencies>
  25. <!--spring boot-->
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-dependencies</artifactId>
  29. <version>${spring.boot.version}</version>
  30. <type>pom</type>
  31. <scope>import</scope>
  32. </dependency>
  33. <!--spring cloud -->
  34. <dependency>
  35. <groupId>org.springframework.cloud</groupId>
  36. <artifactId>spring-cloud-dependencies</artifactId>
  37. <version>${spring.cloud.version}</version>
  38. <type>pom</type>
  39. <scope>import</scope>
  40. </dependency>
  41. <!-- junit -->
  42. <dependency>
  43. <groupId>junit</groupId>
  44. <artifactId>junit</artifactId>
  45. <version>${junit.version}</version>
  46. </dependency>
  47. <!--mysql-->
  48. <dependency>
  49. <groupId>mysql</groupId>
  50. <artifactId>mysql-connector-java</artifactId>
  51. <version>${mysql.version}</version>
  52. </dependency>
  53. </dependencies>
  54. </dependencyManagement>
  55. <build>
  56. <plugins>
  57. <plugin>
  58. <groupId>org.springframework.boot</groupId>
  59. <artifactId>spring-boot-maven-plugin</artifactId>
  60. <configuration>
  61. <fork>true</fork>
  62. <addResources>true</addResources>
  63. </configuration>
  64. </plugin>
  65. </plugins>
  66. </build>
  67. </project>

DependencyManagement 的意义

在父类的 pom 中,你可以观察到,即使你写了这么多的依赖,但是本地却没有加载任何 jar 包,好像就是个摆设,如果单单看父工程的话,这话也没大毛病,但是作为一个拥有多个子模块的父工程来说,它就很有意义了。

Maven 使用 dependencyManagement 元素来提供了一种管理依赖版本号的方式。

通常会在一个组织或者项目的最顶层的父 POM 中看到 dependencyManagement 元素。

使用 pom.xml 中的 dependencyManagement 元素能让所有在子项目中引用依赖而不用显式的列出版本号。Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement 元素的项目,然后它就会使用这个 dependencyManagement 元素中指定的版本号。

<dependencyManagement>
  <dependencies>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

好处就是:如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改;另外如果某个子项目需要另外的一个版本,只需要声明version就可。

  1. 如果不在子项目中声明依赖,不会从父项目中继承下来的。
  2. 只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且 version 和 scope 都读取自父pom。
  3. 如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。