目录

  • 1、初识SpringCloud
  • 1.1、简介
  • 2、SpringCloud升级,部分组件停用:
  • 3、环境搭建:
  • 3.1、创建父工程,pom依赖
  • 3.2、创建子模块
  • 3.3、pom依赖
  • 3.4、创建application.yml
  • 3.5、主启动类
  • 3.6、业务类
  • 3.7、测试
  • 4、热部署
  • 5、order模块
  • 6、api模块
  • 7、服务注册与发现Eureka:
  • 7.1、环境搭建
  • 7.2、将微服务工程注册进Eureka
  • 7.3、Eureka集群
  • 7.3.1、核心原理
  • 7.3.2、搭建集群
  • 7.3.3、微服务注册进集群
  • 7.3.4、新增微服务,模拟负载均衡
  • 7.4、actuator微服务信息完善
  • 7.5、服务发现Discovery
  • 7.6、Eureka自我保护机制
  • 7.6.1、关闭自我保护机制
  • 7.7、Eureka停更说明
  • 8、Consul服务注册与发现
  • 8.1、简介
  • 8.2、安装并运行Consul
  • 8.3、测试
  • 9、注册中心异同点
  • 10、Ribbon负载均衡服务调用
  • 10.1、概述
  • 10.2、Ribbon负载均衡演示
  • 10.3、Ribbon核心组件IRule
  • 10.4、Ribbon负载均衡算法
  • 10.4.1、手写本地负载均衡器
  • 11、OpenFeign服务接口调用
  • 11.1、概述
  • 11.2、OpenFeign使用步骤
  • 11.3、OpenFeign超时控制
  • 11.4、OpenFeign日志打印功能
  • 12、Hystrix断路器
  • 12.1、概述
  • 12.2、Hystrix重要概念
  • 12.3、hystrix案例
  • 12.3.1、入门测试
  • 12.3.2、高并发测试
  • 12.3.2.1、Jmeter压测测试
  • 12.3.2.2、新建模块再测试
  • 12.4、服务降级
  • 12.4.1、单局测试
  • 12.4.2、全局测试
  • 12.5、服务熔断
  • 12.5.1、断路器
  • 12.5.2、熔断是什么
  • 12.5.3、实操
  • 12.5.4、原理(小总结)
  • 12.6、hystrix工作流程
  • 12.7、服务监控hystrixDashboard
  • 13、Gateway新一代网关(替换zuul)
  • 13.1、概述简介
  • 13.2、三大核心概念
  • 13.3、Gateway工作流程
  • 13.4、入门配置
  • 13.5、通过微服务名实现动态路由
  • 13.6、Predicate的使用
  • 13.7、Filter的使用
  • 14、SpringCloud config分布式配置中心
  • 14.1、概述
  • 14.2、Config服务端配置与测试
  • 14.3、Config客户端配置与测试
  • 14.4、Config客户端之动态刷新
  • 15、SpringCloud Bus 消息总线
  • 15.1、概述
  • 15.2、RabbitMQ环境配置
  • 15.3、SpringCloud Bus动态刷新全局广播
  • 15.4、SpringCloud Bus动态刷新定点通知
  • 16、SpringCloud Stream消息驱动
  • 16.1、消息驱动概述
  • 16.2、案例说明
  • 16.3、消息驱动之生产者
  • 16.4、消息驱动之消费者
  • 16.5、分组消费与持久化
  • 17、SpringCloud Sleuth分布式请求链路追踪
  • 17.1、概述
  • 17.2、搭建链路监控步骤
  • 18、SpringCloud Alibaba入门简介
  • 19、SpringCloud Alibaba Nacos服务注册和配置中心
  • 19.1、Nacos简介
  • 19.2、安装并运行Nacos
  • 19.3、Nacos作为服务注册中心演示
  • 19.4、Nacos作为服务配置中心演示
  • 19.5、Nacos集群和持久化配置(重要)
  • 19.5.1、官网说明
  • 19.5.2、Nacos持久化配置解释
  • 19.5.3、Linux版Nacos+MySQL生产环境配置
  • 20、SpringCloud Alibaba Sentinel实现熔断与限流
  • 20.1、Sentinel简介
  • 20.2、安装Sentinel控制台
  • 20.3、初始化演示工程
  • 20.4、流控规则
  • 20.5、降级规则
  • 20.6、热点key限流
  • 20.7、系统规则
  • 20.8、@SentinelResource
  • 20.9、服务熔断功能
  • 20.9.1、Ribbon系列
  • 20.9.2、Feign系列
  • 20.10、规则持久化
  • 21、SpringCloud Alibaba Seata处理分布式事务
  • 21.1、分布式事务问题
  • 21.2、Seata简介
  • 21.3、Seata-Server安装与配置
  • 21.4、订单/库存/账户业务数据库准备
  • 21.5、订单/库存/账户业务微服务准备
  • 21.5.1、新建订单Order-Module
  • 21.5.2、新建库存Storage-Module
  • 21.5.3、新建账户Account-Module
  • 21.5.4、Test
  • 21.6、Seata之原理


1、初识SpringCloud

微服务是一种架构方式,最终肯定需要技术架构去实施。

微服务的实现方式很多,但是最火的莫过于Spring Cloud了。为什么?

  • 后台硬:作为Spring家族的一员,有整个Spring全家桶靠山,背景十分强大。
  • 技术强:Spring作为Java领域的前辈,可以说是功力深厚。有强力的技术团队支撑,一般人还真比不了
  • 群众基础好:可以说大多数程序员的成长都伴随着Spring框架,试问:现在有几家公司开发不用Spring?SpringCloud与Spring的各个框架无缝整合,对大家来说一切都是熟悉的配方,熟悉的味道。
  • 使用方便:相信大家都体会到了SpringBoot给我们开发带来的便利,而SpringCloud完全支持SpringBoot的开发,用很少的配置就能完成微服务框架的搭建

1.1、简介

SpringCloud是Spring旗下的项目之一,官网地址:http://projects.spring.io/spring-cloud/

Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。

SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

  • Eureka:服务治理组件,包含服务注册中心,服务注册与发现机制的实现。(服务治理,服务注册/发现)
  • Zuul:网关组件,提供智能路由,访问过滤功能
  • Ribbon:客户端负载均衡的服务调用组件(客户端负载)
  • Feign:服务调用,给予Ribbon和Hystrix的声明式服务调用组件 (声明式服务调用)
  • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器,容错)

2023 spring cloud 版本 选择 最新spring cloud_spring


以上只是其中一部分。

2、SpringCloud升级,部分组件停用:

1,Eureka停用,可以使用zk作为服务注册中心

2,服务调用,Ribbon准备停更,代替为LoadBalance

3,Feign改为OpenFeign

4,Hystrix停更,改为resilence4j

或者阿里巴巴的sentienl

5.Zuul改为gateway

6,服务配置Config改为 Nacos

7,服务总线Bus改为Nacos

3、环境搭建:

3.1、创建父工程,pom依赖

<!-- 统一管理jar包版本 -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <junit.version>4.12</junit.version>
    <log4j.version>1.2.17</log4j.version>
    <lombok.version>1.16.18</lombok.version>
    <mysql.version>8.0.18</mysql.version>
    <druid.version>1.1.16</druid.version>
    <druid.spring.boot.starter.version>1.1.10</druid.spring.boot.starter.version>
    <spring.boot.version>2.2.2.RELEASE</spring.boot.version>
    <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
    <spring.cloud.alibaba.version>2.1.0.RELEASE</spring.cloud.alibaba.version>
    <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    <mybatis-spring-boot-starter.version>2.1.1</mybatis-spring-boot-starter.version>
    <hutool-all.version>5.1.0</hutool-all.version>
  </properties>

  <!-- 子模块继承之后,提供作用:锁定版本 + 子module不用谢groupId和version -->
  <dependencyManagement>
    <dependencies>
      <!--spring boot 2.2.2-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.2.2.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--spring cloud Hoxton.SR1-->
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR1</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!--Spring cloud alibaba 2.1.0.RELEASE-->
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.1.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
      </dependency>
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>${druid.spring.boot.starter.version}</version>
      </dependency>
      <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis-spring-boot-starter.version}</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <fork>true</fork>
          <addResources>true</addResources>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <!--第三方maven私服-->
  <repositories>
    <repository>
      <id>nexus-aliyun</id>
      <name>Nexus aliyun</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

3.2、创建子模块

2023 spring cloud 版本 选择 最新spring cloud_微服务_02


子模块名字:cloud-provider-payment8001

3.3、pom依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>

       
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3.4、创建application.yml

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    # 当前数据源操作类型
    type: com.alibaba.druid.pool.DruidDataSource
    # mysql驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root

mybatis:
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities

3.5、主启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

3.6、业务类

1、sql

CREATE TABLE `payment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `serial` varchar(200) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8

2、实体类

package com.atguigu.springcloud.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor //全参
@NoArgsConstructor  //空参
public class Payment implements Serializable {

    private Long id;
    private String serial;

}
package com.atguigu.springcloud.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor //全参
@NoArgsConstructor  //空参
public class CommonResult <T>{

    private Integer code;
    private String message;
    private T       data;

    public CommonResult(Integer code,String message){
        this(code,message,null);
    }

}

3、dao层:

package com.atguigu.springcloud.dao;

import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface PaymentDao {

    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") long id);

}

4、mapper配置文件类

在resource下,创建mapper/PayMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.springcloud.dao.PaymentDao">

    <!--   主要是在主键是自增的情况下,添加成功后可以直接使用主键值,其中keyProperty的值是对象的属性值不是数据库表中的字段名-->
    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial) values(#{serial});
    </insert>

    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT" />
        <id column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        select * from payment where id=#{id};
    </select>
</mapper>

5、service和serviceImpl

package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;

public interface PaymentService {

    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") long id);

}
package com.atguigu.springcloud.service.impl;

import com.atguigu.springcloud.dao.PaymentDao;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class PaymentServiceImpl implements PaymentService {

    @Resource
    private PaymentDao paymentDao;

    @Override
    public int create(Payment payment) {
        return paymentDao.create(payment);
    }

    @Override
    public Payment getPaymentById(long id) {
        return paymentDao.getPaymentById(id);
    }
}

6、controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/payment/create")
    public CommonResult create(Payment payment){
        int result = paymentService.create(payment);
        log.info("*****插入结果:"+result);

        if (result > 0){
            return new CommonResult(200,"插入数据库成功",result);
        }else {
            return new CommonResult(444,"插入数据库失败",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("*****查询结果:"+payment+"\t"+"O(∩_∩)O哈哈~");

        if (payment != null){
            return new CommonResult(200,"查询成功",payment);
        }else {
            return new CommonResult(444,"没有查到对应的记录,查询ID:"+id,null);
        }
    }
}

3.7、测试

先运行插入程序再运行查询程序进行测试即可!

4、热部署

1、添加pom依赖

<!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

2、修改配置

2023 spring cloud 版本 选择 最新spring cloud_微服务_03


3、按==ctrl+shift+alt+==

选择Registry…

2023 spring cloud 版本 选择 最新spring cloud_分布式_04

5、order模块

再创建一个微服务子模块,跟上一个模块一起进行测试!

  • 1、子模块名称:cloud-consumer-order80
  • 2、添加pom依赖:
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  • 3、启动类:
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
    }
}
  • 4、配置application.yml
server:
  port: 80
  • 5、实体类
package com.atguigu.springcloud.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor //全参
@NoArgsConstructor  //空参
public class Payment implements Serializable {

    private Long id;
    private String serial;

}
package com.atguigu.springcloud.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor //全参
@NoArgsConstructor  //空参
public class CommonResult<T>{

    private Integer code;
    private String message;
    private T       data;

    public CommonResult(Integer code, String message){
        this(code,message,null);
    }

}
  • 6、config:
package com.atguigu.springcloud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    @Bean
    public RestTemplate getRestTemplate() {
        return  new RestTemplate();
    }
}
  • 7、Controller
package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {
    public static final String PAYMENT_URL="http://localhost:8001";

    @Resource
    private RestTemplate restTemplate;

   @GetMapping("/consumer/payment/create")
    public CommonResult< Payment > create(Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
    }
}
  • 8、测试
    在测试之前一定要先在cloud-provider-payment8001模块中的PaymentController中添加==@RequestBody==,否侧添加数据时会报错。

6、api模块

  • 1、放置一些通用的代码,以此来简化工程代码量。
  • 2、创建子模块:cloud-api-commons
  • 3、添加pom依赖
<dependencies>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>
  • 4、将cloud-consumer-order80或cloud-provider-payment8001工程中的entities包下的两个实体类复制到当前的工程中。
  • 5、在cloud-consumer-order80和cloud-provider-payment8001工程中添加pom依赖。
<dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
  • 6、删除cloud-consumer-order80和cloud-provider-payment8001工程中的entities的实体类。
  • 7、测试。

7、服务注册与发现Eureka:

前面我们没有服务注册中心,也可以服务间调用,为什么还要服务注册?

当服务很多时,单靠代码手动管理是很麻烦的,需要一个公共组件,统一管理多服务,包括服务是否正常运行,等

Eureka用于**服务注册,目前官网已经停止更新**

2023 spring cloud 版本 选择 最新spring cloud_微服务_05

2023 spring cloud 版本 选择 最新spring cloud_spring_06


2023 spring cloud 版本 选择 最新spring cloud_分布式_07


2023 spring cloud 版本 选择 最新spring cloud_Cloud_08

7.1、环境搭建

  • 1、创建子模块:cloud-eureka-server7001
  • 2、添加pom依赖
<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
  • 3、启动类
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}
  • 4、配置application.yml
server:
  port: 7001

eureka:
  instance:
    hostname: localhost  #eureka服务端的实例名字
  client:
    register-with-eureka: false    #表识不向注册中心注册自己
    fetch-registry: false   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
        defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
  • 5、测试
    打开浏览器访问http://localhost:7001/进入Eureka页面。

7.2、将微服务工程注册进Eureka

1、将cloud-provider-payment8001注册进Eureka

  • 添加pom依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
  • 配置application.yml
eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka
  • 在启动类添加==@EnableEurekaClient==

2023 spring cloud 版本 选择 最新spring cloud_分布式_09

  • 测试

打开浏览器访问http://localhost:7001/进入Eureka页面。

2023 spring cloud 版本 选择 最新spring cloud_微服务_10


2、将cloud-consumer-order80注册进Eureka

  • 添加pom依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
  • 配置application.yml
eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka
  • 在启动类添加==@EnableEurekaClient==
  • 测试

打开浏览器访问http://localhost:7001/进入Eureka页面。

2023 spring cloud 版本 选择 最新spring cloud_Cloud_11

7.3、Eureka集群

2023 spring cloud 版本 选择 最新spring cloud_Cloud_12


解决办法:搭建Eureka注册中心集群,实现负载均衡+故障容错

7.3.1、核心原理

互相注册,相互守望

7.3.2、搭建集群

  • 1、创建一个子模块:cloud-eureka-server7002
  • 2、将cloud-eureka-server7001中的依赖复制到cloud-eureka-server7002中。
  • 3、启动类
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7002.class,args);
    }
}
  • 4、修改映射文件
    找到C:\Windows\System32\drivers\etc路径下的hosts文件
    127.0.0.1 eureka7001.com127.0.0.1 eureka7002.com添加到host文件中。
  • 5、修改yml配置文件
    1)cloud-eureka-server7001
server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com    #eureka服务端的实例名字
  client:
    register-with-eureka: false    #表识不向注册中心注册自己
    fetch-registry: false   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7002.com:7002/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址

2)cloud-eureka-server7002

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名字
  client:
    register-with-eureka: false    #表识不向注册中心注册自己
    fetch-registry: false   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/     #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址

7.3.3、微服务注册进集群

  • 1、将cloud-provider-payment8001和cloud-consumer-order80注册进7001/7002集群工程之中。
  • 2、修改yml文件:
    1)cloud-provider-payment8001
eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  #集群版

2)cloud-consumer-order80

eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka
        defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  #集群版
  • 3、测试
    先要启动EurekaServer,7001/7002服务
    再要启动服务提供者provider,8001服务
    再要启动消费者,80

7.3.4、新增微服务,模拟负载均衡

  • 1、创建子模块:cloud-provider-payment8002
  • 2、将cloud-provider-payment8001中的所有代码全部拷贝到cloud-provider-payment8002中。
  • 3、修改端口号和启动类,8001改成8002即可。
  • 4、在controller类中添加
@Value("${server.port}")
    private String serverPort;
  • 5、测试
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_13

  • 6、订单服务访问地址不能写死
    修改cloud-consumer-order80工程中的controller类
  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_14

  • 此时,会出现访问不到具体哪个微服务端口号的错误。
    解决办法:
    使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
  • 7、测试
    打开浏览器访问http://localhost/consumer/payment/get/31
  • 2023 spring cloud 版本 选择 最新spring cloud_spring_15


  • 2023 spring cloud 版本 选择 最新spring cloud_spring_16

  • 通过刷新测试,轮番出现8001和8002端口号时,表示测试成功!

7.4、actuator微服务信息完善

  • 当前问题:在Eureka注册中心处微服务没有没有ip提示。
  • 解决办法:
    cloud-provider-payment8001和cloud-provider-payment8002的yml文件中添加如下配置
instance:
    instance-id: payment8001
    prefer-ip-address: true   #访问路径可以显示IP地址
  • 测试

7.5、服务发现Discovery

  • 1、修改cloud-provider-payment8001的Controller
@Resource
    private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
    public Object discovery(){
        List<String> services = discoveryClient.getServices();
        for (String element : services){
            log.info("*****element:"+element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance instance : instances) {
            log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
        }
        return this.discoveryClient;
    }
  • 2、启动类
@EnableDiscoveryClient
  • 3、测试
    打开浏览器访问http://localhost:8001/payment/discovery

    如图所示,可以看到我们在这个端口号上所有注册的微服务。

7.6、Eureka自我保护机制

2023 spring cloud 版本 选择 最新spring cloud_分布式_17


2023 spring cloud 版本 选择 最新spring cloud_分布式_18


2023 spring cloud 版本 选择 最新spring cloud_微服务_19


2023 spring cloud 版本 选择 最新spring cloud_Cloud_20


2023 spring cloud 版本 选择 最新spring cloud_分布式_21

7.6.1、关闭自我保护机制

  • 1、关闭集群,改用单机版测试
  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_22


  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_23

  • 2、出厂默认,自我保护机制是开启的
    eureka.server.enable-self-preservation = true
  • 3、使用eureka.server.enable-self-preservation = false可以禁用自我保护模式
server:
    enable-self-preservation: false   #关闭自我保护机制,保证不可用服务被及时剔除
    eviction-interval-timer-in-ms: 2000
  • 4、配置服务端cloud-provider-payment8001的yml文件
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
    lease-renewal-interval-in-seconds:  1
    #Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超市将剔除服务
    lease-expiration-duration-in-seconds:  2
  • 5、测试

7.7、Eureka停更说明

官网:https://github.com/Netflix/eureka/wiki

2023 spring cloud 版本 选择 最新spring cloud_Cloud_24


Eureka停止更新了你怎么办?

看下面的consul

8、Consul服务注册与发现

8.1、简介

官网:https://www.consul.io/intro/index.html

  • 1、是什么?
  • 2、能干嘛:

    服务发现:提供HTTP和DNS两种发现方式

健康监测:支持多种协议,HTTP、TCP、Docker、Shell脚本定制化

KV存储:key , Value的存储方式

多数据中心:Consul支持多数据中心

可视化Web界面

8.2、安装并运行Consul

官网下载:https://www.consul.io/downloads.html

下载完成后只有一个consul.exe文件,硬盘路径下双击运行,查看版本信息。

2023 spring cloud 版本 选择 最新spring cloud_Cloud_25


使用开发模式启动

在cmd窗口输入consul agent -dev

通过以下地址可以访问Consul的首页:http;//localhost:8500

2023 spring cloud 版本 选择 最新spring cloud_微服务_26

8.3、测试

1)

  • 1、创建子模块:cloud-providerconsul-payment8006
  • 2、添加pom依赖:
<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  • 3、配置yml文件
server:
  port: 8006

spring:
  application:
    name: consul-provider-payment
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
  • 4、主启动类
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8006.class,args);
    }
}
  • 5、Controller
package com.atguigu.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/consul")
    public String paymentConsul(){
        return "springcloud with consul:"+serverPort+"\t"+ UUID.randomUUID().toString();
    }


}
  • 6、测试
  • 2023 spring cloud 版本 选择 最新spring cloud_微服务_27

  • http://localhost:8006/payment/consul
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_28

  • 2)
  • 1、创建子模块:cloud-consumerconsul-order80
  • 2、添加pom依赖
<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
  • 3、启动类
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class OrderConsulMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderConsulMain80.class,args);
    }
}
  • 4、配置yml文件
server:
  port: 80

spring:
  application:
    name: consul-consumer-order
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}
  • 5、配置Bean
package com.atguigu.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}
  • 6、controller
package com.atguigu.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderConsulController {

    public static final String INVOME_URL = "http://consul-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/consul")
    public String payment (){
        String result = restTemplate.getForObject(INVOME_URL+"/payment/consul",String.class);
        return result;
    }

}

-7、测试

2023 spring cloud 版本 选择 最新spring cloud_分布式_29


http://localhost/consumer/payment/consul

2023 spring cloud 版本 选择 最新spring cloud_spring_30

9、注册中心异同点

2023 spring cloud 版本 选择 最新spring cloud_Cloud_31


2023 spring cloud 版本 选择 最新spring cloud_分布式_32

  • C:Consistency(强一致性)
  • A:Availability(可用性)
  • P:Partition tolerance(分区容错)
  • CAP理论关注粒度是数据,而不是整体系统设计的策略
  • AP(Eureka)
  • CP(Zookeeper/Consul)

10、Ribbon负载均衡服务调用

10.1、概述

1、是什么?

2023 spring cloud 版本 选择 最新spring cloud_spring_33


官网:https://github.com/Netflix/ribbon/wiki/Getting-StartedRibbon目前也进入维护模式

2023 spring cloud 版本 选择 最新spring cloud_spring_34


未来替换方案:

2023 spring cloud 版本 选择 最新spring cloud_Cloud_35


2、能干嘛?

LB(负载均衡)

2023 spring cloud 版本 选择 最新spring cloud_spring_36

  • 集中式LB
  • 进程内LB

    核心概括:负载均衡+RestTemplate调用

10.2、Ribbon负载均衡演示

1、架构说明

2023 spring cloud 版本 选择 最新spring cloud_Cloud_37


总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。2、pom

2023 spring cloud 版本 选择 最新spring cloud_Cloud_38


2023 spring cloud 版本 选择 最新spring cloud_spring_39


3、二说RestTemplate的使用

官网:https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html

2023 spring cloud 版本 选择 最新spring cloud_spring_40


4、测试

cloud-consumer-order80controller类中

@GetMapping("/consumer/payment/getForEntity/{id}")
    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){
        ResponseEntity<CommonResult> forEntity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
        if (forEntity.getStatusCode().is2xxSuccessful()){
            return forEntity.getBody();
        }else {
            return new CommonResult<>(444,"操作失败");
        }

5、结果

2023 spring cloud 版本 选择 最新spring cloud_微服务_41

10.3、Ribbon核心组件IRule

1、IRule:根据特定算法从服务列表中选取一个要访问的服务

2023 spring cloud 版本 选择 最新spring cloud_分布式_42

  • com.netflix.loadbalancer.RoundRobinRule:轮询
  • com.netflix.loadbalancer.RandomRule:随机
  • com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试
  • WeightedResponseTimeRule :对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
  • BestAvailableRule :会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • AvailabilityFilteringRule :先过滤掉故障实例,再选择并发较小的实例
  • ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

2、如何替换

  • 修改cloud-consumer-order80
  • 注意配置细节
  • 新建package:com.atguigu.myrule
  • 上面包下新建MySelfRule规则类
package com.atguigu.myrule;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySelfRule {

    @Bean
    public IRule myRule(){
        return new RandomRule();//定义为随机
    }
}
  • 主启动类添加@RibbonClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
  • 测试
    http://localhost/consumer/payment/get/31
    通过不断刷新页面,8001和8002随机出现即可!

10.4、Ribbon负载均衡算法

1、原理

2023 spring cloud 版本 选择 最新spring cloud_spring_43


2、查看RoundRobinRule源码分析

10.4.1、手写本地负载均衡器

1、7001/7002集群启动
2、8001/8002微服务改造:controller

@GetMapping(value = "/payment/lb")
public String getPaymentLB(){
    return serverPort;
}

3、80微服务改造

  • 1)ApplicationContextBean去掉@LoadBalanced
  • 2)LoadBalancer接口
package com.atguigu.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;

import java.util.List;

public interface LoadBalancer {
     //收集服务器总共有多少台能够提供服务的机器,并放到list里面
    ServiceInstance instances(List<ServiceInstance> serviceInstances);

}
  • 3)MyLB
package com.atguigu.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class MyLB implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    //坐标
    private final int getAndIncrement(){
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        }while (!this.atomicInteger.compareAndSet(current,next));  //第一个参数是期望值,第二个参数是修改值是
        System.out.println("*******第几次访问,次数next: "+next);
        return next;
    }

    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {  //得到机器的列表
       int index = getAndIncrement() % serviceInstances.size(); //得到服务器的下标位置
        return serviceInstances.get(index);
    }
}

4、OrderController

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.lb.LoadBalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;
import java.util.List;

@RestController
@Slf4j
public class OrderController {

   // public static final String PAYMENT_URL = "http://localhost:8001";
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private LoadBalancer loadBalancer;

    @Resource
    private DiscoveryClient discoveryClient;

    @GetMapping("/consumer/payment/create")
    public CommonResult<Payment>   create( Payment payment){
        return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);  //写操作
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
    }

    @GetMapping("/consumer/payment/getForEntity/{id}")
     public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
        if (entity.getStatusCode().is2xxSuccessful()){
          //  log.info(entity.getStatusCode()+"\t"+entity.getHeaders());
            return entity.getBody();
        }else {
            return new CommonResult<>(444,"操作失败");
        }
     }

    @GetMapping(value = "/consumer/payment/lb")
     public String getPaymentLB(){
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.size() <= 0){
            return null;
        }
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/payment/lb",String.class);
    }
}

5、测试

http://localhost/consumer/payment/lb

2023 spring cloud 版本 选择 最新spring cloud_分布式_44


通过不断刷新页面,8001和8002轮番出现即可。

2023 spring cloud 版本 选择 最新spring cloud_Cloud_45

11、OpenFeign服务接口调用

11.1、概述

1、OpenFeign是什么

2023 spring cloud 版本 选择 最新spring cloud_spring_46


2023 spring cloud 版本 选择 最新spring cloud_微服务_47


Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可2、能干嘛

2023 spring cloud 版本 选择 最新spring cloud_spring_48


3、Feign和OpenFeign两者区别

2023 spring cloud 版本 选择 最新spring cloud_spring_49

11.2、OpenFeign使用步骤

1、新建cloud-consumer-feign-order80
2、添加pom依赖

<!--openfeign-->
    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3、配置yml文件

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka

4、启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
public class OrderFeignMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderFeignMain80.class,args);
    }
}

5、service层
新建PaymentFeignService接口

package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);

}

6、controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderFeignController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        return paymentFeignService.getPaymentById(id);
    }

}

7、测试

http://localhost/consumer/payment/get/31

2023 spring cloud 版本 选择 最新spring cloud_分布式_50


8、小总结

2023 spring cloud 版本 选择 最新spring cloud_微服务_51

11.3、OpenFeign超时控制

超时设置,故意设置超时演示出错情况

  • 服务提供方8001故意写暂停程序
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout(){
    try { TimeUnit.SECONDS.sleep(3); }catch (Exception e) {e.printStackTrace();}
    return serverPort;
}
  • 服务消费方80添加超时方法PaymentFeignService
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();
  • 服务消费方80添加超时方法OrderFeignController
@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentFeignTimeout(){
   return paymentFeignService.paymentFeignTimeout();
}
  • 测试
    http://localhost/consumer/payment/feign/timeout

错误页面:

2023 spring cloud 版本 选择 最新spring cloud_分布式_52

  • 解决办法:
    OpenFeign默认等待一秒钟,超过后报错

    YML文件里需要开启OpenFeign客户端超时控制
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
  #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout:  5000
  #指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000
  • 再测试

11.4、OpenFeign日志打印功能

1、是什么?

2023 spring cloud 版本 选择 最新spring cloud_分布式_53


2、日志级别

2023 spring cloud 版本 选择 最新spring cloud_Cloud_54


3、配置日志bean

新建config包

package com.atguigu.springcloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

4、YML文件里需要开启日志的Feign客户端

logging:
  level:
    com.atguigu.springcloud.service.PaymentFeignService: debug

5、测试

2023 spring cloud 版本 选择 最新spring cloud_spring_55


2023 spring cloud 版本 选择 最新spring cloud_分布式_56

12、Hystrix断路器

12.1、概述

1、分布式系统面临的问题

2023 spring cloud 版本 选择 最新spring cloud_分布式_57


2023 spring cloud 版本 选择 最新spring cloud_分布式_58


2023 spring cloud 版本 选择 最新spring cloud_分布式_59


2023 spring cloud 版本 选择 最新spring cloud_Cloud_60


2、是什么?

2023 spring cloud 版本 选择 最新spring cloud_微服务_61


3、能干嘛

  • 服务降级
  • 服务熔断
  • 接近实时的监控
  • 。。。。

4、官网资料
https://github.com/Netflix/Hystrix/wiki/How-To-Use

5、Hystrix官宣,停更进维

https://github.com/Netflix/Hystrix

2023 spring cloud 版本 选择 最新spring cloud_spring_62

12.2、Hystrix重要概念

1、服务降级
服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback

哪些情况会触发降级:

  • 程序运行异常
  • 超时
  • 服务熔断触发服务降级
  • 线程池/信号量打满也会导致服务降级

2、服务熔断
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。

保险丝:
服务的降级->进而熔断->恢复调用链路

3、服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行

12.3、hystrix案例

12.3.1、入门测试

1、新建cloud-provider-hystrix-payment8001
2、添加pom依赖

<dependencies>
        <!--新增hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>


        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3、配置yml文件

server:
  port: 8001


eureka:
  client:
    register-with-eureka: true    #表识不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      # defaultZone: http://eureka7002.com:7002/eureka/    #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/
#  server:
#    enable-self-preservation: false
spring:
  application:
    name: cloud-provider-hystrix-payment
  #    eviction-interval-timer-in-ms: 2000

4、启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

5、service

package com.atguigu.springcloud.service;

import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class PaymentService {

    //成功
    public String paymentInfo_OK(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_OK,id:  "+id+"\t"+"哈哈哈"  ;
    }

    //失败
    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 3;
        try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_TimeOut,id:  "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
    }

}

6、controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentService.paymentInfo_OK(id);
        log.info("*******result:"+result);
        return result;
    }
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*******result:"+result);
        return result;
    }
}

7、测试

启动eureka7001

2023 spring cloud 版本 选择 最新spring cloud_分布式_63


访问正常页面

http://localhost:8001/payment/hystrix/ok/31

2023 spring cloud 版本 选择 最新spring cloud_微服务_64


访问每次调用耗费5秒钟页面

2023 spring cloud 版本 选择 最新spring cloud_Cloud_65


控制台没有报任何异常信息,并打印出访问的信息即可!

2023 spring cloud 版本 选择 最新spring cloud_spring_66


上述module均OK
以上述为根基平台,从正确->错误->降级熔断->恢复

12.3.2、高并发测试

上述在非高并发情形下,还能勉强满足 but…

12.3.2.1、Jmeter压测测试

先去Jmeter官网下载Jmeter

开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务。

2023 spring cloud 版本 选择 最新spring cloud_Cloud_67


2023 spring cloud 版本 选择 最新spring cloud_Cloud_68


启动Jmeter,再来进行访问

http://localhost:8001/payment/hystrix/ok/31 http://localhost:8001/payment/hystrix/timeout/31

演示结果:
两个都在自己转圈圈

为什么会被卡死?
tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。

结论:

上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死。

12.3.2.2、新建模块再测试

1、新建子模块:cloud-consumer-feign-hystrix-order80
2、添加pom依赖

<dependencies>
        <!--新增hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3、配置yml文件

server:
  port: 80

eureka:
  client:
    register-with-eureka: false    #表识不向注册中心注册自己
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

4、启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

5、service

package com.atguigu.springcloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

6、controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_OK(id);
        log.info("*******result:"+result);
        return result;
    }
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        log.info("*******result:"+result);
        return result;
    }

}

7、测试

1)正常测试

http://localhost/consumer/payment/hystrix/ok/31

2023 spring cloud 版本 选择 最新spring cloud_分布式_69


不会出现卡顿现象

2)高并发测试

开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务。

通过不断的刷新三个微服务工程

消费者80:

  • 要么转圈圈等待
  • 要么消费端报超时错误
  • 故障现象和导致原因
    1、8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕。
    2、80此时调用8001,客户端访问响应缓慢,转圈圈

正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生

  • 如何解决?
    1、对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
    2、对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
    3、对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级

12.4、服务降级

12.4.1、单局测试

1、降低配置

使用==@HystrixCommand==注解

2023 spring cloud 版本 选择 最新spring cloud_微服务_70


2、8001先从自身找问题

设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback。

3、8001fallback

1)业务类启用
@HystrixCommand报异常后如何处理:
一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法

@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")  //3秒钟以内就是正常的业务逻辑
    })

    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 3;
        try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_TimeOut,id:  "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
    }

    public String paymentInfo_TimeOutHandler(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"   系统繁忙,请稍后再试,id:  "+id+"\t"+"哭了哇呜";
    }

2023 spring cloud 版本 选择 最新spring cloud_Cloud_71


2)主启动类激活

添加新注解@EnableCircuitBreaker

2023 spring cloud 版本 选择 最新spring cloud_spring_72


3)、测试

2023 spring cloud 版本 选择 最新spring cloud_微服务_73


4、80fallback

80订单微服务,也可以更好的保护自己,自己也依样画葫芦进行客户端降级保护。

我们自己配置过的热部署方式对java代码的改动明显,但对@HystrixCommand内属性的修改建议重启微服务

1)配置yml文件

feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

2)启动类

2023 spring cloud 版本 选择 最新spring cloud_spring_74


3)业务类controller

@GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")  //3秒钟以内就是正常的业务逻辑
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){

        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

    //兜底方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "我是消费者80,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)";
    }

4)测试

2023 spring cloud 版本 选择 最新spring cloud_spring_75


5)目前问题:

  • 每个业务方法对应一个兜底的方法,代码膨胀
  • 统一和自定义的分开

12.4.2、全局测试

每个方法配置一个???膨胀

2023 spring cloud 版本 选择 最新spring cloud_spring_76


2023 spring cloud 版本 选择 最新spring cloud_微服务_77

  • 说明:
  • controller配置
//下面是全局fallback方法
    public String payment_Global_FallbackMethod() {
        return "Global异常处理信息,请稍后再试,(┬_┬)";
    }
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")  //全局的
  • 测试

    和业务逻辑混一起???混乱

1、服务降级,客户端去调用服务端,碰上服务端宕机或关闭 。

2、本次案例服务降级处理是在客户端80实现完成的,与服务端8001没有关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。

3、未来我们要面对的异常:

  • 运行
  • 超时
  • 宕机

4、修改cloud-consumer-feign-hystrix-order80

  • 根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理。
  • PaymentFallbackService类实现PaymentFeignClientService接口
package com.atguigu.springcloud.service;

import org.springframework.stereotype.Component;

@Component
public class PaymentFallbackService implements PaymentHystrixService {
    @Override
    public String paymentInfo_OK(Integer id) {
        return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
    }
}
  • 配置yml文件
feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
  • PaymentHystrixService接口
  • 测试
    1、单个eureka先启动7001
    2、PaymentHystrixMain8001启动
    3、正常访问测试
    http://localhost/consumer/payment/hystrix/ok/31 4、故意关闭微服务8001

    5、客户端自己调用提升
    此时服务端provider已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器。

12.5、服务熔断

12.5.1、断路器

一句话就是家里保险丝

12.5.2、熔断是什么

2023 spring cloud 版本 选择 最新spring cloud_分布式_78

  • 大神论文
    https://martinfowler.com/bliki/CircuitBreaker.html

12.5.3、实操

1、修改cloud-provider-hystrix-payment8001
2、PaymentService:

//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否开启断路器
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),   //请求次数
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),  //时间范围
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    if (id < 0){
        throw new RuntimeException("*****id 不能负数");
    }
    String serialNumber = IdUtil.simpleUUID();

    return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
    return "id 不能负数,请稍候再试,(┬_┬)/~~     id: " +id;
}

why配置这些参数?

2023 spring cloud 版本 选择 最新spring cloud_微服务_79


3、PaymentController:

//===服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("*******result:"+result);
    return result;
}

4、测试

  • 打开Eureka7001服务端
  • 自测cloud-provider-hystrix-payment8001
  • 正确:http://localhost:8001/payment/circuit/31
  • 错误:http://localhost:8001/payment/circuit/-31
  • 多次错误,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路。

12.5.4、原理(小总结)

1、大神结论

2023 spring cloud 版本 选择 最新spring cloud_spring_80


2、熔断类型

  • 熔断打开: 请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入熔断状态。
  • 熔断关闭: 熔断关闭不会对服务进行熔断。
  • 熔断半开: 部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。

12.6、hystrix工作流程

官网:https://github.com/Netflix/Hystrix/wiki/How-it-Works

2023 spring cloud 版本 选择 最新spring cloud_spring_81

12.7、服务监控hystrixDashboard

1、概述

2023 spring cloud 版本 选择 最新spring cloud_分布式_82


2、新建子模块:cloud-consumer-hystrix-dashboard9001

3、添加pom依赖

<dependencies>
        <!--新增hystrix dashboard-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

4、配置yml文件

server:
  port: 9001

5、启动类

配置@EnableHystrixDashboard注解

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class,args);
    }
}

6、所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

7、启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001

访问:http://localhost:9001/hystrix

2023 spring cloud 版本 选择 最新spring cloud_微服务_83


8、修改cloud-provider-hystrix-payment8001

注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径

@Bean
public ServletRegistrationBean getServlet(){
    HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
    ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
    registrationBean.setLoadOnStartup(1);
    registrationBean.addUrlMappings("/hystrix.stream");
    registrationBean.setName("HystrixMetricsStreamServlet");
    return registrationBean;
}

否则会报Unable to connect to Command Metric Stream和404异常

9、测试

  • 启动1个eureka或者3个eureka集群均可
  • 9001监控8001
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_84

  • 填写监控地址:http://localhost:8001/hystrix.stream
  • 测试地址:http://localhost:8001/payment/circuit/31(正常)和http://localhost:8001/payment/circuit/-31(异常)
  • 监控结果,成功
  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_85

  • 监控结果,失败
  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_86

  • 10、如何看上图
  • 7色
  • 1圈
  • 2023 spring cloud 版本 选择 最新spring cloud_微服务_87

  • 1线
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_88

  • 整图说明
  • 2023 spring cloud 版本 选择 最新spring cloud_spring_89


  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_90


  • 2023 spring cloud 版本 选择 最新spring cloud_微服务_91

13、Gateway新一代网关(替换zuul)

13.1、概述简介

1、浏览官网
上一代zuul 1.X官网:https://github.com/Netflix/zuul/wiki

当前gateway:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/

2、是什么?

2023 spring cloud 版本 选择 最新spring cloud_Cloud_92


2023 spring cloud 版本 选择 最新spring cloud_微服务_93


1)概述

2023 spring cloud 版本 选择 最新spring cloud_微服务_94


2023 spring cloud 版本 选择 最新spring cloud_微服务_95


2023 spring cloud 版本 选择 最新spring cloud_分布式_96


2)一句话

Spring Cloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。源码架构

2023 spring cloud 版本 选择 最新spring cloud_分布式_97


3、能干嘛

  • 反向代理
  • 鉴权
  • 流量控制
  • 熔断
  • 日志监控
  • 。。。。

4、微服务架构中网关在哪里

2023 spring cloud 版本 选择 最新spring cloud_Cloud_98


5、有了Zuul了怎么又出来了gateway

1)我们为什么选择Gatway?

  • neflix不太靠谱,zuul2.0一直跳票,迟迟不发布
  • SpringCloud Gateway具有如下特性
  • SpringCloud Gateway与Zuul的区别

    2)Zuul1.x模型


    3)GateWay模型

    WebFlux是什么?

13.2、三大核心概念

  • Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
  • Predicate(断言):参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
  • Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
  • 总体:

13.3、Gateway工作流程

1、官网总结:

2023 spring cloud 版本 选择 最新spring cloud_spring_99


2023 spring cloud 版本 选择 最新spring cloud_Cloud_100


2、核心逻辑

路由转发+执行过滤器链

13.4、入门配置

1、新建子模块:cloud-gateway-gateway9527
2、添加pom依赖

<dependencies>
        <!--新增gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3、配置yml文件

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**   #断言,路径相匹配的进行路由

        - id: payment_routh2
          uri: http://localhost:8001
          predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由


eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

4、主启动类

package com.atguigu.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
    public static void main(String[] args) {
            SpringApplication.run( GateWayMain9527.class,args);
        }
}

5、9527网关如何做路由映射那???

  • cloud-provider-payment8001看看controller的访问地址:get、lb
  • 我们目前不想暴露8001端口,希望在8001外面套一层9527

6、YML新增网关配置(上面yml文件意境配置好了)

cloud:
    gateway:
      routes:
    - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
      uri: http://localhost:8001   #匹配后提供服务的路由地址
      predicates:
        - Path=/payment/get/**   #断言,路径相匹配的进行路由

    - id: payment_routh2
      uri: http://localhost:8001
      predicates:
        - Path=/payment/lb/**   #断言,路径相匹配的进行路由

7、测试

  • 启动7001
  • 启动8001:cloud-provider-payment8001
  • 启动9527网关
  • 访问说明

    1)添加网关前

http://localhost:8001/payment/get/31

2023 spring cloud 版本 选择 最新spring cloud_分布式_101


2)添加网关后http://localhost:9527/payment/get/31

2023 spring cloud 版本 选择 最新spring cloud_微服务_102


8、YML配置说明

Gateway网关路由有两种配置方式

1) 在配置文件yml中配置(见如上步骤)

2) 代码中注入RouteLocator的Bean

官网案例

2023 spring cloud 版本 选择 最新spring cloud_Cloud_103


通过9527网关访问到外网的百度新闻网址

config:

package com.atguigu.springcloud.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GateWayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_rote_atguigu", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
}

结果:

2023 spring cloud 版本 选择 最新spring cloud_微服务_104


举一反三:

2023 spring cloud 版本 选择 最新spring cloud_微服务_105

13.5、通过微服务名实现动态路由

  • 默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
  • 启动:一个eureka7001+两个服务提供者8001/8002
  • YML:
    1、需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。
    2、lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri
server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            - Path=/payment/get/**   #断言,路径相匹配的进行路由

        - id: payment_routh2
          #uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由


eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka
  • 测试

http://localhost:9527/payment/lb

刷新页面8001/8002两个端口在切换即可。

13.6、Predicate的使用

1、是什么

启动我们的gatewat9527

2023 spring cloud 版本 选择 最新spring cloud_微服务_106


2、Route Predicate Factories这个是什么?

2023 spring cloud 版本 选择 最新spring cloud_spring_107


2023 spring cloud 版本 选择 最新spring cloud_分布式_108


3、常用的Route Predicate

2023 spring cloud 版本 选择 最新spring cloud_分布式_109

  • 1.After Route Predicate

    在test目录下面新建测试类,得到上面的时间:
package test;

import java.time.ZonedDateTime;

public class T2 {
    public static void main(String[] args) {
        ZonedDateTime zbj = ZonedDateTime.now();
        System.out.println(zbj);
    }
}

2023 spring cloud 版本 选择 最新spring cloud_spring_110

  • 2.Before Route Predicate
    yml:
- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
   - Before=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
  • 3.Between Route Predicate
    yml
- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] ,  2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
  • 4.Cookie Route Predicate
  • 2023 spring cloud 版本 选择 最新spring cloud_微服务_111

  • 使用cmd命令窗口测试
  • 2023 spring cloud 版本 选择 最新spring cloud_微服务_112

  • 解决加入curl返回中文乱码:
  • 5.Header Route Predicate
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_113

  • yml:
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_114


  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_115

  • 6.Host Route Predicate
    yml
- Host=**.atguigu.com
  • 7.Method Route Predicate
    yml
- Method=GET
  • 8.Query Route Predicate
    yml
- Query=username, \d+ #要有参数名称并且是正整数才能路由
  • 9.小总结
    1)以上配置:
server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            - Path=/payment/get/**   #断言,路径相匹配的进行路由
 
        - id: payment_routh2
          #uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            - Path=/payment/lb/**   #断言,路径相匹配的进行路由
            #- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
            #- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问
            #- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
            #- Host=**.atguigu.com
            #- Method=GET
            #- Query=username, \d+ #要有参数名称并且是正整数才能路由
 
 
eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

2)说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。

13.7、Filter的使用

1、是什么?

2023 spring cloud 版本 选择 最新spring cloud_微服务_116


2、Spring Cloud Gateway的Filter

单一:GatewayFilter

2023 spring cloud 版本 选择 最新spring cloud_分布式_117


全局:GlobalFilter

2023 spring cloud 版本 选择 最新spring cloud_spring_118


3、自定义过滤器

自定义全局GlobalFilter:

两个主要接口:impiemerts GlobalFilter ,Ordered

能干嘛:

  • 全局日志记录
  • 统一网关鉴权
  • 。。。。。。

案例代码:

package com.atguigu.springcloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter,Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info("*********come in MyLogGateWayFilter: "+new Date());
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname == null){
            log.info("*****用户名为Null 非法用户,(┬_┬)");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);//给人家一个回应
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

4、测试

启动:

2023 spring cloud 版本 选择 最新spring cloud_spring_119


正确:

http://localhost:9527/payment/lb?uname=z3

错误:
后面没写uname或错写。uname后面的名字可随意写。

14、SpringCloud config分布式配置中心

14.1、概述

1、分布式系统面临的配置问题

2023 spring cloud 版本 选择 最新spring cloud_分布式_120


2、是什么?

2023 spring cloud 版本 选择 最新spring cloud_Cloud_121


2023 spring cloud 版本 选择 最新spring cloud_spring_122


3、能干嘛

  • 集中管理配置文件。
  • 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release。
  • 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息。
  • 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置。
  • 将配置信息以REST接口的形式暴露:post、curl访问刷新均可…

4、与Github整合配置
由于SpringCloud Config默认使用Git来存储配置文件(也有其它方式,比如支持svn和本地文件,但最推荐的还是Git,而且使用的是http/https访问的形式)

5、官网

https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.1.RELEASE/reference/html/

2023 spring cloud 版本 选择 最新spring cloud_spring_123

14.2、Config服务端配置与测试

1、用你自己的账号在Github上新建一个名为sprincloud-config的新Repository。

2、在本地建一个SpringCloud2020文件夹,并把GitHub上的存储库克隆过来。

3、新建三个yml文件夹:(并在dev的文件中写些代码,并同步到GitHub)

2023 spring cloud 版本 选择 最新spring cloud_spring_124


4、新建Module模块cloud-config-center-3344它既为Cloud的配置中心模块cloudConfig Center

5、添加pom依赖

<dependencies>


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

6、配置yml文件

server:
  port: 3344
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri:  填写你自己的github路径
          search-paths:
            - springcloud-config
      label: master
eureka:
  client:
    service-url:
      defaultZone:  http://localhost:7001/eureka

7、主启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigCenterMain3344 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigCenterMain3344 .class,args);
    }
}

8、在本地的host文件中添加域名配置

127.0.0.1 config-3344.com

9、测试通过Config微服务是否可以从Github上获取配置内容

先启动服务端7001,再启动微服务3344

访问:http://config-3344.com:3344/master/config-dev.yml

2023 spring cloud 版本 选择 最新spring cloud_Cloud_125


10、配置读取规则

官网:

2023 spring cloud 版本 选择 最新spring cloud_微服务_126


2023 spring cloud 版本 选择 最新spring cloud_分布式_127


1)/{label}/{application}-{profile}.yml(最推荐使用这种方式):

http://config-3344.com:3344/master/config-dev.yml

2)/{application}-{profile}.yml:
http://config-3344.com:3344/config-dev.yml

3)/{application}-{profile}[/{label}]:
http://config-3344.com:3344/config/dev/master

重要配置细节总结:

2023 spring cloud 版本 选择 最新spring cloud_Cloud_128

14.3、Config客户端配置与测试

1、新建子模块:cloud-config-client-3355

2、添加pom依赖

<dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3、bootstap.yml

1)是什么?

2023 spring cloud 版本 选择 最新spring cloud_微服务_129


2)内容

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

3)说明

2023 spring cloud 版本 选择 最新spring cloud_spring_130


4、修改config-dev.yml配置并提交到GitHub中,比如加个变量age或者版本号version

5、主启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355 {
    public static void main(String[] args) {
        SpringApplication.run( ConfigClientMain3355.class,args);
    }
}

6、业务类

package com.atguigu.springcloud.Controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo(){
        return configInfo;
    }
}

7、测试
1)启动Config配置中心3344微服务并自测
http://config-3344.com:3344/master/config-dev.yml

2)启动3355作为Client准备访问
http://localhost:3355/configInfo

8、问题随时而来,分布式配置的动态刷新

  • Linux运维修改GitHub上的配置文件内容做调整
  • 刷新3344,发现ConfigServer配置中心立刻响应
  • 刷新3355,发现ConfigServer客户端没有任何响应
  • 3355没有变化除非自己重启或者重新加载
  • 难道每次运维修改配置文件,客户端都需要重启??噩梦

14.4、Config客户端之动态刷新

避免每次更新配置都要重启客户端微服务3355

动态刷新:
1、修改3355模块
2、POM引入actuator监控

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

3、修改YML,暴露监控端口

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

management:
  endpoints:
    web:
      exposure:
        include: "*"

4、@RefreshScope业务类Controller修改

package com.atguigu.springcloud.Controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RefreshScope
@RestController
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/configInfo")
    public String getConfigInfo(){
        return configInfo;
    }
}

5、测试

http://localhost:3355/configInfo

2023 spring cloud 版本 选择 最新spring cloud_Cloud_131


6、想想还有什么问题?

  • 假如有多个微服务客户端3355/3366/3377。。。。
  • 每个微服务都要执行一次post请求,手动刷新?
  • 可否广播,一次通知,处处生效?
  • 我们想大范围的自动刷新,求方法

15、SpringCloud Bus 消息总线

15.1、概述

1、是什么

2023 spring cloud 版本 选择 最新spring cloud_分布式_132


Bus支持两种消息代理:RabbitMQ和Kafka2、能干嘛

2023 spring cloud 版本 选择 最新spring cloud_spring_133


3、为何被称为总线

2023 spring cloud 版本 选择 最新spring cloud_Cloud_134

15.2、RabbitMQ环境配置

1、安装Erlang,下载地址:http://erlang.org/download/otp_win64_21.3.exe

2、安装RabbitMQ,下载地址:https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe

3、访问地址查看是否安装成功
http://localhost:15672/

4、输入账号密码并登录: guest guest

15.3、SpringCloud Bus动态刷新全局广播

1、必须先具备良好的RabbitMQ环境先

2、演示广播效果,增加复杂度,再以3355为模板再制作一个3366

  • 新建子模块:cloud-config-client-3366
  • 添加pom依赖
<dependencies>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>com.atguigu.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  • 配置bootstrap.yml
server:
  port: 3366

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
management:
  endpoints:
    web:
      exposure:
        include: "*"
  • 主启动类
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@SpringBootApplication
public class ConfigClientMain3366 {
    public static void main(String[] args) {
            SpringApplication.run( ConfigClientMain3366.class,args);
        }
}
  • controller
package com.atguigu.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RefreshScope
public class ConfigClientController {

    @Value("${server.port}")
    private String serverPort;

    @Value("${config.info}")
    private String configInfo;


    @GetMapping("/configInfo")
    public String getConfigInfo(){
        return "serverPort:"+serverPort+"\t\n\n configInfo: "+configInfo;
    }


}

3、设计思想

  • 利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_135


  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_136

  • 利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置(更加推荐)
  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_137


  • 2023 spring cloud 版本 选择 最新spring cloud_spring_138

  • 4、图二的架构显然更加合适,图一不适合的原因如下:
    1、打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新职责。
    2、破坏了微服务各节点的对等性。
    3、有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的修改。

5、给cloud-config-center-3344配置中心服务端添加消息总线支持
1)pom

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>

2)yml

server:
  port: 3344
spring:
  application:
    name: cloud-config-center
  cloud:
    config:
      server:
        git:
          uri:  https://github.com/hhf19906/springcloud-config.git  #git@github.com:hhf19906/springcloud-config.git
          search-paths:
            - springcloud-config
      label: master

rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone:  http://localhost:7001/eureka

management:
  endpoints:
    web:
      exposure:
        include: 'bus-refresh'

6、给cloud-config-center-3355客户端添加消息总线支持
1)pom

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>

2)yml

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344

rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
management:
  endpoints:
    web:
      exposure:
        include: "*"

7、给cloud-config-center-3366客户端添加消息总线支持
1)pom

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
		</dependency>

2)yml

server:
  port: 3366

spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      name: config
      profile: dev
      uri: http://localhost:3344


  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone:  http://localhost:7001/eureka

management:
  endpoints:
    web:
      exposure:
        include: '*'

8、测试
1)修改Github上配置文件增加版本号

2)发送Post请求
打开cmd命令窗口:curl -X POST “http://localhost:3344/actuator/bus-refresh”
一次发送,处处生效

访问:
http://config-3344.com/config-dev.yml http://localhost:3355/configInfo http://localhost:3366/configInfo

15.4、SpringCloud Bus动态刷新定点通知

1、简单一句话:

  • 指定具体某一个实例生效而不是全部
  • 公式:http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
  • /bus/refresh请求不再发送到具体的服务实例上,而是发给config server并通过destination参数类指定需要更新配置的服务或实例。

2、案例
我们这里以刷新运行在3355端口上的config-client为例
只通知3355,不通知3366

curl -X POST “http://localhost:3344/actuator/bus-refresh/config-client:3355”

2023 spring cloud 版本 选择 最新spring cloud_Cloud_139


3、通知总结All

2023 spring cloud 版本 选择 最新spring cloud_分布式_140

16、SpringCloud Stream消息驱动

16.1、消息驱动概述

1、是什么?

2023 spring cloud 版本 选择 最新spring cloud_Cloud_141


屏蔽底层消息中间件的差异,降低切换版本,统一消息的编程模型。2、官网

1)https://spring.io/projects/spring-cloud-stream#overview

2023 spring cloud 版本 选择 最新spring cloud_spring_142


2023 spring cloud 版本 选择 最新spring cloud_微服务_143


2)https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/3.0.1.RELEASE/reference/html/

3)Spring Cloud Stream中文指导手册
https://m.wang1314.com/doc/webapp/topic/20971999.html

3、设计思想

1)标准MQ

2023 spring cloud 版本 选择 最新spring cloud_Cloud_144

  • 生产者/消费者之间靠消息媒介传递信息内容:Message
  • 消息必须走特定的通道:消息通道MessageChannel
  • 消息通道里的消息如何被消费呢,谁负责收发处理:消息通道MessageChannel的子接口SubscribableChannel,由MessageHandler消息处理器订阅。

2)为什么用Cloud Stream

2023 spring cloud 版本 选择 最新spring cloud_Cloud_145


2023 spring cloud 版本 选择 最新spring cloud_Cloud_146


2023 spring cloud 版本 选择 最新spring cloud_spring_147

  • stream凭什么可以统一底层差异
  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_148


  • 2023 spring cloud 版本 选择 最新spring cloud_spring_149

  • Binder:
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_150


  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_151


  • 2023 spring cloud 版本 选择 最新spring cloud_Cloud_152

  • INPUT对应于消费者
  • OUTPUT对应于生产者

3)Stream中的消息通信方式遵循了发布-订阅模式

Topic主题进行广播:
在RabbitMQ就是Exchange
在kafka中就是Topic

4)Spring Cloud Stream标准流程套路

2023 spring cloud 版本 选择 最新spring cloud_分布式_153


2023 spring cloud 版本 选择 最新spring cloud_Cloud_154

  • Binder:很方便的连接中间件,屏蔽差异。
  • Channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过对Channel对队列进行配置。
  • Source和Sink:简单的可理解为参照对象是Spring Cloud Stream自身,从Stream发布消息就是输出,接受消息就是输入。

5)编码API和常用注解

2023 spring cloud 版本 选择 最新spring cloud_Cloud_155

16.2、案例说明

1、RabbitMQ环境已经OK

2、工程中新建三个子模块

  • cloud-stream-rabbitmq-provider8801,作为生产者进行发消息模块。
  • cloud-stream-rabbitmq-consumer8802,作为消息接收模块。
  • cloud-stream-rabbitmq-consumer8803,作为消息接收模块。

16.3、消息驱动之生产者

1、新建子模块:cloud-stream-rabbitmq-provider8801

2、添加pom依赖

<dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3、配置yml

server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息;
        defaultRabbit: # 表示定义的名称,用于于binding整合
          type: rabbit # 消息组件类型
          environment: # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服务的整合处理
        output: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: send-8801.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址

4、主启动类

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StreamMQMain8801 {
    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8801.class, args);
    }
}

5、业务类
1)发送消息接口

package com.atguigu.springcloud.service;


public interface IMessageProvider
{
    public String send();
}

2)发送消息接口实现类

@EnableBinding(Source.class) //定义消息的推送管道
public class MessageProviderImpl implements IMessageProvider
{
    @Resource
    private MessageChannel output; // 消息发送管道

    @Override
    public String send()
    {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("*****serial: "+serial);
        return null;
    }
}

3)Controller

@RestController
public class SendMessageController
{
    @Resource
    private IMessageProvider messageProvider;

    @GetMapping(value = "/sendMessage")
    public String sendMessage()
    {
        return messageProvider.send();
    }

}

6、测试
1)启动7001eureka

2)启动rabbitmq:http://localhost:15672/

3)访问:http://localhost:8801/sendMessage
不断的刷新页面来发送消息

2023 spring cloud 版本 选择 最新spring cloud_微服务_156


通过RabbitMQ来查看波峰。

16.4、消息驱动之消费者

1、新建子模块:cloud-stream-rabbitmq-consumer8802

2、添加pom依赖

<dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3、配置yml

server:
  port: 8802

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息;
        defaultRabbit: # 表示定义的名称,用于于binding整合
          type: rabbit # 消息组件类型
          environment: # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
          binder: defaultRabbit  # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
    instance-id: receive-8802.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址

4、主启动类

@SpringBootApplication
public class StreamMQMain8802 {

    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8802.class, args);
    }

}

5、业务类

@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    public void input(Message<String> message) {
        System.out.println("消费者1号,接受:"+message.getPayload()+"\t port:"+serverPort);
    }

}

6、测试8801发送8802接收消息

http://localhost:8801/sendMessage 不断刷新来发送消息

2023 spring cloud 版本 选择 最新spring cloud_spring_157


2023 spring cloud 版本 选择 最新spring cloud_Cloud_158

16.5、分组消费与持久化

1、依照8802,clone出来一份运行8803
cloud-stream-rabbitmq-consumer8803
除了端口号其他的都不变。

2、启动

  • RabbitMQ
  • 7001:服务注册
  • 8801:消息生产
  • 8802:消息消费
  • 8803:消息消费

3、运行后两个问题

  • 有重复消费问题
  • 消息持久化问题

4、消费
目前是8802/8803同时都收到了,存在重复消费问题

如何解决:
分组和持久化属性group

5、分组
1)原理
微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。

2)8802/8803都变成不同组,group两个不同
group: atguiguA、atguiguB

  • 8802修改YML
  • 8803加atguiguB

3)测试

2023 spring cloud 版本 选择 最新spring cloud_spring_159


看控制台,还是重复消费

4)8802/8803实现了轮询分组,每次只有一个消费者 8801模块的发的消息只能被8802或8803其中一个接收到,这样避免了重复消费。

5)8802/8803都变成相同组,group两个相同
8802/8803都改成group:atguiguA

6)结论

同一个组的多个微服务实例,每次只会有一个拿到

2023 spring cloud 版本 选择 最新spring cloud_Cloud_160


2023 spring cloud 版本 选择 最新spring cloud_微服务_161


2023 spring cloud 版本 选择 最新spring cloud_微服务_162


6、持久化

1、通过上述,解决了重复消费问题,再看看持久化

2、停止8802/8803并去除掉8802的分组group:atguiguA
8803的分组group:atguiguA没有去掉

3、8801先发送4条信息到rabbitmq

4、先启动8802,无分组属性配置,后台没有打出来消息

5、先启动8803,有分组属性配置,后台打出来了MQ上的消息

17、SpringCloud Sleuth分布式请求链路追踪

17.1、概述

1、为什么会出现这个技术?需要解决哪些问题?

问题:

2023 spring cloud 版本 选择 最新spring cloud_spring_163


2023 spring cloud 版本 选择 最新spring cloud_微服务_164


2、是什么

官网:https://github.com/spring-cloud/spring-cloud-sleuth

Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案

在分布式系统中提供追踪解决方案并且兼容支持了zipkin

3、解决

2023 spring cloud 版本 选择 最新spring cloud_Cloud_165

17.2、搭建链路监控步骤

1.zipkin
1)下载
SpringCloud从F版起已不需要自己构建Zipkin server了,只需要调用jar包即可
官网:https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/

zipkin-server-2.12.9.exec.jar

运行jar:java -jar zipkin-server-2.12.9-exec.jar

2023 spring cloud 版本 选择 最新spring cloud_spring_166


2)运行控制台

http://localhost:9411/zipkin/1、术语

完整的调用链路

2023 spring cloud 版本 选择 最新spring cloud_Cloud_167


上图解析

2023 spring cloud 版本 选择 最新spring cloud_分布式_168


名词解释

  • Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识
  • span:表示调用链路来源,通俗的理解span就是一次请求信息

2.服务提供者
cloud-provider-payment8001

修改POM

<!--包含了sleuth+zipkin-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

修改yml

2023 spring cloud 版本 选择 最新spring cloud_微服务_169


业务类PaymentController

@GetMapping("/payment/zipkin")
    public String paymentZipkin()
    {
        return "hi ,i'am paymentzipkin server fall back,welcome to atguigu,O(∩_∩)O哈哈~";
    }

3.服务消费者(调用方)
cloud-consumer-order80

修改POM

<!--包含了sleuth+zipkin-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

修改yml

2023 spring cloud 版本 选择 最新spring cloud_微服务_170


业务类OrderController

@GetMapping("/consumer/payment/zipkin")
    public String paymentZipkin()
    {
        String result = restTemplate.getForObject("http://localhost:8001"+"/payment/zipkin/", String.class);
        return result;
    }

4.依次启动eureka7001/8001/80

2023 spring cloud 版本 选择 最新spring cloud_分布式_171


刷新几次5.打开浏览器访问:http:localhost:9411

会出现以下界面

2023 spring cloud 版本 选择 最新spring cloud_微服务_172


查看

2023 spring cloud 版本 选择 最新spring cloud_微服务_173


原理

2023 spring cloud 版本 选择 最新spring cloud_spring_174

18、SpringCloud Alibaba入门简介

1、why会出现SpringCloud alibaba

1)Spring Cloud Netflix项目进入维护模式

官网:https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now

2023 spring cloud 版本 选择 最新spring cloud_Cloud_175


说明

2023 spring cloud 版本 选择 最新spring cloud_分布式_176


2023 spring cloud 版本 选择 最新spring cloud_分布式_177


2)SpringCloud NetFlix Projects Entering Maintenance Mode

什么是维护模式

2023 spring cloud 版本 选择 最新spring cloud_分布式_178


进入维护模式意味着什么呢?

2023 spring cloud 版本 选择 最新spring cloud_微服务_179


2023 spring cloud 版本 选择 最新spring cloud_微服务_180


2、SpringCloud alibaba带来了什么?

1)是什么

2023 spring cloud 版本 选择 最新spring cloud_spring_181


2)能干嘛

2023 spring cloud 版本 选择 最新spring cloud_spring_182


3)去哪下

官网:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

4)怎么玩
Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。

Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。

Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。

Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。

Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。

Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

3、SpringCloud alibaba学习资料获取

1)官网:https://spring.io/projects/spring-cloud-alibaba#overview

2023 spring cloud 版本 选择 最新spring cloud_Cloud_183


2023 spring cloud 版本 选择 最新spring cloud_Cloud_184


2)英文

https://github.com/alibaba/spring-cloud-alibaba

https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html

3)中文
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

19、SpringCloud Alibaba Nacos服务注册和配置中心

19.1、Nacos简介

1)为什么叫Nacos?
前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service。

2)是什么

  • 一个更易于构建云原生应用的动态服务发现,配置管理和服务管理中心
  • Nacos:Dynamic Naming and Configuration Service
  • Nacos就是注册中心+配置中心的组合,等价于Nacos = Eureka+Config+Bus

3)能干嘛

  • 替代Eureka做服务注册中心
  • 替代Config做服务配置中心

4)去哪下
https://github.com/alibaba/Nacos

官网文档:
https://nacos.io/zh-cn/index.html

https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_discovery

5)各种注册中心比较

2023 spring cloud 版本 选择 最新spring cloud_Cloud_185

19.2、安装并运行Nacos

1、本地Java8+Maven环境已经OK

2、先从官网下载Nacos
https://github.com/alibaba/nacos/releases/tag

3、解压安装包,直接运行bin目录下的startup.cmd

4、命令运行成功后直接访问http://localhost:8848/nacos
默认账号密码都是nacos

5、结果页面

2023 spring cloud 版本 选择 最新spring cloud_分布式_186

19.3、Nacos作为服务注册中心演示

基于Nacos的服务提供者
1、新建子模块:cloudalibaba-provider-payment9001
2、添加pom
1)父POM

<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  <version>2.1.0.RELEASE</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>

2)本模块POM

<dependencies>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.62</version>
    </dependency>

    </dependencies>

3、配置yml

server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

4、主启动类

@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9001.class,args);
    }
}

5、业务类

@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/nacos/{id}")
    public String getPayment(@PathVariable("id") Integer id)
    {
        return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
    }
}

6、测试

运行9001后,查看nacos界面

2023 spring cloud 版本 选择 最新spring cloud_Cloud_187


以上nacos服务注册中心+服务提供者9001都ok了。

7、为了演示nacos的负载均衡,参照9001新建9002
新建子模块:cloudalibaba-provider-payment9002
其他代码跟9001一样。9001改成9002

启动9002

2023 spring cloud 版本 选择 最新spring cloud_分布式_188


访问:http://lcoalhost:9001/payment/nacos/1

2023 spring cloud 版本 选择 最新spring cloud_Cloud_189


访问:http://lcoalhost:9002/payment/nacos/1

2023 spring cloud 版本 选择 最新spring cloud_spring_190

基于Nacos的服务消费者
1、新建子模块:cloudalibaba-consumer-nacos-order83
2、添加pom

<dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3、配置yml

server:
  port: 83


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848


service-url:
  nacos-user-service: http://nacos-payment-provider

4、主启动类

@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain83
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderNacosMain83.class,args);
    }
}

5、业务类
config:

@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

controller:

@RestController
@Slf4j
public class OrderNacosController
{
    @Resource
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @GetMapping(value = "/consumer/payment/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Long id)
    {
        return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
    }

}

6、测试

启动85

2023 spring cloud 版本 选择 最新spring cloud_分布式_191


访问:http://localhost:83/consumer/payment/nacos/13

不断刷新,83访问轮番9001/9002,轮询负载OK。

服务注册中心对比
各种注册中心对比:

  • Nacos全景图所示
  • Nacos和CAP


    切换:
    Nacos支持AP和CP模式的切换

    切换命令:curl -X PUT ‘$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP’

19.4、Nacos作为服务配置中心演示

Nacos作为配置中心-基础配置
1、新建子模块:cloudalibaba-config-nacos-client3377
2、添加pom

<dependencies>

        <!--nacos-config-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!--nacos-discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--web + actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--一般基础配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3、配置YML

why配置两个

2023 spring cloud 版本 选择 最新spring cloud_微服务_192


1)bootstrap.yml

server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册中心地址
      config:
        server-addr: localhost:8848 #配置中心地址
        file-extension: yaml #指定yaml格式的配置
  1. application.yml
spring:
  profiles:
    active: dev

4、主启动类

@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377
{
    public static void main(String[] args) {
        SpringApplication.run(NacosConfigClientMain3377.class, args);
    }
}

5、业务类

@RestController
@RefreshScope
public class ConfigClientController
{
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}

@RefreshScope:

2023 spring cloud 版本 选择 最新spring cloud_spring_193

6、在Nacos中添加配置信息
Nacos中的匹配规则:

1)理论:
Nacos中的dataid的组成格式与SpringBoot配置文件中的匹配规则

官网: https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html

2023 spring cloud 版本 选择 最新spring cloud_spring_194


2023 spring cloud 版本 选择 最新spring cloud_分布式_195


2)实操

配置新增:nacos-config-client-dev.yaml

2023 spring cloud 版本 选择 最新spring cloud_spring_196


注意:一定要写yaml,切勿写成yml,nacos识别不出来ymlNacos界面配置对应

2023 spring cloud 版本 选择 最新spring cloud_spring_197


config:

info: nacos config center,version = 1

设置DataId:

  • 公式:2023 spring cloud 版本 选择 最新spring cloud_分布式_198{spring.profile.active}.${spring.cloud.nacos.config.file-extension}
  • prefix默认为spring.application.name的值
  • spring.profile.active既为当前环境对应的profile,可以通过配置项spring.profile.active 来配置
  • file-exetension为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension配置
  • 小总结说明:

    7、测试
    运行cloud-config-nacos-client3377的主启动类

访问:http://localhost:3377/config/info

8、自带动态刷新

修改下Nacos中的yaml配置文件,再次调用查看配置的接口,就会发现配置已经刷新。

2023 spring cloud 版本 选择 最新spring cloud_微服务_199


2023 spring cloud 版本 选择 最新spring cloud_分布式_200

Nacos作为配置中心-分类配置

1、问题

多环境多项目管理

2023 spring cloud 版本 选择 最新spring cloud_spring_201


2、Nacos的图形化管理界面

1)配置管理

2023 spring cloud 版本 选择 最新spring cloud_spring_202


2)命名空间

2023 spring cloud 版本 选择 最新spring cloud_微服务_203


3、Namespace+Group+Data ID三者关系?为什么这么设计?

2023 spring cloud 版本 选择 最新spring cloud_微服务_204


2023 spring cloud 版本 选择 最新spring cloud_分布式_205


4、Case

1)DataID方案

指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置。

默认空间+默认分组+新建dev和test两个DataID

新建test配置DataID:

2023 spring cloud 版本 选择 最新spring cloud_spring_206


通过spring.profile.active属性就能进行多环境下配置文件的读取

2023 spring cloud 版本 选择 最新spring cloud_Cloud_207


5、测试

访问:http://localhost:3377/config/info

2023 spring cloud 版本 选择 最新spring cloud_spring_208


2)Group方案

通过Group实现环境区分

新建两个Group

2023 spring cloud 版本 选择 最新spring cloud_微服务_209


在config下增加一条group的配置即可。可配置为DEV_GROUP或TEST_GROUP。

2023 spring cloud 版本 选择 最新spring cloud_spring_210


测试:

2023 spring cloud 版本 选择 最新spring cloud_spring_211


3)Namespace方案新建dev/test的Namespace

2023 spring cloud 版本 选择 最新spring cloud_微服务_212


回到服务管理-服务列表查看

2023 spring cloud 版本 选择 最新spring cloud_微服务_213


按照域名配置填写

2023 spring cloud 版本 选择 最新spring cloud_spring_214


修改YML

2023 spring cloud 版本 选择 最新spring cloud_Cloud_215


2023 spring cloud 版本 选择 最新spring cloud_分布式_216


测试:

2023 spring cloud 版本 选择 最新spring cloud_Cloud_217

19.5、Nacos集群和持久化配置(重要)

19.5.1、官网说明

1、官网:https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html

2、官网架构图

2023 spring cloud 版本 选择 最新spring cloud_Cloud_218


3、上图官网翻译,真实情况

2023 spring cloud 版本 选择 最新spring cloud_微服务_219


2023 spring cloud 版本 选择 最新spring cloud_微服务_220


4、说明

2023 spring cloud 版本 选择 最新spring cloud_分布式_221


2023 spring cloud 版本 选择 最新spring cloud_微服务_222


2023 spring cloud 版本 选择 最新spring cloud_微服务_223


按照上述,我们需要mysql数据库

官网:https://nacos.io/zh-cn/docs/deployment.html

重点说明

2023 spring cloud 版本 选择 最新spring cloud_spring_224


2023 spring cloud 版本 选择 最新spring cloud_spring_225

19.5.2、Nacos持久化配置解释

1、Nacos默认自带的是嵌入式数据库derby
官网:https://github.com/alibaba/nacos/blob/develop/config/pom.xml

2、derby到mysql切换配置步骤
1)nacos-server-1.1.4\nacos\conf目录下找到sql脚本
nacos-mysql.sql
执行脚本

2)nacos-server-1.1.4\nacos\conf目录下找到application.properties

spring.datasource.platform=mysql
 
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root(自己的用户名)
db.password=root(自己的密码)

启动nacos,可以看到是个全新的空记录界面,以前是记录进derby

19.5.3、Linux版Nacos+MySQL生产环境配置

1、集群配置步骤(重点)

1)Linux服务器上mysql数据库配置

SQL脚本在哪里

2023 spring cloud 版本 选择 最新spring cloud_分布式_226


自己Linux机器上的Mysql数据库黏贴

执行后结果

2023 spring cloud 版本 选择 最新spring cloud_分布式_227


2)application.properties配置

位置

2023 spring cloud 版本 选择 最新spring cloud_Cloud_228


内容

2023 spring cloud 版本 选择 最新spring cloud_微服务_229


mysql 授权远程访问

GRANT ALL PRIVILEGES ON . TO ‘root’@’%’ IDENTIFIED BY ‘123456’ WITH GRANT OPTION;

flush privileges;3)Linux服务器上nacos的集群配置cluster.conf

梳理出3台nacos机器的不同服务端口号

复制出cluster.conf

2023 spring cloud 版本 选择 最新spring cloud_分布式_230


内容

2023 spring cloud 版本 选择 最新spring cloud_spring_231


这个IP不能写127.0.0.1,必须是Linux命令hostname -i能够识别的IP

2023 spring cloud 版本 选择 最新spring cloud_分布式_232


4)编辑Nacos的启动脚本startup.sh,使它能够接受不同的启动端

  • /mynacos/nacos/bin目录下有startup.sh
  • 在什么地方,修改什么,怎么修改
  • 思考
  • 2023 spring cloud 版本 选择 最新spring cloud_微服务_233

  • 修改内容
  • 2023 spring cloud 版本 选择 最新spring cloud_spring_234


  • 2023 spring cloud 版本 选择 最新spring cloud_spring_235


  • 2023 spring cloud 版本 选择 最新spring cloud_微服务_236

  • 执行方式
  • 2023 spring cloud 版本 选择 最新spring cloud_spring_237

  • 5)Nginx的配置,由它作为负载均衡器
  • 修改nginx的配置文件
  • 2023 spring cloud 版本 选择 最新spring cloud_分布式_238

  • nginx.conf
    upstream cluster{
    server 127.0.0.1:3333;
    server 127.0.0.1:4444;
    server 127.0.0.1:5555;
    } server{
    listen 1111;
    server_name localhost;
    location /{
    proxy_pass http://cluster;
    }
    …省略

2023 spring cloud 版本 选择 最新spring cloud_spring_239

  • 按照指定启动

    6)截止到此处,1个Nginx+3个nacos注册中心+1个mysql
    测试通过nginx访问nacos
    https://写你自己虚拟机的ip:1111/nacos/#/login

新建一个配置测试

2023 spring cloud 版本 选择 最新spring cloud_spring_240


linux服务器的mysql插入一条记录

2023 spring cloud 版本 选择 最新spring cloud_分布式_241


2、测试

微服务cloudalibaba-provider-payment9002启动注册进nacos集群

yml

server-addr:  写你自己的虚拟机ip:1111

高可用小总结

2023 spring cloud 版本 选择 最新spring cloud_Cloud_242

20、SpringCloud Alibaba Sentinel实现熔断与限流

20.1、Sentinel简介

1、官网:https://github.com/alibaba/Sentinel

2、中文官网:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

3、是什么

2023 spring cloud 版本 选择 最新spring cloud_Cloud_243


一句话解释,之前我们讲解过的Hystrix

4、去哪下
https://github.com/alibaba/Sentinel/releases

5、能干嘛

2023 spring cloud 版本 选择 最新spring cloud_微服务_244


6、怎么玩

https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

服务使用中的各种问题

  • 服务雪崩
  • 服务降级
  • 服务熔断
  • 服务限流

20.2、安装Sentinel控制台

1、sentinel组件由2部分组成

2023 spring cloud 版本 选择 最新spring cloud_Cloud_245


2、安装步骤

1)下载

https://github.com/alibaba/Sentinel/releases

2)运行命令
前提
java8环境OK
8080端口不能被占用

命令
java -jar sentinel-dashboard-1.7.2.jar

3)访问sentinel管理界面
http://localhost:8080 登录账号密码均为sentinel

20.3、初始化演示工程

1、启动Nacos8848成功
http://localhost:8848/nacos/#/login

2、Module
1)创建子模块:cloudalibaba-sentinel-service8401
2)添加pom依赖

<dependencies>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3)yml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719  #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口

management:
  endpoints:
    web:
      exposure:
        include: '*'

4)主启动类

@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401
{
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
    }
}

5)业务类FlowLimitController

@RestController
public class FlowLimitController
{
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB() {

        return "------testB";
    }
}

3、启动Sentinel8080
java -jar sentinel-dashboard-1.7.2

4、启动微服务8401

5、启动8401微服务后查看sentienl控制台

Sentinel采用的懒加载说明

执行一次访问即可
http://localhost:8401/testA http://localhost:8401/testB

效果

2023 spring cloud 版本 选择 最新spring cloud_spring_246


结论:

sentinel8080正在监控微服务8401

20.4、流控规则

1、基本介绍

2023 spring cloud 版本 选择 最新spring cloud_Cloud_247


进一步解释说明

2023 spring cloud 版本 选择 最新spring cloud_spring_248


2023 spring cloud 版本 选择 最新spring cloud_微服务_249


2、流控模式

1)直接(默认)

直接->快速失败
系统默认

配置及说明

2023 spring cloud 版本 选择 最新spring cloud_微服务_250


测试:

快速点击访问http://localhost:8401/testA

结果:
Blocked by Sentinel (flow limiting)

思考???
直接调用默认报错信息,技术方面OK but,是否应该有我们自己的后续处理?
类似有一个fallback的兜底方法?

2)关联
是什么?

  • 当关联的资源达到阈值时,就限流自己
  • 当与A关联的资源B达到阈值后,就限流自己
  • B惹事,A挂了

配置A

2023 spring cloud 版本 选择 最新spring cloud_微服务_251


postman模拟并发密集访问testB

2023 spring cloud 版本 选择 最新spring cloud_spring_252

  • 访问testB成功
  • postman里新建多线程集合组
  • 将访问地址添加进新线程组

    Run
    大批量线程高并发访问B,导致A失效了

运行后发现testA挂了
点击访问http://localhost:8401/testA

结果:
Blocked by Sentinel (flow limiting)

3)链路
多个请求调用了同一个微服务

3、流控效果
1)直接->快速失败(默认的流控处理)
直接失败,抛出异常
Blocked by Sentinel (flow limiting)

2)源码
com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

3)预热

a.说明

公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值

b.官网

2023 spring cloud 版本 选择 最新spring cloud_微服务_253


2023 spring cloud 版本 选择 最新spring cloud_spring_254


默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值。

限流 冷启动
https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8

源码:

com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

2023 spring cloud 版本 选择 最新spring cloud_分布式_255

Warmup配置

2023 spring cloud 版本 选择 最新spring cloud_微服务_256


多次点击http://localhost:8401/testB

刚开始不行,后续慢慢OK应用场景

2023 spring cloud 版本 选择 最新spring cloud_Cloud_257


4) 排队等待

2023 spring cloud 版本 选择 最新spring cloud_spring_258


匀速排队,阈值必须设置为QPS官网:

2023 spring cloud 版本 选择 最新spring cloud_微服务_259


2023 spring cloud 版本 选择 最新spring cloud_spring_260


源码

com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController测试

2023 spring cloud 版本 选择 最新spring cloud_微服务_261

20.5、降级规则

RT

1、是什么

2023 spring cloud 版本 选择 最新spring cloud_spring_262


2023 spring cloud 版本 选择 最新spring cloud_Cloud_263


2、测试

1)代码

@GetMapping("/testD")
    public String testD()
    {
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("testD 测试RT");

        return "------testD";
    }

2)配置

2023 spring cloud 版本 选择 最新spring cloud_微服务_264


3)jmeter压测4)结论

2023 spring cloud 版本 选择 最新spring cloud_微服务_265


2023 spring cloud 版本 选择 最新spring cloud_分布式_266

异常比例

1、是什么

2023 spring cloud 版本 选择 最新spring cloud_微服务_267


2023 spring cloud 版本 选择 最新spring cloud_微服务_268


2、测试

1)代码

@GetMapping("/testD")
    public String testD()
    {

        log.info("testD 测试RT");
        int age = 10/0;
        return "------testD";
    }

2)配置

2023 spring cloud 版本 选择 最新spring cloud_分布式_269


3)jmeter

2023 spring cloud 版本 选择 最新spring cloud_分布式_270


4)结论

2023 spring cloud 版本 选择 最新spring cloud_Cloud_271

异常数

1、是什么

2023 spring cloud 版本 选择 最新spring cloud_分布式_272


2023 spring cloud 版本 选择 最新spring cloud_spring_273


2、异常数是按照分钟统计的

3、测试
1)代码

@GetMapping("/testE")
public String testE()
{
    log.info("testE 测试异常数");
    int age = 10/0;
    return "------testE 测试异常数";
}

2)配置

2023 spring cloud 版本 选择 最新spring cloud_spring_274


3)jmeter压测

20.6、热点key限流

1、是什么

2023 spring cloud 版本 选择 最新spring cloud_Cloud_275


2、官网

https://github.com/alibaba/Sentinel/wiki/热点参数限流3、承上启下复习start

@SentinelResource

2023 spring cloud 版本 选择 最新spring cloud_Cloud_276


4、代码

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2) {
    //int age = 10/0;
    return "------testHotKey";
}
 
//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
    return "------deal_testHotKey,o(╥﹏╥)o";  
}

5、配置

2023 spring cloud 版本 选择 最新spring cloud_spring_277


1)第一种方法

@SentinelResource(value = “testHotKey”)

异常打到了前台用户界面看不到,不友好

2)第二种方法
@SentinelResource(value = “testHotKey”,blockHandler = “deal_testHotKey”)
方法testHostKey里面第一个参数只要QPS超过每秒1次,马上降级处理
用了我们自己定义的

6、测试

2023 spring cloud 版本 选择 最新spring cloud_分布式_278


7、参数例外项

上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流

1)特殊情况
普通:超过1秒钟一个后,达到阈值1后马上被限流
我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
特例:假如当p1的值等于5时,它的阈值可以达到200

2)配置

2023 spring cloud 版本 选择 最新spring cloud_微服务_279


3)测试

http://localhost:8401/testHotKey?p1=5

当p1等于5的时候,阈值变为200
当p1不等于5的时候,阈值就是平常的1

前提条件:热点参数的注意点,参数必须是基本类型或者String

其他

手贱添加异常看看…

2023 spring cloud 版本 选择 最新spring cloud_分布式_280


2023 spring cloud 版本 选择 最新spring cloud_Cloud_281

20.7、系统规则

1、是什么
官网:https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

2、各项配置参数说明

2023 spring cloud 版本 选择 最新spring cloud_spring_282


3、配置全局QPS

20.8、@SentinelResource

按资源名称限流+后续处理
1、启动Nacos成功
2、启动Sentinel成功
3、修改cloudalibaba-sentinel-service8401
1)POM

<dependency>
    <groupId>com.atguigu.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

2)业务类RateLimitController

@RestController
public class RateLimitController
{
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource()
    {
        return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
    }
    public CommonResult handleException(BlockException exception)
    {
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
    }

4、配置流控规则

1)配置步骤

2023 spring cloud 版本 选择 最新spring cloud_分布式_283


2)表示1秒钟内查询次数大于1,就跑到我们自定义的处流,限流5、测试

2023 spring cloud 版本 选择 最新spring cloud_分布式_284


1秒钟点击1下,OK1秒多次点击:

2023 spring cloud 版本 选择 最新spring cloud_spring_285


6、额外问题

此时关闭微服务8401看看

Sentinel控制台,流控规则消失了?????

临时/持久?

按照Url地址限流+后续处理
通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息

业务类RateLimitController:

@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl()
{
    return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
}

访问一次

2023 spring cloud 版本 选择 最新spring cloud_spring_286


Sentinel控制台配置:

2023 spring cloud 版本 选择 最新spring cloud_微服务_287


测试:

疯狂点击http://localhost:8401/rateLimit/byUrl

结果:

2023 spring cloud 版本 选择 最新spring cloud_分布式_288


上面兜底方法面临的问题:

2023 spring cloud 版本 选择 最新spring cloud_微服务_289

客户自定义限流处理逻辑
1、创建customerBlockHandler类用于自定义限流处理逻辑
2、自定义限流处理类:CustomerBlockHandler

package com.atguigu.springcloud.controller.myhandler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;

public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception){
        return new CommonResult(444,"按客户自定义,global handlerException----1");
    }

    public static CommonResult handlerException2(BlockException exception){
        return new CommonResult(444,"按客户自定义,global handlerException----2");
    }

}

3、RateLimitController

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class,
        blockHandler = "handlerException2")
public CommonResult customerBlockHandler()
{
    return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
}

4、启动微服务后先调用一次

http://localhost:8401/rateLimit/customerBlockHandler

2023 spring cloud 版本 选择 最新spring cloud_spring_290


5、Sentinel控制台配置

2023 spring cloud 版本 选择 最新spring cloud_微服务_291


一秒一下,没问题,但疯狂点击后我们自定义的兜底方法就会出来了

2023 spring cloud 版本 选择 最新spring cloud_微服务_292


6、进一步说明

2023 spring cloud 版本 选择 最新spring cloud_微服务_293

更多注解属性说明

2023 spring cloud 版本 选择 最新spring cloud_Cloud_294


2023 spring cloud 版本 选择 最新spring cloud_Cloud_295


多说一句:

2023 spring cloud 版本 选择 最新spring cloud_微服务_296


Sentinel主要有三个核心API:

  • SphU定义资源
  • Tracer定义统计
  • ContextUtil定义了上下文

20.9、服务熔断功能

20.9.1、Ribbon系列

1、sentinel整合ribbon+openFeign+fallback
启动nacos和sentinel

2、提供者9003/9004
1)新建cloudalibaba-provider-payment9003/9004
2)pom

<dependencies>

        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--日常通用jar包配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3)yml:记得修改不同的端口号

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

4)主启动类

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class, args);
    }
}

5)业务类

@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    public static HashMap<Long, Payment> hashMap = new HashMap<>();
    static{
        hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
        hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
        hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
    }

    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
        return result;
    }
}

6)测试

http://localhost:9003/paymentSQL/1

2023 spring cloud 版本 选择 最新spring cloud_Cloud_297


端口号改为9004再测试一下9004微服务工程

3、消费者84
1)新建cloudalibaba-consumer-nacos-order84
2) POM

<dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3)yml

server:
  port: 84


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

service-url:
  nacos-user-service: http://nacos-payment-provider

4)主启动类:

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84
{
    public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class, args);
    }
}

5)业务类
a.ApplicationContextConfig

@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

b.CircleBreakerController的全部源码

@RestController
@Slf4j
public class CircleBreakerController {
   
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

   
    
    @RequestMapping("/consumer/fallback/{id}")
    //@SentinelResource(value = "fallback") //没有配置
    //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常
    //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class})
    public CommonResult<Payment> fallback(@PathVariable Long id) {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }
  
    //fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }
  
    //blockHandler
    public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }


}

6)测试

http://localhost:84/consumer/fallback/1

2023 spring cloud 版本 选择 最新spring cloud_分布式_298


9003、9004交替出现即实现了互载均衡

20.9.2、Feign系列

1、修改84模块:
1)pom

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2)yml

#对Feign的支持
feign:
  sentinel:
    enabled: true

3)业务类
带@FeignClient注解的业务接口:

package com.atguigu.springcloud.alibaba.service;


import com.atguigu.springcloud.alibaba.entities.CommonResult;
import com.atguigu.springcloud.alibaba.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;


@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

PaymentFallbackService实现类

@Component
public class PaymentFallbackService implements PaymentService
{
    @Override
    public CommonResult<Payment> paymentSQL(Long id)
    {
        return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
    }
}

Controller:

// OpenFeign
@Resource
private PaymentService paymentService;

@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
    return paymentService.paymentSQL(id);
}

主启动:

添加@EnableFeignClients启动Feign的功能

2023 spring cloud 版本 选择 最新spring cloud_微服务_299


2、测试

http://lcoalhost:84/consumer/openfeign/1

2023 spring cloud 版本 选择 最新spring cloud_分布式_300


测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死熔断框架比较:

2023 spring cloud 版本 选择 最新spring cloud_Cloud_301


2023 spring cloud 版本 选择 最新spring cloud_Cloud_302

20.10、规则持久化

1、是什么?
一旦我们重启应用,Sentinel规则将消失,生产环境需要将配置规则进行持久化。

2、怎么玩
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效。

3、修改cloudalibaba-sentinel-service8401
1)POM

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

2)YML

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

management:
  endpoints:
    web:
      exposure:
        include: '*'

feign:
  sentinel:
    enabled: true # 激活Sentinel对Feign的支持

3)添加Nacos业务规则配置

2023 spring cloud 版本 选择 最新spring cloud_Cloud_303


2023 spring cloud 版本 选择 最新spring cloud_Cloud_304


内容解析:

[
    {
         "resource": "/retaLimit/byUrl",
         "limitApp": "default",
         "grade":   1,
         "count":   1,
         "strategy": 0,
         "controlBehavior": 0,
         "clusterMode": false    
    }
]

2023 spring cloud 版本 选择 最新spring cloud_微服务_305


4、启动8401后刷新sentinel发现业务规则有了

2023 spring cloud 版本 选择 最新spring cloud_分布式_306


5、快速点击刷新来测试接口

默认

2023 spring cloud 版本 选择 最新spring cloud_Cloud_307


6、停止8401再看sentinel

2023 spring cloud 版本 选择 最新spring cloud_spring_308


7、重新启动8401再看sentinel

扎一看还是没有,稍等一会儿

多次调用:http://localhost:8401/rateLimit/byUrl

重新配置出现了,持久化验证通过

21、SpringCloud Alibaba Seata处理分布式事务

21.1、分布式事务问题

1、分布式前
单机单库没这个问题
从1:1 -> 1:N -> N: N

2、分布式之后

2023 spring cloud 版本 选择 最新spring cloud_微服务_309


2023 spring cloud 版本 选择 最新spring cloud_微服务_310


3、一句话

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题。

21.2、Seata简介

1、是什么
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。

2、官网地址
http://seata.io/zh-cn/

3、能干嘛
一个典型的分布式事务过程
分布式事务处理过程的-ID+三组件模型

1)Transaction ID XID
全局唯一的事务ID

2)3组件概念

  • Transaction Coordinator(TC) :事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
  • Transaction Manager™ :控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
  • Resource Manager(RM) :控制分支事务,负责分支注册,状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚;

3)处理过程

2023 spring cloud 版本 选择 最新spring cloud_分布式_311


2023 spring cloud 版本 选择 最新spring cloud_微服务_312


4、去哪下

发布说明:https://github.com/seata/seata/releases

5、怎么玩
Spring 本地@Transactional
全局@GlobalTransactional

SEATA的分布式交易解决方案

2023 spring cloud 版本 选择 最新spring cloud_Cloud_313

21.3、Seata-Server安装与配置

1、官网地址
http://seata.io/zh-cn/

2、下载版本

3、seata-server-0.9.0.zip解压到指定目录并修改conf目录下的file.conf配置文件。
1)先备份原始file.conf文件
2)主要修改:自定义事务组名称+事务日志存储模式为db+数据库连接信息。
3)file.conf
service模块:

vgroup_mapping.my_test_tx_group = “fsp_tx_group”

store模块:

mode = "db"
 
  url = "jdbc:mysql://127.0.0.1:3306/seata"
  user = "root"
  password = "你自己的密码"

4、数据库新建库seata

5、在seata库里建表

-- the table to store GlobalSession data
drop table if exists `global_table`;
create table `global_table` (
  `xid` varchar(128)  not null,
  `transaction_id` bigint,
  `status` tinyint not null,
  `application_id` varchar(32),
  `transaction_service_group` varchar(32),
  `transaction_name` varchar(128),
  `timeout` int,
  `begin_time` bigint,
  `application_data` varchar(2000),
  `gmt_create` datetime,
  `gmt_modified` datetime,
  primary key (`xid`),
  key `idx_gmt_modified_status` (`gmt_modified`, `status`),
  key `idx_transaction_id` (`transaction_id`)
);
 
-- the table to store BranchSession data
drop table if exists `branch_table`;
create table `branch_table` (
  `branch_id` bigint not null,
  `xid` varchar(128) not null,
  `transaction_id` bigint ,
  `resource_group_id` varchar(32),
  `resource_id` varchar(256) ,
  `lock_key` varchar(128) ,
  `branch_type` varchar(8) ,
  `status` tinyint,
  `client_id` varchar(64),
  `application_data` varchar(2000),
  `gmt_create` datetime,
  `gmt_modified` datetime,
  primary key (`branch_id`),
  key `idx_xid` (`xid`)
);
 
-- the table to store lock data
drop table if exists `lock_table`;
create table `lock_table` (
  `row_key` varchar(128) not null,
  `xid` varchar(96),
  `transaction_id` long ,
  `branch_id` long,
  `resource_id` varchar(256) ,
  `table_name` varchar(32) ,
  `pk` varchar(36) ,
  `gmt_create` datetime ,
  `gmt_modified` datetime,
  primary key(`row_key`)
);

6、修改seata-server-0.9.0\seata\conf目录下的registry.conf配置文件

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"
 
  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }

**目的是:指明注册中心为nacos,及修改nacos连接信息
**

7、先启动Nacos端口号8848
8、再启动seata-server
seata-server.bat

21.4、订单/库存/账户业务数据库准备

1、以下演示都需要先启动Nacos后启动Seata,保证两个都OK
Seata没启动报错no available server to connect

2、分布式事务业务说明

业务说明

2023 spring cloud 版本 选择 最新spring cloud_Cloud_314


下订单–>扣库存–>减账户(余额)

3、创建业务数据库
seata_order: 存储订单的数据库
seata_storage:存储库存的数据库
seata_account: 存储账户信息的数据库

建表SQL:
CREATE DATABASE seata_order;

CREATE DATABASE seata_storage;

CREATE DATABASE seata_account;

4、按照上述3库分别建对应业务表
seata_order库下建t_order表:

CREATE TABLE t_order(
    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
    `count` INT(11) DEFAULT NULL COMMENT '数量',
    `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
    `status` INT(1) DEFAULT NULL COMMENT '订单状态:0:创建中; 1:已完结'
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
 
SELECT * FROM t_order;

seata_storage库下建t_storage表:

CREATE TABLE t_storage(
    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
   `'total` INT(11) DEFAULT NULL COMMENT '总库存',
    `used` INT(11) DEFAULT NULL COMMENT '已用库存',
    `residue` INT(11) DEFAULT NULL COMMENT '剩余库存'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
 
INSERT INTO seata_storage.t_storage(`id`,`product_id`,`total`,`used`,`residue`)
VALUES('1','1','100','0','100');
 
 
SELECT * FROM t_storage;

seata_account库下建t_account表:

CREATE TABLE t_account(
    `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',
    `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    `total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',
    `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额',
    `residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
 
INSERT INTO seata_account.t_account(`id`,`user_id`,`total`,`used`,`residue`) VALUES('1','1','1000','0','1000')
 
 
 
SELECT * FROM t_account;

5、按照上述3库分别建对应的回滚日志表
订单-库存-账户3个库下都需要建各自的回滚日志表
建表SQL:

drop table `undo_log`;
CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

6、最终效果

2023 spring cloud 版本 选择 最新spring cloud_Cloud_315

21.5、订单/库存/账户业务微服务准备

21.5.1、新建订单Order-Module

1、创建子模块:seata-order-service2001
2、添加pom依赖:

<dependencies>

        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--web-actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--mysql-druid-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

3、yml

server:
  port: 2001

spring:
  application:
    name: seata-order-service
  cloud:
    alibaba:
      seata:
        #自定义事务组名称需要与seata-server中的对应
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_order
    username: root
    password: root

feign:
  hystrix:
    enabled: false

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml

4、file.conf:

transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}

service {

  vgroup_mapping.fsp_tx_group = "default"

  default.grouplist = "127.0.0.1:8091"
  enableDegrade = false
  disable = false
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
  disableGlobalTransaction = false
}


client {
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 10
    retry.times = 30
  }
  report.retry.count = 5
  tm.commit.retry.count = 1
  tm.rollback.retry.count = 1
}

## transaction log store
store {
  ## store mode: file、db
  mode = "db"

  ## file store
  file {
    dir = "sessionStore"

    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    max-branch-session-size = 16384
    # globe session size , if exceeded throws exceptions
    max-global-session-size = 512
    # file buffer size , if exceeded allocate new buffer
    file-write-buffer-cache-size = 16384
    # when recover batch read size
    session.reload.read_size = 100
    # async, sync
    flush-disk-mode = async
  }

  ## database store
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "root"
    password = "root"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
}
lock {
  ## the lock store mode: local、remote
  mode = "remote"

  local {
    ## store locks in user's database
  }

  remote {
    ## store locks in the seata's server
  }
}
recovery {
  #schedule committing retry period in milliseconds
  committing-retry-period = 1000
  #schedule asyn committing retry period in milliseconds
  asyn-committing-retry-period = 1000
  #schedule rollbacking retry period in milliseconds
  rollbacking-retry-period = 1000
  #schedule timeout retry period in milliseconds
  timeout-retry-period = 1000
}

transaction {
  undo.data.validation = true
  undo.log.serialization = "jackson"
  undo.log.save.days = 7
  #schedule delete expired undo_log in milliseconds
  undo.log.delete.period = 86400000
  undo.log.table = "undo_log"
}

## metrics settings
metrics {
  enabled = false
  registry-type = "compact"
  # multi exporters use comma divided
  exporter-list = "prometheus"
  exporter-prometheus-port = 9898
}

support {
  ## spring
  spring {
    # auto proxy the DataSource bean
    datasource.autoproxy = false
  }
}

5、registry.conf

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"

  nacos {
    serverAddr = "localhost"
    namespace = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

6、domain
CommonResult:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>
{
    private Integer code;
    private String  message;
    private T       data;
 
    public CommonResult(Integer code, String message)
    {
        this(code,message,null);
    }
}

Order:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order
{
    private Long id;
 
    private Long userId;
 
    private Long productId;
 
    private Integer count;
 
    private BigDecimal money;
 
    private Integer status; //订单状态:0:创建中;1:已完结
}

7、Dao接口及实现
OrderDao:

@Mapper
public interface OrderDao
{
    //新建订单
    void create(Order order);
 
    //修改订单状态,从零改为1
    void update(@Param("userId") Long userId,@Param("status") Integer status);
}

resources文件夹下新建mapper文件夹后添加
OrderMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 
<mapper namespace="com.atguigu.springcloud.alibaba.dao.OrderDao">
 
    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.alibaba.domain.Order">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="user_id" property="userId" jdbcType="BIGINT"/>
        <result column="product_id" property="productId" jdbcType="BIGINT"/>
        <result column="count" property="count" jdbcType="INTEGER"/>
        <result column="money" property="money" jdbcType="DECIMAL"/>
        <result column="status" property="status" jdbcType="INTEGER"/>
    </resultMap>
 
    <insert id="create">
        insert into t_order (id,user_id,product_id,count,money,status)
        values (null,#{userId},#{productId},#{count},#{money},0);
    </insert>
 
 
    <update id="update">
        update t_order set status = 1
        where user_id=#{userId} and status = #{status};
    </update>
 
</mapper>

8.Service接口及实现
OrderService:

public interface OrderService{
    void create(Order order);
}

OrderServiceImpl:

@Service
@Slf4j
public class OrderServiceImpl implements OrderService
{
    @Resource
    private OrderDao orderDao;
    @Resource
    private StorageService storageService;
    @Resource
    private AccountService accountService;
 
    /**
     * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
     */
     
    @Override
    @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
    public void create(Order order){
        log.info("----->开始新建订单");
        //新建订单
        orderDao.create(order);
 
        //扣减库存
        log.info("----->订单微服务开始调用库存,做扣减Count");
        storageService.decrease(order.getProductId(),order.getCount());
        log.info("----->订单微服务开始调用库存,做扣减end");
 
        //扣减账户
        log.info("----->订单微服务开始调用账户,做扣减Money");
        accountService.decrease(order.getUserId(),order.getMoney());
        log.info("----->订单微服务开始调用账户,做扣减end");
 
         
        //修改订单状态,从零到1代表已经完成
        log.info("----->修改订单状态开始");
        orderDao.update(order.getUserId(),0);
        log.info("----->修改订单状态结束");
 
        log.info("----->下订单结束了");
 
    }
}

StorageService:

@FeignClient(value = "seata-storage-service")
public interface StorageService{
    @PostMapping(value = "/storage/decrease")
    CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}

AccountService:

@FeignClient(value = "seata-account-service")
public interface AccountService{
    @PostMapping(value = "/account/decrease")
    CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}

9.Controller:

@RestController
public class OrderController{
    @Resource
    private OrderService orderService;
 
 
    @GetMapping("/order/create")
    public CommonResult create(Order order)
    {
        orderService.create(order);
        return new CommonResult(200,"订单创建成功");
    }
}

10.Config配置:
MyBatisConfig:

@Configuration
@MapperScan({"com.atguigu.springcloud.alibaba.dao"})
public class MyBatisConfig {
 
}

DataSourceProxyConfig:

@Configuration
public class DataSourceProxyConfig {
 
 
    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;
 
    
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }
 
    
    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
 
    
    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
 
}

11.主启动:

@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动创建的配置
public class SeataOrderMainApp2001{
 
    public static void main(String[] args)
    {
        SpringApplication.run(SeataOrderMainApp2001.class, args);
    }
}

21.5.2、新建库存Storage-Module

1、创建子模块:seata-order-service2002
2.POM:

<dependencies>

        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

3、yml

server:
  port: 2002

spring:
  application:
    name: seata-storage-service
  cloud:
    alibaba:
      seata:
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_storage
    username: root
    password: root

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml

4.file.conf

transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}

service {
  #vgroup->rgroup
  vgroup_mapping.fsp_tx_group = "default"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
  disableGlobalTransaction = false
}

client {
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 10
    retry.times = 30
  }
  report.retry.count = 5
  tm.commit.retry.count = 1
  tm.rollback.retry.count = 1
}

transaction {
  undo.data.validation = true
  undo.log.serialization = "jackson"
  undo.log.save.days = 7
  #schedule delete expired undo_log in milliseconds
  undo.log.delete.period = 86400000
  undo.log.table = "undo_log"
}

support {
  ## spring
  spring {
    # auto proxy the DataSource bean
    datasource.autoproxy = false
  }
}

5.registry.conf

registry {
  # file 、nacos 、eureka、redis、zk
  type = "nacos"

  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6381"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk
  type = "file"

  nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
  apollo {
    app.id = "fescar-server"
    apollo.meta = "http://192.168.1.204:8801"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  file {
    name = "file.conf"
  }
}

6.domain
CommonResult:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>
{
    private Integer code;
    private String  message;
    private T       data;
 
    public CommonResult(Integer code, String message)
    {
        this(code,message,null);
    }
}

Storage:

@Data
public class Storage {
 
    private Long id;
 
    // 产品id
    private Long productId;
 
    //总库存
    private Integer total;
 
 
    //已用库存
    private Integer used;
 
  
    //剩余库存
    private Integer residue;
}

7.Dao接口及实现
StorageDao:

@Mapper
public interface StorageDao {
 
 
    //扣减库存信息
    void decrease(@Param("productId") Long productId, @Param("count") Integer count);
}

resources文件夹下新建mapper文件夹后添加:
StorageMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 
 
<mapper namespace="com.atguigu.springcloud.alibaba.dao.StorageDao">
 
    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.alibaba.domain.Storage">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="product_id" property="productId" jdbcType="BIGINT"/>
        <result column="total" property="total" jdbcType="INTEGER"/>
        <result column="used" property="used" jdbcType="INTEGER"/>
        <result column="residue" property="residue" jdbcType="INTEGER"/>
    </resultMap>
 
    <update id="decrease">
        UPDATE
            t_storage
        SET
            used = used + #{count},residue = residue - #{count}
        WHERE
            product_id = #{productId}
    </update>
 
</mapper>

8.Service接口及实现:
StorageService:

public interface StorageService {
    
     // 扣减库存
    void decrease(Long productId, Integer count);
}

StorageServiceImpl:

@Service
public class StorageServiceImpl implements StorageService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(StorageServiceImpl.class);
 
    @Resource
    private StorageDao storageDao;
 
     // 扣减库存
    @Override
    public void decrease(Long productId, Integer count) {
        LOGGER.info("------->storage-service中扣减库存开始");
        storageDao.decrease(productId,count);
        LOGGER.info("------->storage-service中扣减库存结束");
    }
}

9.Controller:

@RestController
public class StorageController {
 
    @Autowired
    private StorageService storageService;
 
 
    //扣减库存
    @RequestMapping("/storage/decrease")
    public CommonResult decrease(Long productId, Integer count) {
        storageService.decrease(productId, count);
        return new CommonResult(200,"扣减库存成功!");
    }
}

10.Config配置
MyBatisConfig:

@Configuration
@MapperScan({"com.atguigu.springcloud.alibaba.dao"})
public class MyBatisConfig {
}

DataSourceProxyConfig:

@Configuration
public class DataSourceProxyConfig {
 
    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;
 
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }
 
    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
 
    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
 
}

11.主启动

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class SeataStorageServiceApplication2002
{
    public static void main(String[] args)
    {
        SpringApplication.run(SeataStorageServiceApplication2002.class, args);
    }
}

21.5.3、新建账户Account-Module

1、创建子模块:seata-order-service2003
2.POM

<dependencies>

        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>seata-all</artifactId>
                    <groupId>io.seata</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-all</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!--feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

3、yml

server:
  port: 2003

spring:
  application:
    name: seata-account-service
  cloud:
    alibaba:
      seata:
        tx-service-group: fsp_tx_group
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_account
    username: root
    password: root

feign:
  hystrix:
    enabled: false

logging:
  level:
    io:
      seata: info

mybatis:
  mapperLocations: classpath:mapper/*.xml

4.file.conf

transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}

service {

  vgroup_mapping.fsp_tx_group = "default" #修改自定义事务组名称

  default.grouplist = "127.0.0.1:8091"
  enableDegrade = false
  disable = false
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
  disableGlobalTransaction = false
}


client {
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 10
    retry.times = 30
  }
  report.retry.count = 5
  tm.commit.retry.count = 1
  tm.rollback.retry.count = 1
}

## transaction log store
store {
  ## store mode: file、db
  mode = "db"

  ## file store
  file {
    dir = "sessionStore"

    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    max-branch-session-size = 16384
    # globe session size , if exceeded throws exceptions
    max-global-session-size = 512
    # file buffer size , if exceeded allocate new buffer
    file-write-buffer-cache-size = 16384
    # when recover batch read size
    session.reload.read_size = 100
    # async, sync
    flush-disk-mode = async
  }

  ## database store
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "root"
    password = "root"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
}
lock {
  ## the lock store mode: local、remote
  mode = "remote"

  local {
    ## store locks in user's database
  }

  remote {
    ## store locks in the seata's server
  }
}
recovery {
  #schedule committing retry period in milliseconds
  committing-retry-period = 1000
  #schedule asyn committing retry period in milliseconds
  asyn-committing-retry-period = 1000
  #schedule rollbacking retry period in milliseconds
  rollbacking-retry-period = 1000
  #schedule timeout retry period in milliseconds
  timeout-retry-period = 1000
}

transaction {
  undo.data.validation = true
  undo.log.serialization = "jackson"
  undo.log.save.days = 7
  #schedule delete expired undo_log in milliseconds
  undo.log.delete.period = 86400000
  undo.log.table = "undo_log"
}

## metrics settings
metrics {
  enabled = false
  registry-type = "compact"
  # multi exporters use comma divided
  exporter-list = "prometheus"
  exporter-prometheus-port = 9898
}

support {
  ## spring
  spring {
    # auto proxy the DataSource bean
    datasource.autoproxy = false
  }
}

5.registry.conf

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"

  nacos {
    serverAddr = "localhost"
    namespace = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

6.domain
CommonResult:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>
{
    private Integer code;
    private String  message;
    private T       data;
 
    public CommonResult(Integer code, String message)
    {
        this(code,message,null);
    }
}

Account:

public class Account {
 
    private Long id;
 
    /**
     * 用户id
     */
    private Long userId;
 
    /**
     * 总额度
     */
    private BigDecimal total;
 
    /**
     * 已用额度
     */
    private BigDecimal used;
 
    /**
     * 剩余额度
     */
    private BigDecimal residue;
}

7.Dao接口及实现
AccountDao:

@Mapper
public interface AccountDao {
 
    /**
     * 扣减账户余额
     */
    void decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);
}

resources文件夹下新建mapper文件夹后添加:
AccountMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 
<mapper namespace="com.atguigu.springcloud.alibaba.dao.AccountDao">
 
    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.alibaba.domain.Account">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="user_id" property="userId" jdbcType="BIGINT"/>
        <result column="total" property="total" jdbcType="DECIMAL"/>
        <result column="used" property="used" jdbcType="DECIMAL"/>
        <result column="residue" property="residue" jdbcType="DECIMAL"/>
    </resultMap>
 
    <update id="decrease">
        UPDATE t_account
        SET
          residue = residue - #{money},used = used + #{money}
        WHERE
          user_id = #{userId};
    </update>
 
</mapper>

8.Service接口及实现
AccountService:

public interface AccountService {
 
    /**
     * 扣减账户余额
     */
    void decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}

AccountServiceImpl:

/**
 * 账户业务实现类
 */
@Service
public class AccountServiceImpl implements AccountService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
 
 
    @Resource
    AccountDao accountDao;
 
    /**
     * 扣减账户余额
     */
    @Override
    public void decrease(Long userId, BigDecimal money) {
        
         LOGGER.info("------->account-service中扣减账户余额开始");
        try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
        accountDao.decrease(userId,money);
        LOGGER.info("------->account-service中扣减账户余额结束");
    }
}

9.Controller

@RestController
public class AccountController {
 
    @Resource
    AccountService accountService;
 
    /**
     * 扣减账户余额
     */
    @RequestMapping("/account/decrease")
    public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money){
        accountService.decrease(userId,money);
        return new CommonResult(200,"扣减账户余额成功!");
    }
}

10.Config配置
MyBatisConfig:

@Configuration
@MapperScan({"com.atguigu.springcloud.alibaba.dao"})
public class MyBatisConfig {
 
}

DataSourceProxyConfig:

@Configuration
public class DataSourceProxyConfig {
 
    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;
 
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }
 
    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
 
    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSourceProxy);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }
 
}

11.主启动

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class SeataAccountMainApp2003
{
    public static void main(String[] args)
    {
        SpringApplication.run(SeataAccountMainApp2003.class, args);
    }
}

21.5.4、Test

1、下订单->减库存->扣余额->改(订单)状态

2023 spring cloud 版本 选择 最新spring cloud_Cloud_316


2023 spring cloud 版本 选择 最新spring cloud_分布式_317


2、数据库初始情况

2023 spring cloud 版本 选择 最新spring cloud_分布式_318


3、正常下单

http://localhost:2001/order/create?userid=1&producrid=1&counr=10&mnotallow=100数据库情况

2023 spring cloud 版本 选择 最新spring cloud_spring_319


4、超时异常,没加@GlobalTransactional

AccountServiceImpl添加超时

故障情况:

当库存和账户余额扣减后,订单状态并没有设置为已经完成,没有从零改为1

而且由于feign的重试机制,账户余额还有可能被多次扣减

5、超时异常,添加@GlobalTransactional
AccountServiceImpl添加超时
OrderServiceImpl@GlobalTransactional
下单后数据库数据并没有任何改变:记录都添加不进来

21.6、Seata之原理

1、Seata
2019年1月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案
Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架

2、2020起初,用1.0以后的版本

2023 spring cloud 版本 选择 最新spring cloud_分布式_320


3、再看TC/TM/RM三大组件

2023 spring cloud 版本 选择 最新spring cloud_Cloud_321


分布式事务的执行流程:

  • TM开启分布式事务(TM向TC注册全局事务记录)
  • 换业务场景,编排数据库,服务等事务内资源(RM向TC汇报资源准备状态)
  • TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务)
  • TC汇总事务信息,决定分布式事务是提交还是回滚
  • TC通知所有RM提交/回滚资源,事务二阶段结束。

4、AT模式如何做到对业务的无侵入

1)是什么

2023 spring cloud 版本 选择 最新spring cloud_微服务_322


2023 spring cloud 版本 选择 最新spring cloud_分布式_323


2)一阶段加载

2023 spring cloud 版本 选择 最新spring cloud_spring_324


2023 spring cloud 版本 选择 最新spring cloud_微服务_325


3)二阶段提交

2023 spring cloud 版本 选择 最新spring cloud_Cloud_326


4)二阶段回滚

2023 spring cloud 版本 选择 最新spring cloud_分布式_327


2023 spring cloud 版本 选择 最新spring cloud_spring_328


5、补充

2023 spring cloud 版本 选择 最新spring cloud_Cloud_329


2023 spring cloud 版本 选择 最新spring cloud_spring_330