SpringCloud01:分布式基础

  1. 项目架构的演进
    1. 单体架构(all in one)
    2. 集群架构(Cluster)
    3. 分布式架构(Distributed)
  2. 创建微服务项目
    1. 项目结构
    2. 创建项目(父工程)
    3. 创建Module

项目架构的演进

单体架构(all in one)

所谓单体结构就是将所有功能模块放在同一个项目中。在项目打包的时候只需要把项目打包起来,放在一个服务器里面,另外在用一台服务器部署数据库即可。一个服务器,我们称之为一个节点

单体机构的优缺点:

  • 开发和部署比较简单
  • 无法应对高并发

由于一台服务器的性能有限,如果一台服务器每秒只能处理200个请求,但是如果每秒进入5000个请求,那么一台服务器的性能就不够用了。

为了解决高并发问题,我们需要引入集群架构

集群架构(Cluster)

由于一台服务器的性能有限,那么如果我们都复制几台同样的服务器不就可以解决性能问题了吗?

这样的复制品,我们称之为副本

  • 主从复制:一个主副本负责写入,多个从副本负责读取
  • 多主复制:多个副本都可以接受写入
  • 无主复制:所有副本地位平等,通过一致性协议协调

一群服务器在一起工作我们称之为集群。

用户通过网址需要指向那个服务器呢?

这个时候我们需要网关(比如Nginx),这个网关也会单独部署在一个服务器里面。用户访问Nginx的公网的公网IP,网关并不处理业务请求,主要功能是把请求转发给处理业务的服务器。转发请求的过程,我们称为路由。如何在服务器之间分配请求,这个算法称之为负载均衡。因此,网关是请求流量的入口

常⻅的负载均衡算法:

  • 轮询:为第⼀个请求选择健康池中的第⼀个后端服务器,然后按顺序往后依次选择,直到最后⼀个,然后循环。
  • 最⼩连接:优先选择连接数最少,也就是压⼒最⼩的后端服务器,在会话较⻓的情况下可以考虑采取这种⽅式。
  • 散列:根据请求源的 IP 的散列(hash)来选择要转发的服务器。这种⽅式可以⼀定程度上保证特定⽤户能连接到相同的服务器。如果你的应⽤需要处理状态⽽要求⽤户能连接到和之前相同的服务器,可以考虑采取这种⽅式。

当然,对应数据库,我们也可以做集群处理。

集群的优缺点:

  • 解决大并发问题
  • 如果未来流量继续增大,可以对服务器进行扩容(多复制几个副本);流量减少则可以缩容(减少几个副本)。
  • 模块化升级会导致牵一发而动全身:比如我们需要对订单模块进行升级,那么所涉及的所有模块都需要重新部署
  • 多语言团队:比如,我们需要上线一个直播模块,但是Java在流媒体处理这块不是其专长,那么这个时候我们就需要使用C++这个语言。这个时候,C++这个直播模块就不能和Java放在同一个项目里打成jar包发布。

分布式架构(Distributed)

为了解决模块化升级和多语言团队的问题,我们可以使用分布式架构

现在可以把整个应用根据业务功能模块进行拆分,把⼀个单独的应⽤程序开发为⼀套⼩服务(微服务),每个⼩服务运⾏在⾃⼰的进程。这些应用可以自己独立部署。

不仅应用可以拆分,数据库也可以拆分。商品的数据放在商品库,订单的数据放在订单库。

这样,每一个微服务只需要连接自己对应的数据库,如果订单系统需要商品的数据,那么订单系统就需要向商品系统发送请求,获取订单的数据。这样就做到了数据隔离。同时,每一个微服务都可以使用不同的语言来写。这些特征充分体现了微服务自治的特点。

这个时候,我们的部署架构就不再是一个服务器单独部署完整的商城应用,而是部署商城拆分出来的每一个微服务

如果商品的访问量比较大,就多部署几台服务器,如果物流的访问量比较少,就少部署几台服务器。

在上面的架构中,我们没有吧所有的副本都放在同一个服务器里面。比如我们没有把所有的商品的微服务都放在同一个0.5的服务器里面,这样是为了避免单点故障。也就如果把所有的商品微服务放在同一个服务器下面,如果该服务器出现问题,那么用户就无法访问商品服务了。

但是这样也会出现问题。如果用户想查询自己的订单,但是该订单服务在其他服务器里面,这个时候,用户服务就需要发送一个远程调用(HTTP请求),让订单返回订单数据(Json数据)。

远程调用的全称为:Remote Procedure Call,也即RPC。HTTP+Json只是RPC的一种方式。

但是用户服务如何知道订单服务在0.5这个服务器呢?再比如,如果用户服务想访问物流服务,假设访问的是0.6的服务器,如果0.6服务器的物流服务出现了问题导致无法访问,用户服务如何知道在0.5的服务器还有一个物流服务器呢?

为了应对以上问题,我们需要依赖两个机制:服务发现服务注册

这个时候我们需要引入注册中心这个概念

  • 首先,微服务在上线或者下线的时候,会把服务的IP地址给到注册中心,或者注册中心有能力获取微服务的IP地址。获得各个微服务IP之后,会生成一个服务IP清单列表,里面记录了各个微服务的地址。这个过程叫做服务注册
  • 接着,如果用户服务需要调用订单服务,需要先访问注册中心,知道订单服务在哪儿,然后再去访问订单服务。这个过程叫作服务发现。如果有多个服务,比如物流服务,那么注册中心可以使用负载均衡技术,决定让用户服务访问哪个微服务。
  • 一般,注册中心也同时作为配置中心使用。在过去,我们一般把配置写死在配置文件里面,如果配置一旦发生变化,我们就需要修改配置后重新打包上线。但是现在,我们可以把各个微服务配置写在配置中心,由配置中心统一管理。如果配置需要修改,我们在配置中心修改之后,配置中心会把变更的配置推送给对应的微服务。

服务雪崩:一个服务(库存服务)的故障通过依赖关系(调用链)扩散到整个系统。

  1. 某天大促销,流量激增
  2. 库存服务因数据库压力大开始变慢
  3. 支付服务调用库存服务时大量请求被阻塞,线程占满
  4. 支付服务无法响应订单服务的调用
  5. 订单服务也开始阻塞,线程占满
  6. 最终整个商城系统瘫痪

为了解决服务雪崩的问题,我们需要服务熔断:所谓服务熔断就是快速失败。比如上面支付系统调用库存系统的时候,设置5秒的熔断机制,也就是如果库存系统5秒内没有返回数据,那种这个请求就会返回失败,这样就可以尽快释放资源,防范服务雪崩。

区别分布式与集群的概念:

  • 分布式是把一个大型应用分别拆分成多个小应用分别部署在各个机器上
  • 集群是一个物理形态。集群指的是将⼏台服务器集中在⼀起,实现同⼀业务。例如:京东是⼀个分布式系统,众多业务运⾏在不同的机器,所有业务构成⼀个⼤型的业务集群。每⼀个⼩的业务,⽐如⽤户系统,访问压⼒⼤的时候⼀台服务器是不够的。我们就应该将⽤户系统部署到多个服务器,也就是每⼀个业务系统也可以做集群化
  • 分布式中的每⼀个节点,都可以做集群。 ⽽集群并不⼀定就是分布式的。
  • 节点:集群中的⼀个服务器

用户要如何访问分布式应用呢?网关

用户需要访问网关所在的服务器的公网IP,然后又网关通过请求路由访问对应的微服务。比如,设定规则以/order/xxx 为路径的请求访问的是订单微服务。那么订单微服务可能分布在多个服务器上,那么网关怎么知道订单微服务的地址呢?这个时候网关需要先从注册中心获取订单微服务的地址,才能访问到订单服务。

分布式事务:有这么一个场景,用户下订单,下完订单之后需要增加积分。用户下订单涉及到订单数据库,增加积分涉及到用户数据库。用户数据库和订单数据库可能会在不同的服务器里面,这个时候如果实现事务的回滚呢?这就涉及分布式事务

总结我们使用的技术:

  • 微服务:SpringBoot
  • 注册中心和配置中心:Spring Cloud Alibaba Nacos
  • 网关:Spring Cloud Gateway
  • 远程调用:Spring Cloud OpenFeign
  • 服务熔断:Spring Cloud Alibaba Sentinel
  • 分布式事务:Spring Cloud Alibaba Seata

创建微服务项目

项目结构

框架版本

  • SpringBoot 3.3.4
  • SpringCloud 2023.0.3
  • SpringCloud Alibaba 2023.0.3.2

组件版本

  • Nacos 2.4.3
  • Sentinel 1.8.8
  • Seata 2.2.0

项目结构:

  • cloud-demo: 父工程。用于版本锁定:SpringBoot、SpringCloud和SpringCloud Alibaba 2
    • service:继承cloud-demo。管理微服务。用于放公共依赖
      • service-product:继承service
      • service-order:继承service
      • service-xxxx:继承service

创建项目(父工程)

父工程的项目POM文件主要是版本控制

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!--  父项目:  -->
    <packaging>pom</packaging>
    <modules>
        <module>service</module>
    </modules>
    <groupId>org.example</groupId>
    <artifactId>cloud-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud-demo</name>
    <description>cloud-demo</description>
    <!-- cloud-demo:主要用于指定版本   -->
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-cloud.version>2023.0.3</spring-cloud.version>
        <spring-cloud-alibaba.version>2023.0.3.2</spring-cloud-alibaba.version>
</properties>
    <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>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

创建Module

创建module: service、service-product和service-order

service的pom文件主要放一些公共依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>cloud-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <packaging>pom</packaging>
    <modules>
        <module>service-product</module>
        <module>service-order</module>
    </modules>
    <artifactId>service</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- nacos 服务发现与注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
</project>

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com

×

喜欢就点赞,疼爱就打赏