SpringCloud学习笔记

文章目录

  • 一、SpringCloud概述
    • SpringCloud微服务全家桶
  • 二、微服务架构编码构建(订单——支付微服务)
    • 1)总览
    • 2)代码
      • cloud-api-common
      • cloud-provider-payment8001
      • cloud-cosumer-order80
    • 3)总结
  • 三、服务注册中心(Eureka、Zookeeper、Consul)
    • 3.1Eureka
      • 3.1.1Eureka概述
      • 3.1.2 单机Eurake
        • 3.1.2.1 单机Eurake服务器端
        • 3.1.2.2 provider注册进EurekaSever
        • 3.1.2.3 consumer注册进EurekaSever
      • 3.1.3 集群Eureka(高可用)
        • 3.1.3.1 集群原理
        • 3.1.3.2 其他服务注册进集群
      • 3.1.4 服务发现
      • 3.1.5 自我保护
        • 3.1.5.1自我保护概念——高可用的设计思想
    • 3.2 Zookeeper
      • 3.2.1创建新的pay模块cloud-provider-payment8004 的 Module
      • 3.2.2 创建order模块cloud-consumerzk-order80
      • 3.2.3 zookeeper里面都是临时节点
    • 3.3 Consul
      • 3.2.1 下载
      • 3.2.2 创建新的pay模块cloud-providerconsul-payment8006
    • 3.4 注册中心的异同
  • 四、Ribbon负载均衡
    • 4.1 概念
    • 4.2 Ribbon = 负载均衡 + RestTemplate
    • 4.3 步骤
    • 4.4 更改负载均衡算法
    • 4.5 自定义负载均衡算法
      • 4.5.1 轮询规则RoundRobinRule.java源码
      • 4.5.2 手写自己的负载均衡的规则
  • 五、OpenFeign服务调用
    • 5.1 步骤
    • 5.2 OpenFeign超时控制
    • 5.3 OpenFeign日志
  • 六、Hystrix断路器
    • 6.1 概念
    • 6.2 提供者
    • 6.3 消费者
    • 6.4 服务降级(兜底方法)
      • 6.4.1 提供者fallback
      • 6.4.2 消费者fallback
      • 6.4.3 代码膨胀——全局fallback
      • 6.4.4 全局fallback改进版
    • 6.5 服务熔断(恢复链路)
      • 6.5.1 提供者service
      • 6.5.2 总结
      • 6.5.3 DashBoard
  • 七、Nacos服务注册
    • 7.1 安装nacos
    • 7.2 单机nacos
    • 7.3 集群nacos
    • 7.4 注册中心对比
  • 八、Nacos配置中心
    • 8.1 本地端配置
    • 8.2 Nacos配置中心配置
    • 8.3 Nacos分类配置
    • 8.4 Nacos集群和持久化配置
      • 8.4.1 官方架构图
  • 九、Sentinel 限流
    • 9.1 测试
    • 9.2 流控规则
    • 9.3 流控模式——直接(默认)
    • 9.4 流控模式——关联
    • 9.5 流控模式——链路
    • 9.6 流控效果——快速失败(默认)
    • 9.7 流控效果——Warm up
    • 9.8 流控效果——排队等待
    • 9.9 热点Key限流
      • 备选方案
      • 热点key设置
      • 例外参数
  • 十、Sentinel熔断降级
    • 10.1 Sentinel熔断策略——慢调用比例RT
    • 10.2 Sentinel熔断策略——异常比例
    • 10.3 Sentinel熔断策略——异常数
    • 10.4 规则持久化
  • 十一、Seata分布式处理
    • 11.1 面对的问题——分布式业务问题
    • 11.2 Seata分布式解决方案
    • 11.3 Seata理论
    • 11.4 Seata的AT模式
      • 11.4.1 一阶段加载
      • 11.4.2 二阶段提交(正常情况)
      • 11.4.3 二阶段回滚(异常情况)

一、SpringCloud概述

  1. 微服务是一种架构风格
  2. 将单个应用程序开发为一组小服务
  3. 每个服务在自己的进程中运行,服务与服务之间通过HTTP进行通信
  4. 服务自治

SpringCloud微服务全家桶

SpringCloud学习笔记_第1张图片

二、微服务架构编码构建(订单——支付微服务)

1)总览

SpringCloud学习笔记_第2张图片
本项目主要针对两个端口,80(即默认端口,现在访问网站都是自动默认80,可省略),8001。

端口号 作用
8001 微服务的提供者,支付module
80 微服务的消费者,下订单module

其中将两个微服务都会用到的entities实体类放在maven工程“cloud-api-common”下,clean,install,再在另两个微服务的pom中引入依赖即可使用。

2)代码

父pom:采用dependencyManagement这样能够在子pom直接继承这里的版本号,原本要写gav,到子pom中可以不写v了,并可以统一进行

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.atguigu.springcloud</groupId>
  <artifactId>cloud2020</artifactId>
  <version>1.0-SNAPSHOT</version>
  <modules>
    <module>cloud-provider-payment8001</module>
    <module>cloud-consumer-order80</module>
      <module>cloud-api-common</module>
  </modules>
  <packaging>pom</packaging>

  <!--统一管理jar包和版本-->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</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.verison>1.1.16</druid.verison>
    <mybatis.spring.boot.verison>1.3.0</mybatis.spring.boot.verison>
  </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>
      <!-- MySql -->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
      </dependency>
      <!-- Druid -->
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>${druid.verison}</version>
      </dependency>
      <!-- mybatis-springboot整合 -->
      <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis.spring.boot.verison}</version>
      </dependency>
      <!--lombok-->
      <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
        <scope>provided</scope>
      </dependency>
      <!--junit-->
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
      </dependency>
      <!-- log4j -->
      <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
      <!-- log4j日志系统 -->
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</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>

</project>

cloud-api-common

entities包中
CommonResult类

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);
    }
}

Payment类

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;
}

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-api-common</artifactId>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <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>

        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>



</project>

再clean,install,其他微服务只需引入依赖即可

cloud-provider-payment8001

SpringCloud学习笔记_第3张图片
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/cloud?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

子pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8001</artifactId>
    <dependencies>
        <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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <!--如果没写版本,从父层面找,找到了就直接用,全局统一-->
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <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>



</project>

sqlyog建表:

CREATE TABLE `payment`(
	`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
	`serial` VARCHAR(200) DEFAULT '' ,
	 PRIMARY KEY (`id`)
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

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);
}

由于采用了mybatis,写成mapper

<?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">

    <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>

service层PaymentService接口

package com.atguigu.springcloud.service;

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

public interface PaymentSerice {
    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.PaymentSerice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PaymentServiceImpl implements PaymentSerice {
    @Autowired
    private PaymentDao paymentDao;


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

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

controller:

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentSerice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class PaymentController {
    @Autowired
    private PaymentSerice paymentSerice;

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentSerice.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 getPaymentById(@PathVariable("id") Long id) {
        Payment payment = paymentSerice.getPaymentById(id);
        log.info("查询结果:"+payment);
        if(payment!=null){
            return new CommonResult(1200,"查询成功",payment);
        }else{
            return new CommonResult(444,"没有对应记录",null);
        }
    }


}

最重要的是记住:controller调用了service,service调用dao层。

由于前后端交互采用json串,所以在controller层返回值均为CommonResult类

cloud-cosumer-order80

SpringCloud学习笔记_第4张图片
yml:

server:
  port: 80

作为消费者类没有service和dao,而是需要调用pay模块的方法,并且这里还没有微服务的远程调用,那么如果要调用另外一个模块,则需要使用基本的api调用,可以使用RestTemplate调用pay模块。

RestTemplate介绍:
​ 原本发送Http请求我们会使用封装好的HttpClient工具类,而Spring为我们提供了RestTemplate工具集。它提供了多种便携访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类。

config配置类

package com.atguigu.springcloud.congif;

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();
    }
}

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.beans.factory.annotation.Autowired;
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;

@RestController
@Slf4j
public class OrderController {

    public static final String PAYMENT_URL = "http://localhost:8001";
    @Autowired
    private RestTemplate restTemplate;

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

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

}

代码结束

3)总结

SpringCloud学习笔记_第5张图片
目前搭建了简单的“消费者80端口调用提供者端口的8001项目”,消费者下订单,跳转提供者支付功能,注意到现在是两个端口,但是实际上可能有消费者集群和提供者集群,这样就必须需要一些技术来提供服务注册。

三、服务注册中心(Eureka、Zookeeper、Consul)

已经实现了两个服务模块一个端口通过http调用另一个端口,当模块多时就不好监控了,引入注册中心,避免单点故障,采取集群

3.1Eureka

3.1.1Eureka概述

Eureka包含了两个组件:

  • Eureka Server(物业公司):提供注册服务,各个微服务节点会在其中进行注册。
  • Eureka Client(居民):一个Java客户端,用于简化与Eureka Server 的交互,同时会定时向Eureka Server发送心跳,如果Eureka Server 在多个心跳周期内没有接收到某个节点的心跳则会将该节点移除。
    SpringCloud学习笔记_第6张图片

3.1.2 单机Eurake

3.1.2.1 单机Eurake服务器端

上面的图,consumer是80,provider是8001,都有了,缺少eureka server,创建他
SpringCloud学习笔记_第7张图片
pom文件:注意第一个spring-cloud-starter-netflix-eureka-server代表这是服务器


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020artifactId>
        <groupId>com.atguigu.springcloudgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>cloud-eruka-server7001artifactId>
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
        dependency>

        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonartifactId>
            <version>${project.version}version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
        dependency>

    dependencies>



project>

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交互的地址查询服务和注册服务都需要依赖这个地址



主启动类:注意注解@EnableEurekaSever代表这个是服务器端
SpringCloud学习笔记_第8张图片
启动访问localhost:7001,得到:
SpringCloud学习笔记_第9张图片

3.1.2.2 provider注册进EurekaSever

更改之前的8001端口服务即可,cloud-provider-payment8001
添加pom

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>

添加yml:


eureka:
  client:
  #注册到eureka
    register-with-eureka: true
    #需要检索服务
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

主启动加上注解

运行得到:
SpringCloud学习笔记_第10张图片
SpringCloud学习笔记_第11张图片

3.1.2.3 consumer注册进EurekaSever

同样是client端,pom与上面相同,yml也更改相同。

SpringCloud学习笔记_第12张图片

3.1.3 集群Eureka(高可用)

为了实现高可用(不能总搬家)
SpringCloud学习笔记_第13张图片
RPC:Remote Procedure Call 远程过程调用

3.1.3.1 集群原理

互相注册,相互守望,对外暴露一个整体。
如图,两个server,互相注册,7001注册进7002, 7002注册进7001.
SpringCloud学习笔记_第14张图片
由于是单机实现两种,具体步骤看https://blog.csdn.net/MOKEXFDGH/article/details/107258228#comments_16609600

3.1.3.2 其他服务注册进集群

将支付模块、订单模块都这样改即可
SpringCloud学习笔记_第15张图片
SpringCloud学习笔记_第16张图片
上图中有两个服务提供者,负载均衡步骤:

  1. 将订单oeder的代码写成:
  2. 在order的配置类中添加@loadbalance注解
    SpringCloud学习笔记_第17张图片
    使用此注解赋予了负载均衡的能力!默认轮询,8001与8002轮着上

3.1.4 服务发现

在8001的PaymentController中加入如下代码:

@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;
}

在8001主启动类上面写注解@EnableDiscoveryClient
SpringCloud学习笔记_第18张图片
SpringCloud学习笔记_第19张图片

得到“关于我们”的说明!

3.1.5 自我保护

在这里插入图片描述

3.1.5.1自我保护概念——高可用的设计思想

某时刻可能某个微服务不可用了,但是Eureka不会立即清理,而是会对其的信息进行保存。
属于CAP的AP分支。
正常情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,就会注销该实例。

3.2 Zookeeper

在linux上安装好zookeeper,再关闭linux的防火墙。

  1. docker拉取zookeeper
    SpringCloud学习笔记_第20张图片

3.2.1创建新的pay模块cloud-provider-payment8004 的 Module

  1. pom依赖,相比之前的pay模块,添加如下
		<!--SpringBoot整合Zookeeper客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <exclusions>
                <!--先排除自带的zookeeper3.5.3-->
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加zookeeper3.6.1版本-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.6.1</version>
		 </dependency>
  1. 配置文件,配置端口号、微服务名称、zookeeper地址
    server:
      port: 8004
    spring:
      application:
        name: cloud-provider-payment
        cloud:
          zookeeper:
            connect-string: hw.mokespace.cn:2181

  1. 主启动类
        @SpringBootApplication
        @EnableDiscoveryClient
        public class PaymentMain8004 {
            public static void main(String[] args){
                SpringApplication.run(PaymentMain8004.class,args);
            }
        }

  1. Controller
    @RestController
    @Slf4j
    @RequestMapping("/payment")
    public class PaymentController {
        @Value("${server.port}")
        private String serverPort;

        @GetMapping("/zk")
        public String paymentzk(){
            return "springcloud with zookeeper"+serverPort+"\t"+ UUID.randomUUID().toString();
        }
    }

  1. 启动后就能发现已经注册进了zookeeper中
    SpringCloud学习笔记_第21张图片
    SpringCloud学习笔记_第22张图片
    在这里插入图片描述

3.2.2 创建order模块cloud-consumerzk-order80

  1. 创建名称为 cloud-consumerzk-order80 的 Module
  2. pom、配置文件、RestTemplate 与8004类似
  3. controller
        @RestController
        @Slf4j
        @RequestMapping("/consumer")
        public class OrderController {

            private static final String INVOKE_URL = "http://cloud-provider-payment";

            @Resource
            private RestTemplate restTemplate;

            @GetMapping("/payment/zk")
            public String paymentInfo(){
                return restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class);
            }
        }

在这里插入图片描述

3.2.3 zookeeper里面都是临时节点

一段时间内有就还有。没了就没了。Zookeeper比Eureka更加干脆

3.3 Consul

  • 服务发现:提供HTTP和DNS两种发现方式
  • 健康检测:支持HTTP、TCP、Docker、Shell等方式
  • KV存储:key-value的存储方式
  • 多数据中心:Consul支持多数据中心
  • 可视化 Web 界面

3.2.1 下载

https://www.consul.io/downloads
中文教程 springcloud.cc/spring-cloud-consul.html

3.2.2 创建新的pay模块cloud-providerconsul-payment8006

  1. pom依赖
		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

  1. yml
        server:
          port: 8004
        spring:
          application:
            name: cloud-provider-payment
          cloud:
            consul:
              host: localhost
              port: 8500
              discovery:
                service-name: ${spring.application.name}

主启动类、controller 和 8004 差不多,编写好后启动
SpringCloud学习笔记_第23张图片

3.4 注册中心的异同

SpringCloud学习笔记_第24张图片
SpringCloud学习笔记_第25张图片
SpringCloud学习笔记_第26张图片
在这里插入图片描述
SpringCloud学习笔记_第27张图片
SpringCloud学习笔记_第28张图片
SpringCloud学习笔记_第29张图片

四、Ribbon负载均衡

4.1 概念

SpringCloud学习笔记_第30张图片
Nginx当成是个大门,全进去再分
Ribbon就是在家就分好了

4.2 Ribbon = 负载均衡 + RestTemplate

SpringCloud学习笔记_第31张图片
consumer继承了Ribbon。

Ribbon步骤
SpringCloud学习笔记_第32张图片

4.3 步骤

pom

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

这里就已经自带了ribbon

RestTemplate类

xxxForObject()方法:返回的是响应体中的数据
xxxForEntity()方法:返回的是entity对象,包含响应体数据、响应体信息(状态码等)

  • 默认的轮询SpringCloud学习笔记_第33张图片

Ribbon默认自带的负载均衡的算法:
SpringCloud学习笔记_第34张图片
com.netflix.loadbalancer.RoundRobinRule:轮询
com.netflix.loadbalancer.RandomRule:随机
com.netflix.loadbalancer.RetryRule:先轮询,失败则在指定时间内进行重试
WeightedResponseTimeRule:对轮询的扩展,响应速度越快权重越大
BestAvailableRule:先过滤处于断路状态的服务,选择一个并发量最小的服务
AvailabilityFilteringRule:先过滤故障实例,再选择并发较小的实例
ZoneAvoidanceRule:默认规则,根据Server所在区域的性能和可用性选择服务器。

4.4 更改负载均衡算法

改成随机

修改Order模块:

  • 这里使用 Eureka 的相关服务

  • IRule 自定义配置类不能放在@ComponentScan所扫描的当前包以及子包下(有@SpringBootApplication注解的Main所在的包)源码中有@CompontScan

  • 包结构
    SpringCloud学习笔记_第35张图片

  • 在这个包中创建配置类

        @Configuration
        public class MySelfRule {
            @Bean
            public IRule myRule(){
                return new RandomRule();//随机负载均衡算法
            }
        }

  • 在主启动类上加@RibbonClient注解,表示访问CLOUD-PAYMENT-SERVICE服务时,使用我们自定义的负载均衡算法。
        @SpringBootApplication
        @EnableEurekaClient
        @RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
        public class OrderMain80 {
            public static void main(String[] args){
                SpringApplication.run(OrderMain80.class,args);
            }
        }

4.5 自定义负载均衡算法

4.5.1 轮询规则RoundRobinRule.java源码

CAS+自旋锁 的典型应用
SpringCloud学习笔记_第36张图片

public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        } else {
            Server server = null;
            int count = 0;

            while(true) {
                if (server == null && count++ < 10) {
                    List<Server> reachableServers = lb.getReachableServers();
                    List<Server> allServers = lb.getAllServers();
                    int upCount = reachableServers.size();
                    int serverCount = allServers.size();有效集群的总数量
                    if (upCount != 0 && serverCount != 0) {
                        int nextServerIndex = this.incrementAndGetModulo(serverCount);
                        server = (Server)allServers.get(nextServerIndex);
                        if (server == null) {
                            Thread.yield();
                        } else {
                            if (server.isAlive() && server.isReadyToServe()) {
                                return server;
                            }

                            server = null;
                        }
                        continue;
                    }

                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }

                if (count >= 10) {
                    log.warn("No available alive servers after 10 tries from load balancer: " + lb);
                }

                return server;
            }
        }
    }

4.5.2 手写自己的负载均衡的规则

  1. 给pay模块(8001,8002)的controller方法添加一个方法,返回当前节点端口
 @GetMapping("/lb")
        public String getPaymentLB(){
            return serverPort;
        }
  1. 修改order模块,去掉ApplicationContextConfig中的 @LoadBalanced 注解,以及Main上的@RibbonClient注解。
    SpringCloud学习笔记_第37张图片
  2. 自定义接口(放在spring能扫描到的包下)
        public interface LoadBalancer {
            //从服务列表中选取一个服务
            ServiceInstance instances(List<ServiceInstance> serviceInstances);
        }

  1. 写上面接口的实现类(放在spring能扫描到的包下)
        @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 >= Integer.MAX_VALUE ? 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);
            }
        }

  1. 修改controller
        @Resource
        private LoadBalancer loadBalancer;//自定义的
        @Resource
        private DiscoveryClient discoveryClient;
        @GetMapping("/payment/lb")
        public String getPaymentLB(){
            List<ServiceInstance> serviceInstances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
            if(serviceInstances == null || serviceInstances.size() <= 0){
                return null;
            }
            ServiceInstance instance = loadBalancer.instances(serviceInstances);
            URI url = instance.getUri();
            return restTemplate.getForObject(url+"/payment/lb",String.class);
        }

五、OpenFeign服务调用

已经有Ribbon了,OpenFeign是干嘛的?
是Ribbon的进一步封装,仅需写接口以后加注解
SpringCloud学习笔记_第38张图片

5.1 步骤

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

  1. yml
        server:
          port: 80

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

  1. 主启动
        @SpringBootApplication
        @EnableFeignClients //开启
        public class OrderFeginMain80 {
            public static void main(String[] args){
                SpringApplication.run(OrderFeginMain80.class,args);
            }
        }

  1. Feign调用其他服务的接口
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentService {

    @GetMapping("/payment/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id);
}

  1. Controller
        @RestController
        @Slf4j
        @RequestMapping("/consumer")
        public class OrderFeginController {

            @Resource
            private PaymentService paymentService;

            @GetMapping("/payment/get/{id}")
            public CommonResult<Payment> getPyamentById(@PathVariable("id")Long id){
                return paymentService.getPaymentById(id);
            }
        }

在这里插入图片描述
Feign天生自带Ribbon,有负载均衡:
SpringCloud学习笔记_第39张图片

5.2 OpenFeign超时控制

接口加注解的形式(客户端):
SpringCloud学习笔记_第40张图片
服务提供者:提供一个耗时的服务。
SpringCloud学习笔记_第41张图片
SpringCloud学习笔记_第42张图片
默认只等1秒,所以超时以后就不行了,具体的超时时间设置在yaml中设置即可:

ribbon:
    # 指的是建立连接后从服务器读取到可用资源所用的时间, 默认1000,即1s
    ReadTimeout: 5000
    # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
    ConnectTimeout: 5000

5.3 OpenFeign日志

日志分类:

  • NOEE:默认,不显示日志
  • BASIC:仅记录请求方法、URL、响应状态码以及执行时间
  • HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息
  • FULL:除了 HEADERS 中定义的信息外,还有请求和响应的正文以及元数据
  1. 实现在配置类中添加OpenFeign的日志类
        @Configuration
        public class FeignConfig {
            @Bean
            public Logger.Level feignLoggerLevel(){
                return Logger.Level.FULL;//日志级别
            }
        }

  1. yml
        logging:
          level:
            # feign 日志以什么级别监控哪个接口
            com.moke.springcloud.service.PaymentService: debug

SpringCloud学习笔记_第43张图片

六、Hystrix断路器

6.1 概念

背景:服务雪崩

多个微服务之间调用时,假设微服务 A 调用微服务 B 和微服务 C,微服务 B 和微服务 C 又调用其它的微服务,这就是所谓的”扇出“。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务 A 的调用就会占用越来越多的系统资源,进而导致系统”雪崩效应“。

服务降级:比如当某个服务繁忙,不能让客户端的请求一直等待,应该立刻返回给客户端一个备选方案。(兜底的解决方法)
服务熔断:当某个服务出现问题,卡死了,不能让用户一直等待,需要关闭所有对此服务的访问然后调用服务降级。(保险丝,类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示)
服务限流:比如秒杀场景,不能访问用户瞬间都访问服务器,限制一次只可以有多少请求。
接近实时的监控

6.2 提供者

SpringCloud学习笔记_第44张图片
PaymentService:

@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;
    }

}

controller:


@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;
    }
}

主启动类
在这里插入图片描述
yml文件:

server:
  port: 8001
eureka:
  client:
    register-with-eureka: true    #表识不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://localhost:7001/eureka/
spring:
  application:
    name: cloud-provider-hystrix-payment

启动以后:
在这里插入图片描述
完全没问题,但是经过Jmeter的压测以后,发现这个变得很慢了。(原因:用尽了tomcat线程数)
正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生。

6.3 消费者

Hystrix在消费侧和提供侧都能实现,一般用于消费侧。
cloud-consumer-feign-hystrix-order80:

pom:

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

yml:

        server:
          port: 80

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

主启动类:

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

Service接口:

        @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);
        }

Controller:

        @RestController
        @Slf4j
        @RequestMapping("/consumer")
        public class OrderHystrixController {
            @Resource
            private PaymentHystrixService paymentHystrixService;

            @GetMapping("/payment/hystrix/ok/{id}")
            public String paymentInfo_OK(@PathVariable("id") Integer id){
                String result = paymentHystrixService.paymentInfo_OK(id);
                return result;
            }

            @GetMapping("/payment/hystrix/timeout/{id}")
            public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
                String result = paymentHystrixService.paymentInfo_TimeOut(id);
                return result;
            }
        }

结果非常慢!
SpringCloud学习笔记_第45张图片
SpringCloud学习笔记_第46张图片

6.4 服务降级(兜底方法)

6.4.1 提供者fallback

PaymentService.java中添加如下:注意这里的fallback方法的名字指定了兜底方法(无论是系统繁忙还是报错了都走这个方法

        /*
        会超时报错的方法
         */
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")})//设置调用超时时间的峰值,超时则会调用设置的paymentInfo_TimeOutHandler方法
        public String paymentInfo_TimeOut(Integer id){
            int timeNumber = 5;
            //int age = 10/0;
            //其他异常也会触发服务降级
            try{
                TimeUnit.SECONDS.sleep(timeNumber);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id:"+id+"\t"+"!!!";
        }
        public String paymentInfo_TimeOutHandler(Integer id){
            return "线程池:"+Thread.currentThread().getName()+" 系统繁忙,请稍后再试,id:"+id+"\t"+"。。。";
        }

主启动类加注解

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

访问8001的timeout接口,可以看到触发了降级。
在这里插入图片描述

6.4.2 消费者fallback

一般的服务降级都放在客户端即消费者!

  1. 修改yml
        feign:
          hystrix:
            enabled: true
  1. 主启动加上@EnableHystrix
  2. 修改controller
        @GetMapping("/payment/hystrix/timeout/{id}")
        @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
        })
        public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
            String result = paymentHystrixService.paymentInfo_TimeOut(id);
            return result;
        }
        public String paymentInfo_TimeOutHandler(Integer id){
            return "本80系统繁忙,请稍后再试,id:"+id+"\t"+"。。。";
        }

客户端在1.5s内都OK,超过不玩了!

6.4.3 代码膨胀——全局fallback

每个业务逻辑都有一个兜底的,代码膨胀了!

全局的fallback
在Controller中写:

        //全局降级方法
        public String paymentInfo_Global_FallbackMethod(){
            return "Global异常处理信息,请稍后再试,。。。";
        }

再在这个Controller头上加注解:

        @RestController
        @Slf4j
        @RequestMapping("/consumer")
        @DefaultProperties(defaultFallback = "paymentInfo_Global_FallbackMethod")
        public class OrderHystrixController {
            ...
        }

如果这个方法头上没规定fallback就找全局的,自己规定好了就走自己的:

        @HystrixCommand
        public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
            String result = paymentHystrixService.paymentInfo_TimeOut(id);
            return result;
        }

比如这个就找全局的在这里插入图片描述

6.4.4 全局fallback改进版

全局的fallback代码和业务逻辑混合在一起了,尽量分开
SpringCloud学习笔记_第47张图片
根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理
SpringCloud学习笔记_第48张图片
创建他的实现类
在这里插入图片描述
SpringCloud学习笔记_第49张图片
确保配置文件中开启了feign的hystrix支持SpringCloud学习笔记_第50张图片

6.5 服务熔断(恢复链路)

保险丝,比降级更厉害。

熔断机制:
应对雪崩效应的微服务链路保护机制,当扇出链路其中的某个微服务出错不可用了或者响应时间太长了就会进行服务的降级,进而熔断,返回错误的响应信息,当检测到该节点微服务正常以后,恢复链路
SpringCloud学习笔记_第51张图片

6.5.1 提供者service

在“时间窗口期”内失败率大于“失败率”就会跳闸

        //服务熔断
        @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
                @HystrixProperty(name = "circuitBreaker.enable",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();//hutool工具
            return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
        }
        public String paymentCircuitBreaker_fallback(Integer id){
            return "id:"+id+",不能为负数,请稍后再试,。。。";
        }

如果并发超过10个10个并发中失败了6个,就会开启断路器!
时间窗口期10秒之后会尝试请求(半开),如果请求成功就会关闭断路器!

在Controller中调用service即可:

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

SpringCloud学习笔记_第52张图片

6.5.2 总结

在这里插入图片描述
SpringCloud学习笔记_第53张图片
SpringCloud学习笔记_第54张图片
前两个条件为“且”的关系!

断路器打开以后: 自动调用fallback!

主逻辑如何恢复: Hystrix提供自动恢复功能,对主逻辑进行熔断以后,Hystrix会启动一个休眠时间窗,在这个时间内,降级逻辑fallback,过了时间窗口期,进入半开状态,释放一次请求到原来的主逻辑上,如果此次成功了,断路器失效,服务正常,如果此次没成功,继续断路,窗口期 重新计时!

6.5.3 DashBoard

pom

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

yml:

        server:
          port: 9001

主启动:

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

其他想看的pay模块(8001,8002,8003…)添加一个pom依赖,我们之前都配过了

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

启动9001即可,访问: http://localhost:9001/hystrix
SpringCloud学习笔记_第55张图片

此时仅仅是可以访问HystrixDashboard,并不代表已经监控了8001,8002。如果要监控,还需要配置(8001为例):
SpringCloud学习笔记_第56张图片
SpringCloud学习笔记_第57张图片
SpringCloud学习笔记_第58张图片

七、Nacos服务注册

SpringCloud学习笔记_第59张图片

7.1 安装nacos

SpringCloud学习笔记_第60张图片
SpringCloud学习笔记_第61张图片
注意此处必须mysql8.0!其余都不好使
打开网站:http://localhost:8848/nacos
SpringCloud学习笔记_第62张图片

7.2 单机nacos

SpringCloud学习笔记_第63张图片
SpringCloud学习笔记_第64张图片
SpringCloud学习笔记_第65张图片
PaymentController.java:

@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;
    }
}

主函数:

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

yml:

server:
  port: 9001

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

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

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>

7.3 集群nacos

做好的单机版直接端口映射
SpringCloud学习笔记_第66张图片
SpringCloud学习笔记_第67张图片
可以看到已经成为了一个集群。

为什么nacos天生自带负载均衡?
SpringCloud学习笔记_第68张图片

7.4 注册中心对比

nacos:AP或CP
A:可用性:所有的请求都会受到响应
C:强一致性:所有节点在同一时间看到的数据是一致的
P:分布容错性

AP:为了服务的可能性减弱了一致性,因此AP模式仅支持注册临时实例
CP:保证一致性,支持注册持久化实例
SpringCloud学习笔记_第69张图片
SpringCloud学习笔记_第70张图片

八、Nacos配置中心

springboot中配置中心的加载优先级,bootstrap>application。先从配置中心拉取再读取本地的。

8.1 本地端配置

Controller:
SpringCloud学习笔记_第71张图片
两个yml:bootstrap.yml和application.yml
SpringCloud学习笔记_第72张图片
SpringCloud学习笔记_第73张图片

8.2 Nacos配置中心配置

SpringCloud学习笔记_第74张图片
一图总结:
SpringCloud学习笔记_第75张图片
SpringCloud学习笔记_第76张图片
之后:
SpringCloud学习笔记_第77张图片
注意这里会报错,因为nacos网站上后缀名仅支持yaml,不可写成yml。
在这里插入图片描述
注意Controller上的注解**@RefreshScope**,加上这个才支持动态刷新!

8.3 Nacos分类配置

SpringCloud学习笔记_第78张图片
SpringCloud学习笔记_第79张图片
在这里插入图片描述
SpringCloud学习笔记_第80张图片

8.4 Nacos集群和持久化配置

8.4.1 官方架构图

SpringCloud学习笔记_第81张图片
SpringCloud学习笔记_第82张图片
Nacos自带嵌入式数据库derby,但是集群中每个Nacos都自带一个,集群环境就必须将其化为一个mysql.
SpringCloud学习笔记_第83张图片
通过以上的步骤就放弃了derby,进入了Mysql。

九、Sentinel 限流

中文文档 :https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
SpringCloud学习笔记_第84张图片
下载地址 :https://github.com/alibaba/Sentinel/releases
确保有 Java 8 的环境,且 8080 端口没有被占用
进入存放 Sentinel 的目录,并运行命令

java -jar sentinel-dashboard-1.8.0.jar 

访问 :http://localhost:8080
SpringCloud学习笔记_第85张图片
登录账号密码均为 :sentinel

9.1 测试

启动8848Nacos和8080Sentinel,引入这三个包
SpringCloud学习笔记_第86张图片
sentinel懒加载,必须执行一次访问才能出现。
SpringCloud学习笔记_第87张图片
结论:8080正在监控8848

9.2 流控规则

SpringCloud学习笔记_第88张图片
SpringCloud学习笔记_第89张图片

9.3 流控模式——直接(默认)

SpringCloud学习笔记_第90张图片
SpringCloud学习笔记_第91张图片
先是一秒点一次,可以正常访问。SpringCloud学习笔记_第92张图片
连续点几次,出现提示语句

在这里插入图片描述

9.4 流控模式——关联

当关联的资源达到阈值时,就限流自己
实际场景:对于同级别的服务,比如 下单、支付 两个服务,当 支付 流量过高,撑不住了,就限制一下 下单 服务,把资源留给 支付 服务,先撑过去。
SpringCloud学习笔记_第93张图片
在/testB非常忙的时候,无法
SpringCloud学习笔记_第94张图片

9.5 流控模式——链路

只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)。
SpringCloud学习笔记_第95张图片
只要从这个入口资源进来的流量超过了阈值就对A限流。

9.6 流控效果——快速失败(默认)

SpringCloud学习笔记_第96张图片

9.7 流控效果——Warm up

在这里插入图片描述
SpringCloud学习笔记_第97张图片
默认 coldFactor 为 3,即请求 QPS 从 threshold/3 开始,经预热时长逐渐升至设定的QPS阈值。

一直发出 /testA 请求
发现刚开始提示 Blocked by Sentinel(flow limiting) 的频率比较高
越往后频率越低
最后几乎不会出现
这就是阈值再慢慢预热的过程

9.8 流控效果——排队等待

让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
每个请求,愿意等就等,不愿意等就超时重试
SpringCloud学习笔记_第98张图片

SpringCloud学习笔记_第99张图片

9.9 热点Key限流

SpringCloud学习笔记_第100张图片

备选方案

SpringCloud学习笔记_第101张图片修改 Controller ,加入备选方案

@GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
    public String hotKeyTest(@RequestParam(value = "p1", required = false)String p1,
                             @RequestParam(value = "p2", required = false)String p2){
        return "===== (*^_^*) 成功了 (*^_^*) =====";
    }
    public String deal_testHotKey(String p1, String p2, BlockException exception){
        return "===== (ㄒoㄒ) 失败了 (ㄒoㄒ) =====";
    }

SpringCloud学习笔记_第102张图片

热点key设置

SpringCloud学习笔记_第103张图片
SpringCloud学习笔记_第104张图片
SpringCloud学习笔记_第105张图片

例外参数

某个参数和其他参数不一样,传入的阈值不同。
SpringCloud学习笔记_第106张图片

十、Sentinel熔断降级

在这里插入图片描述SpringCloud学习笔记_第107张图片

Sentinel的断路器是没有半开状态的

10.1 Sentinel熔断策略——慢调用比例RT

SpringCloud学习笔记_第108张图片
SpringCloud学习笔记_第109张图片
SpringCloud学习笔记_第110张图片
SpringCloud学习笔记_第111张图片

10.2 Sentinel熔断策略——异常比例

SpringCloud学习笔记_第112张图片

SpringCloud学习笔记_第113张图片
SpringCloud学习笔记_第114张图片
SpringCloud学习笔记_第115张图片

10.3 Sentinel熔断策略——异常数

SpringCloud学习笔记_第116张图片
SpringCloud学习笔记_第117张图片
SpringCloud学习笔记_第118张图片
SpringCloud学习笔记_第119张图片
SpringCloud学习笔记_第120张图片

10.4 规则持久化

  • 一旦服务重启就会规则消失
  • 所以将限流配置规则持久化仅Nacos保存
  • 只要 Nacos 里面的配置不删除,针对 8401 上 Sentinel 上的流控规则持续有效。

pom

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

SpringCloud学习笔记_第121张图片
SpringCloud学习笔记_第122张图片

SpringCloud学习笔记_第123张图片

十一、Seata分布式处理

11.1 面对的问题——分布式业务问题

在这里插入图片描述
订单、库存、余额三个数据库
SpringCloud学习笔记_第124张图片

11.2 Seata分布式解决方案

即@GlobalTransactional注解,放在业务方法上
SpringCloud学习笔记_第125张图片
SpringCloud学习笔记_第126张图片

11.3 Seata理论

TC:全局管理者——seata服务器
TM:事物的发起方——头上戴了这个注解的业务类
RM:事务的参与方——数据库
SpringCloud学习笔记_第127张图片
执行流程:SpringCloud学习笔记_第128张图片

11.4 Seata的AT模式

11.4.1 一阶段加载

SpringCloud学习笔记_第129张图片
SpringCloud学习笔记_第130张图片

11.4.2 二阶段提交(正常情况)

因为“业务SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可.
SpringCloud学习笔记_第131张图片

11.4.3 二阶段回滚(异常情况)

SpringCloud学习笔记_第132张图片
脏写和脏读类似:看有没有人动过!
在这里插入图片描述
SpringCloud学习笔记_第133张图片

你可能感兴趣的:(springcloud,ruby,on,rails)