SpringCloud入门

1. 微服务理论

https://www.martinfowler.com/articles/microservices.html 微服务microservices
http://blog.cuicc.com/blog/2015/07/22/microservices/
In short(简言之), the microservice architectural style 【架构风格】[1] is an approach to developing a single application as a suite of small services【独立应用变成一套小服务】, each running in its own process and communicating with lightweight(轻量级沟通) mechanisms(每一个都运行在自己的进程内(容器)), often an HTTP resource API(用HTTP,将功能写成能接受请求). These services are built around business capabilities (独立业务能力)and independently deployable by fully automated deployment machinery(应该自动化独立部署). There is a bare minimum of centralized management of these services(应该有一个能管理这些服务的中心), which may be written in different programming languages (独立开发语言)and use different data storage technologies(独立的数据存储)

2. 分布式概念

2.1. 什么是分布式

《分布式系统原理与范型》定义:“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”。
分布式系统(distributed system)是建立在网络之上的软件系统。

2.2. 分布式与集群的关系

集群指的是将几台服务器集中在一起,实现同一业务。
分布式中的每一个节点,都可以做集群。 而集群并不一定就是分布式的。

2.3. 软件架构演变

SpringCloud入门_第1张图片
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
在这里插入图片描述
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
SpringCloud入门_第2张图片
分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

SpringCloud入门_第3张图片
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
SpringCloud入门_第4张图片

2.4. RPC是什么

RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。
它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。
即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。

2.4.1. 解决分布式系统的各个服务之间互相交互问题

2.4.2. RPC思想原理

SpringCloud入门_第5张图片
SpringCloud入门_第6张图片

2.4.3. 服务之间的交互可以用两种方式

 RPC
 Netty(Socket)+自定义序列化
 RestAPI (严格来说,SpringCloud是使用Rest方式进行服务之间交互的,不属于RPC)
 HTTP+JSON

2.5. 分布式思想与基本概念

2.5.1. 高并发

  1. 通过设计保证系统可以并行处理很多请求。应对大量流量与请求
     Tomcat最多支持并发多少用户?
    Tomcat 默认配置的最大请求数是 150,也就是说同时支持 150 个并发,当然了,也可以将其改大。
    当某个应用拥有 250 个以上并发的时候,应考虑应用服务器的集群。
    具体能承载多少并发,需要看硬件的配置,CPU 越多性能越高,分配给 JVM 的内存越多性能也就越高,但也会加重 GC 的负担。
     操作系统对于进程中的线程数有一定的限制:
    Windows 每个进程中的线程数不允许超过 2000
    Linux 每个进程中的线程数不允许超过 1000
    另外,在 Java 中每开启一个线程需要耗用 1MB 的 JVM 内存空间用于作为线程栈之用。
    Tomcat 默认的 HTTP 实现是采用阻塞式的 Socket 通信,每个请求都需要创建一个线程处理。这种模式下的并发量受到线程数的限制,但对于 Tomcat 来说几乎没有 BUG 存在了。
    Tomcat 还可以配置 NIO 方式的 Socket 通信,在性能上高于阻塞式的,每个请求也不需要创建一个线程进行处理,并发能力比前者高。但没有阻塞式的成熟。
    这个并发能力还与应用的逻辑密切相关,如果逻辑很复杂需要大量的计算,那并发能力势必会下降。如果每个请求都含有很多的数据库操作,那么对于数据库的性能也是非常高的。
    对于单台数据库服务器来说,允许客户端的连接数量是有限制的。
    并发能力问题涉及整个系统架构和业务逻辑。
    系统环境不同,Tomcat版本不同、JDK版本不同、以及修改的设定参数不同。并发量的差异还是满大的。
     maxThreads=“1000” 最大并发数 ,默认值为200
     minSpareThreads=“100”//初始化时创建的线程数,默认值为10
     acceptCount=“700”// 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,默认值为100
    https://tomcat.apache.org/tomcat-8.0-doc/config/http.html
  2. 高并发衡量指标
     响应时间(RT)
     请求做出响应的时间,即一个http请求返回所用的时间
     吞吐量
     系统在单位时间内处理请求的数量
     QPS(Query/Request Per Second)、 TPS(Transaction Per Second)
     每秒查询(请求)数、每秒事务数
     专业的测试工具:Load Runner
     Apache ab
     Apache JMeter
     并发用户数
     承载的正常使用系统功能的用户的数量

2.5.2. 高可用

服务集群部署
数据库主从+双机热备
 主-备方式(Active-Standby方式)
主-备方式即指的是一台服务器处于某种业务的激活状态(即Active状态),另一台服务器处于该业务的备用状态(即Standby状态)。
 双主机方式(Active-Active方式)
双主机方式即指两种不同业务分别在两台服务器上互为主备状态(即Active-Standby和Standby-Active状态)

2.5.3. 注册中心

保存某个服务所在地址等信息,方便调用者实时获取其他服务信息
 服务注册
 服务提供者
 服务发现
 服务消费者

2.5.4. 负载均衡

 动态将请求派发给比较闲的服务器
策略:
 轮询(Round Robin)
 加权轮询(Weighted Round Robin)
 随机Random
 哈希Hash
 最小连接数LC
 最短响应时间LRT

2.5.5. 服务雪崩

服务之间复杂调用,一个服务不可用,导致整个系统受影响不可用

SpringCloud入门_第7张图片

2.5.6. 熔断

某个服务频繁超时,直接将其短路,快速返回mock(模拟/虚拟)值

SpringCloud入门_第8张图片

2.5.7. 限流

限制某个服务每秒的调用本服务的频率

SpringCloud入门_第9张图片

2.5.8. API网关

API网关要做很多工作,它作为一个系统的后端总入口,承载着所有服务的组合路由转换等工作,除此之外,我们一般也会把安全,限流,缓存,日志,监控,重试,熔断等放到 API 网关来做

2.5.9. 服务跟踪

追踪服务的调用链,记录整个系统执行请求过程。如:请求响应时间,判断链中的哪些服务属于慢服务(可能存在问题,需要改善)。

2.5.10. 弹性云

Elastic Compute Service(ECS)弹性计算服务
动态扩容,压榨服务器闲时能力
例如:双11,618,高峰时多配置些服务器,平时减少多余的服务器配置(用于其他服务应用),避免资源浪费

3. SpringCloud背景

3.1. 背景介绍

3.1.1. 微服务架构

SpringCloud入门_第10张图片
物联网( IoT ,Internet of things )即“万物相连的互联网”,是互联网基础上的延伸和扩展的网络,将各种信息传感设备与互联网结合起来而形成的一个巨大网络,实现在任何时间、任何地点,人、机、物的互联互通。
Breaker dashboard 断路器仪表板
Distributed Tracing分布式跟踪 (分布式处理程序链跟踪用于监视网络等待时间,并可视化通过微服务的请求流)

3.1.2. 微服务框架之SpringBoot

https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/htmlsingle/

3.1.3. 分布式系统微服务架构之SpringCloud

https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/
英文困难的同学,也不耽误学习的
https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md

3.1.4. 组件概述

SpringCloud入门_第11张图片

3.2. 关于SpringBoot和SpringCloud版本

3.2.1. SpringCloud版本选择

SpringBoot2.X版和SpringCloud H版
SpringCloud Alibaba 2.1

3.2.2. Springboot版本选择

git源码地址:
https://github.com/spring-projects/spring-boot/releases/
SpringBoot2.0新特性:
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes
通过上面官网发现,Boot官方强烈建议你升级到2.X以上版本
SpringCloud入门_第12张图片

3.2.3. 官网看Boot版本

springboot(截至2020.4.4)

SpringCloud入门_第13张图片

3.2.4. SpringCloud版本选择

 git源码地址: https://github.com/spring-projects/spring-cloud/wiki
 官网: https://spring.io/projects/spring-cloud
官网看Cloud版本
 Cloud命名规则
Spring Cloud采用了英国伦敦地铁站的名称来命名,并由地铁站名称字母A-Z依次类推的形式来发布迭代版本。
Spring Cloud 是一个由许多子项目组成的综合项目,各子项目有不同的发布节奏。为了管理SpringCloud与各子项目的版本依赖关系,发布了一个清单,其中包括了某个SpringCloud版本对应的子项目版本。为了避免SpringCloud版本号与子项目版本号混淆,SpringCloud版本采用了名称而非版本号的命名,这些版本的名字采用了伦敦地铁站的名字,根据字母表的顺序来应对版本时间顺序。例如Angel是第一个版本,Brixton是第二个版本。当SpringCloud的发布内容积累到临界点或者一个重大BUG被解决后,会发布一个"service releases"版本,简称SRX版本,比如Greenwich.SR2就是SpringCloud发布的Greenwich版本的第二个SRX版本。
 SpringCloud(截至2020.4.4)

SpringCloud入门_第14张图片

3.2.5. SpringCloud和Springboot之间的依赖关系

https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/

SpringCloud入门_第15张图片
最新版本对应关系:截止2020年4月初
https://cloud.spring.io/spring-cloud-static/Hoxton.SR3/reference/html/spring-cloud.html
SpringCloud入门_第16张图片
更详细的版本对应查看方法: https://start.spring.io/actuator/info

SpringCloud入门_第17张图片

3.2.6. SpringCloud(授课选择版本)

o cloud
• Hoxton.SR1
o boot
• 2.2.2.RELEASE
o cloud Alibaba
• 2.1.0.RELEASE
o java
• JAVA8
o maven
• 3.5及以上
o mysql
• 5.7及以上

3.3. 微服务架构编码构建-IDEA新建project工作空间

3.3.1. 微服务cloud整体聚合父工程Project

1. New Project

SpringCloud入门_第18张图片

2. 聚合总工程名字

SpringCloud入门_第19张图片

3. Maven选版本

SpringCloud入门_第20张图片

4. 工程名字

SpringCloud入门_第21张图片

5. 字符编码

在这里插入图片描述

6. 注解生效激活

SpringCloud入门_第22张图片

7. java编译版本

SpringCloud入门_第23张图片

8. File Type过滤【可选】

SpringCloud入门_第24张图片

3.3.2. 父工程POM


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

  <modelVersion>4.0.0modelVersion>

  <groupId>com.atguigu.springcloudgroupId>
  <artifactId>cloud2020artifactId>
  <version>1.0-SNAPSHOTversion>
  <packaging>pompackaging>

  
  <properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <maven.compiler.source>1.8maven.compiler.source>
    <maven.compiler.target>1.8maven.compiler.target>
    <junit.version>4.12junit.version>
    <log4j.version>1.2.17log4j.version>
    <lombok.version>1.16.18lombok.version>
    <mysql.version>5.1.47mysql.version>
    <druid.version>1.1.16druid.version>
    <mybatis.spring.boot.version>1.3.0mybatis.spring.boot.version>
  properties>

  
  <dependencyManagement>
    <dependencies>
      
      <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-dependenciesartifactId>
        <version>2.2.2.RELEASEversion>
        <type>pomtype>
        <scope>importscope>
      dependency>
      
      <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-dependenciesartifactId>
        <version>Hoxton.SR1version>
        <type>pomtype>
        <scope>importscope>
      dependency>
      
      <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-alibaba-dependenciesartifactId>
        <version>2.1.0.RELEASEversion>
        <type>pomtype>
        <scope>importscope>
      dependency>

      <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>${mysql.version}version>
      dependency>
      <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druidartifactId>
        <version>${druid.version}version>
      dependency>
      <dependency>
        <groupId>org.mybatis.spring.bootgroupId>
        <artifactId>mybatis-spring-boot-starterartifactId>
        <version>${mybatis.spring.boot.version}version>
      dependency>
      <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>${junit.version}version>
      dependency>
      <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>${log4j.version}version>
      dependency>
      <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>${lombok.version}version>
        <optional>trueoptional>
      dependency>
    dependencies>
  dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-maven-pluginartifactId>
        <configuration>
          <fork>truefork>
          <addResources>trueaddResources>
        configuration>
      plugin>
    plugins>
  build>

project>

3.3.3. Maven工程落地细节复习

Maven中的dependencyManagement和dependencies区别
maven中跳过单元测试【可选】
SpringCloud入门_第25张图片

3.3.4. 父工程创建完成执行mvn:install

3.4. 微服务架构编码构建-Rest微服务-【服务提供者】

3.4.1. 建cloud-provider-payment8001

SpringCloud入门_第26张图片
SpringCloud入门_第27张图片
创建完成后请回到父工程查看pom文件变化,增加了聚合模块

  <modules>
    <module>cloud-provider-payment8001</module>
  </modules>

3.4.2. 改POM文件


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

    <dependencies>
        <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.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.10version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
           <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

    dependencies>
project>

3.4.3. 写YML

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

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

3.4.4. 主启动

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.4.5. 业务类

1. 建表SQL

CREATE DATABASE  IF NOT EXISTS cloud2020 DEFAULT CHARACTER SET utf8 ;

USE cloud2020 ;

DROP TABLE IF EXISTS payment ;

CREATE TABLE payment (
  id BIGINT (20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  SERIAL VARCHAR (300) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE = INNODB AUTO_INCREMENT = 33 DEFAULT CHARSET = utf8 ;

INSERT INTO payment (id, SERIAL) VALUES(31, '尚硅谷001'),(32, 'atguigu002') ;

2. Entitles

1) 主实体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;
}

2) Json封装体CommonResult

package com.atguigu.springcloud.entities;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult <T> implements Serializable{

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

    public CommonResult(Integer code,String message){
        this(code,message,null);//如果这行报错,请安装lombok插件
    }
}

3) 安装lombok插件

SpringCloud入门_第28张图片
https://www.projectlombok.org/
@Data:提供getter/setter
@NoArgsConstructor, 无参构造器 @RequiredArgsConstructor @AllArgsConstructor 全参数构造器
@EqualsAndHashCode:提供equals和hashCode方法
@Getter/@Setter
@Slf4j 内置log对象,直接调用日志方法输出日志

3. Dao

1) 接口PaymentDao

package com.atguigu.springcloud.dao;

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

@Component       //代替@Repository声明bean
@Mapper               //mybatis提供的,等价:@MapperScan("com.atguigu.springcloud.dao")
//@Repository     //spring提供的。在此,只是为了声明bean对象
public interface PaymentDao {
    public int create(Payment payment);    
    public Payment getPaymentById(@Param("id") Long id);
}

2) mybatis的映射文件

src\main\resources\mapper\PaymentMapper.xml


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"  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>
        <result column="serial" property="serial" jdbcType="VARCHAR">result>
    resultMap>

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

mapper>

4. Service

1) 接口PaymentService

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(Long id);  //读取
}

2) 实现类PaymentServiceImpl

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.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class PaymentServiceImpl implements PaymentService {

    @Resource
    //@Autowired
    private PaymentDao paymentDao;

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

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

5. 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<Payment> 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);
        if (payment!=null){  //说明有数据,能查询成功
            return new CommonResult(200,"查询成功",payment);
        }else {
            return new CommonResult(444,"没有对应记录,查询ID:"+id,null);
        }
    }
}

3.4.6. 测试

1. postman测试get请求

SpringCloud入门_第29张图片

2. postman测试post请求

SpringCloud入门_第30张图片

3. 快速运行设置

SpringCloud入门_第31张图片
SpringCloud入门_第32张图片

3.4.7. 开发步骤-小总结

  1. 建module
  2. 改POM
  3. 写YML
  4. 主启动
  5. 业务类

3.4.8. 热部署Devtools

1. Adding devtools to your project

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

2. Adding plugin to your pom.xml

下一段配置黏贴到父工程当中的pom里
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-maven-pluginartifactId>
      <configuration>
        <fork>truefork>
        <addResources>trueaddResources>
      configuration>
    plugin>
  plugins>
build>

3. Enabling automatic build

SpringCloud入门_第33张图片

4. Update the value of

Ctrl+Shift+Alt+/选择Registry…

SpringCloud入门_第34张图片

compiler.automake.allow.when.app.running -> 自动编译
compile.document.save.trigger.delay -> 自动更新文件;它主要是针对静态文件如JS CSS的更新,将延迟时间减少后,直接按F5刷新页面就能看到效果!

SpringCloud入门_第35张图片

5. 重启IDEA

3.5. 微服务架构编码构建-Rest微服务-【服务消费者】

3.5.1. 建cloud-consumer-order80

3.5.2. 改POM


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

    <dependencies>
        <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>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
project>

3.5.3. 写YML

server:
  port: 80
spring:
  application:
    name: cloud-consumer-order80

3.5.4. 主启动

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

3.5.5. 业务类

1. 创建entities

(将cloud-provider-payment8001工程下的entities包下的两个实体类复制过来)

2. RestTemplate

 是什么
RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问Restful服务模板类,是Spring 提供的用于访问Rest服务的客户端模板工具集
 官网及使用
官网地址: https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html
使用RestTemplate访问Restful接口非常的简单粗暴无脑。(url,requestMap,ResponseBean.class)这三个参数分别代表REST请求地址、请求参数、Http响应转换被转换成的对象类型。

3. config配置类

ApplicationContextConfig

package com.atguigu.springcloud.config;

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

//@Configuration
@SpringBootConfiguration
public class ApplicationContextConfig {

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

}

4. 创建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;

    @PostMapping("/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);
    }
}

3.5.6. 测试

  1. 先启动cloud-provider-payment8001
  2. 再启动cloud-consumer-order80
  3. http://localhost/consumer/payment/get/32
  4. 不要忘记@RequestBody注解
  5. 服务提供者接口方法需要增加@RequestBody注解(踩雷or破雷);否则,接收不到数据。

3.6. 工程重构

3.6.1. 观察问题

SpringCloud入门_第36张图片

3.6.2. 新建:cloud-api-commons

3.6.3. POM


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

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

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
           <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.1.0version>
        dependency>
    dependencies>

project>

3.6.4. entities

Payment实体
CommonResult通用封装类

3.6.5. maven命令clean install

3.6.6. 订单80和支付8001分别改造

删除各自的原先有过的entities
各自黏贴POM内容,依赖于cloud-api-commons公共项目

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

3.7. 目前工程样图

SpringCloud入门_第37张图片

4. Eureka服务注册与发现

4.1. Eureka基础知识

4.1.1. 什么是服务治理

SpringCloud封装了Netflix公司开发的Eureka模块来实现服务治理。
在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂、所以需要进行服务治理,管理服务与服务之间依赖关联,以实现服务调用,负载均衡、容错等,实现服务发现与注册。

4.1.2. 什么是服务注册

Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。
而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息,比如:服务通讯地址等以别名方式注册到注册中心上。
另一方(消费者服务),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后,再实现本地RPC远程调用。
RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。
在任何RPC远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))。
SpringCloud入门_第38张图片

4.1.3. Eureka两组件

 Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
 Eureka Client通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会在Eureka Server发送心跳(默认周期30秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移出(默认90秒)

4.2. 单机Eureka构建步骤

4.2.1. IDEA生成eurekaServer端服务注册中心

1. 建Module:cloud-eureka-server7001

2. 改POM


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

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

        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <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>

 1.X和2.X的对比说明

1.X版本
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>

2.X版本
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>

3. 写YML

server:
  port: 7001

eureka:
  instance:
    hostname: localhost

  client:
    register-with-eureka: false
    fetchRegistry: false
    service-url:
      defaultZone: http://localhost:7001/eureka


4. 主启动

@EnableEurekaServer

5. 测试

http://localhost:7001/

4.2.2. 服务提供者

EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider

  1. 建Module:cloud-provider-payment8001
  2. 改POM
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>

  1. 写YML
eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka


  1. 主启动
    @EnableEurekaClient

  2. 测试
    先启动EurekaServer
    http://localhost:7001/

4.2.3. 服务消费者

EurekaClient端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer

  1. 建Module:cloud-consumer-order80
  2. POM
  3. 写YML
eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka


  1. 主启动
    @EnableEurekaClient

  2. 测试

  1. 先要启动EurekaServer,7001服务
  2. 再要启动服务提供者8001服务和服务消费者80服务
  3. eureka服务器

在这里插入图片描述
4) 测试查询:http://localhost/consumer/payment/get/31
5) 测试添加:postman测试添加
6) 测试8001服务和80服务效果一样
SpringCloud入门_第39张图片

5. Ribbon负载均衡服务调用

5.1. 概述

5.1.1. 是什么

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。
Ribbon客户端组件提供一系列完善的配置项,如:连接超时,重试等。
简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。

5.1.2. 官网资料

https://github.com/Netflix/ribbon/wiki/Getting-Started
Ribbon目前也进入维护模式
SpringCloud入门_第40张图片
 未来替换方案
 Spring Cloud LoadBalancer

5.1.3. 能干嘛

1. LB(负载均衡)

  1. 简单的说就是将用户的请求平均分配到多个服务器上,从而达到系统的HA(高可用)。
  2. 常见的负载均衡有软件Nginx,LVS,硬件F5等。
  3. Ribbon的本地负载均衡客户端 VS Nginx服务端负载均衡区别:
    • Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后,由nginx实现转发请求。即负载均衡是由服务器端完成的。
    • Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用。
  4. 集中式LB
    • 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如Nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
  5. 进程内LB
    • 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
    • Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
    2. 一句话
    Ribbon=负载均衡+RestTemplate调用

5.2. Ribbon负载均衡演示

5.2.1. 架构说明

SpringCloud入门_第41张图片Ribbon在工作时分成两步:
第一步,先选择EurekaServer,它优先选择在同一个区域内负载较少的server。
第二步,再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略。比如:轮询、随机和根据响应时间加权。
总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。

5.2.2. POM

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

注意:这个不需要手动引用,Eureka客户端自带Ribbon

SpringCloud入门_第42张图片

5.3. Ribbon核心组件Irule

SpringCloud入门_第43张图片
SpringCloud入门_第44张图片

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

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

5.3.2. 如何替换

 修改cloud-consumer-order80
 注意配置细节
官方文档明确给出警告:
https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#customizing-the-ribbon-client
SpringCloud入门_第45张图片
这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化订制的目的了。

5.3.3. 新建package(注意:包的位置)

com.atguigu.myrule

5.3.4. 上面包下新建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();//定义为随机
    }
}

5.3.5. 主启动类添加@RibbonClient

package com.atguigu.springcloud;

import com.atguigu.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@EnableEurekaClient
@SpringBootApplication
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
    }
}

5.3.6. 测试

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

5.4. Ribbon负载均衡算法

5.4.1. 原理

SpringCloud入门_第46张图片

6. OpenFeign服务接口调用

6.1. 概述

6.1.1. OpenFeign是什么

 Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可
 SpringCloud对Feign进行了封装,使其支持了SpringMVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign
https://github.com/spring-cloud/spring-cloud-openfeign

6.1.2. 能干嘛

 Feign能干什么?
Feign旨在使用编写Java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。
但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务端额调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。
在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是DAO接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
 Feign集成了 Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
 Feign和OpenFeign两者区别
SpringCloud入门_第47张图片

6.2. OpenFeign使用步骤

SpringCloud入门_第48张图片

6.2.1. 接口+注解

微服务调用接口+@FeignClient

6.2.2. 新建Module:cloud-consumer-feign-order80

6.2.3. POM

注意:openFeign也是自带bibbon


<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-consumer-feign-order80artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <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>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
project> 

6.2.4. YML

server:
  port: 80
spring:
  application:
    name: cloud-consumer-feign-order80
eureka:
  client:
register-with-eureka: true
fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka


6.2.5. 主启动类

package com.atguigu.springcloud;

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

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

6.2.6. 业务类

1. 业务逻辑接口+@FeignClient配置调用provider服务

2. 新建PaymentFeignService接口并新增注解@FeignClient

package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import feign.Param;
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 getPaymentById(@PathVariable("id") Long id);
}

3. 控制层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 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
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);
    }
}

6.2.7. 测试

  1. 先启动Eureka7001
  2. 再启动2个微服务8001/8002
  3. 启动OpenFeign微服务:cloud-consumer-feign-order80
  4. http://localhost/consumer/payment/get/31
  5. Feign自带负载均衡配置项

6.2.8. 小总结

SpringCloud入门_第49张图片

6.3. OpenFeign超时控制

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

  1. 服务提供方8001故意写暂停程序
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout(){
    try { TimeUnit.SECONDS.sleep(3); }catch (Exception e) {e.printStackTrace();} //单位秒
    return serverPort;
}

  1. 服务消费方80添加超时方法PaymentFeignService
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();

  1. 服务消费方80添加超时方法OrderFeignController
@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentFeignTimeout(){
   return paymentFeignService.paymentFeignTimeout();
}

  1. 测试
    http://localhost/consumer/payment/feign/timeout
    错误页面,OpenFeign默认等待一秒钟,超过后报错
    SpringCloud入门_第50张图片

6.3.2. 是什么

默认Feign客户端只等待一秒钟,但是,服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接报错。
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制,也即Ribbon的超时时间,因为Feign集成了Ribbon进行负载均衡。

6.3.3. YML中需要开启OpenFeign客户端超时控制

Feign设置超时时间
使用Feign调用接口分两层,ribbon的调用和hystrix的调用,所以ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间
#设置Feign客户端超时时间(openfeign默认支持ribbon)
ribbon:
  ReadTimeout:  3000
  ConnectTimeout: 3000
  MaxAutoRetries: 1 #同一台实例最大重试次数,不包括首次调用
  MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数,不包括首次调用
  OkToRetryOnAllOperations: false  #是否所有操作都重试
#hystrix的超时时间
hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 9000

一般情况下 都是 ribbon 的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制)
因为ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制,源码如下
SpringCloud入门_第51张图片

要开启Feign的重试机制如下:(Feign默认重试五次 源码中有)

@Bean
Retryer feignRetryer() {
        return  new Retryer.Default();
}

根据上面的参数计算重试的次数:MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) 即重试3次 则一共产生4次调用
如果在重试期间,时间超过了hystrix的超时时间,便会立即执行熔断,fallback。所以要根据上面配置的参数计算hystrix的超时时间,使得在重试期间不能达到hystrix的超时时间,不然重试机制就会没有意义
hystrix超时时间的计算: (1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout 即按照以上的配置 hystrix的超时时间应该配置为 (1+1+1)*3=9秒
当ribbon超时后且hystrix没有超时,便会采取重试机制。当OkToRetryOnAllOperations设置为false时,只会对get请求进行重试。如果设置为true,便会对所有的请求进行重试,如果是put或post等写操作,如果服务器接口没做幂等性,会产生不好的结果,所以OkToRetryOnAllOperations慎用。
如果不配置ribbon的重试次数,默认会重试一次
注意:
默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试
非GET方式请求,只有连接异常时,才会进行重试

6.4. OpenFeign日志打印功能

6.4.1. 日志打印功能

6.4.2. 是什么

Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。说白了就是对Feign接口的调用情况进行监控和输出。

6.4.3. 日志级别

NONE:默认的,不显示任何日志
BASIC:仅记录请求方法、RUL、响应状态码及执行时间
HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据

6.4.4. 配置日志bean

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
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

6.4.5. YML文件里需要开启日志的Feign客户端

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

6.4.6. 后台日志查看

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

SpringCloud入门_第52张图片

7. Hystrix断路器

7.1. 概述

7.1.1. 分布式系统面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系,每一个依赖关系在某些时候将不可避免的失败。
SpringCloud入门_第53张图片
服务雪崩
多个微服务之间调用的时候,假如微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的"扇出"。
如果扇出的链路上某个微服务的调用响应的时间过长或者不可用,对微服A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的"雪崩效应"。
对于高流量的应用来说,单一的后端依赖可能会导致所有的服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。

7.1.2. 是什么

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,
Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(Fallback),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

7.1.3. 能干嘛

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

7.1.4. 官网资料

https://github.com/Netflix/Hystrix/wiki/How-To-Use

7.1.5. Hystrix官宣,停更进维

https://github.com/Netflix/Hystrix

在这里插入图片描述

7.2. Hystrix重要概念

7.2.1. 服务降级Fallback

 服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示
 哪些情况会触发降级
 程序运行异常
 超时自动降级
 服务熔断触发服务降级
 线程池/信号量打满也会导致服务降级
 人工降级

7.2.2. 服务熔断Breaker

 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
 就是保险丝
 服务的降级->进而熔断->恢复调用链路(降级累计到一定程度就熔断)

7.2.3. 服务限流Flowlimit

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

7.3. hystrix案例

7.3.1. 构建

  1. 新建Module:cloud-provider-hystrix-payment8001
  2. POM

<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-provider-hystrix-payment8001artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <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>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
project>

  1. YML
server:
  port: 8001

spring:
  application:
    name: cloud-hystrix-payment-service

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka/


  1. 主启动
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);
    }
}

  1. 业务类
    Service/ServiceImpl
package com.atguigu.springcloud.service;

public interface PaymentService {
    public String paymentInfo_OK(Integer id);
    public String payment_Timeout(Integer id);
}
package com.atguigu.springcloud.service.impl;

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

@Service
public class PaymentServiceImpl implements PaymentService {

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

    //失败
    public String payment_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

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

  1. 正常测试
     启动eureka7001
     启动cloud-provider-hystrix-payment8001
     访问
    访问: http://localhost:8001/payment/hystrix/ok/31
    每次调用耗费3秒钟: http://localhost:8001/payment/hystrix/timeout/31
     上述module均OK
    以上述为根基平台,从正确->错误->降级熔断->恢复

7.3.2. 高并发测试

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

2. Jmeter压测测试

下载地址:https://archive.apache.org/dist/jmeter/binaries/
开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务
SpringCloud入门_第54张图片
SpringCloud入门_第55张图片
 压测的过程中再来访问一下微服务
http://localhost:8001/payment/hystrix/ok/31
http://localhost:8001/payment/hystrix/timeout/31
 演示结果
 两个都在自己转圈圈
 为什么会被卡死
tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。

3. Jmeter压测结论

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

4. 看热闹不嫌弃事大,80新建加入:cloud-consumer-feign-hystrix-order80

1) 新建:cloud-consumer-feign-hystrix-order80

2) POM


<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-consumer-feign-hystrix-order80artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <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>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
project>

3) YML

server:
  port: 80

spring:
  application:
    name: cloud-provider-hystrix-payment-service

eureka:
  client:
    register-with-eureka: true   
    fetch-registry: true   
    service-url:
      defaultZone: http://localhost: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
@EnableEurekaClient
@EnableFeignClients
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

5) 业务类

PaymentHystrixService

package com.atguigu.springcloud.service;

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

@FeignClient("CLOUD-HYSTRIX-PAYMENT-SERVICE")
public interface PaymentHystrixService {
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);

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

OrderHystrixController

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentHystrixService;
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 OrderHystrixController {
    @Resource
    private PaymentHystrixService paymentHystrixService;

    @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.payment_Timeout(id);
        log.info("*******result:"+result);
        return result;
    }

}

6) 正常测试

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

7) 高并发测试

 2W个线程压8001
 消费端80微服务再去访问正常的OK微服务8001地址
 http://localhost/consumer/payment/hystrix/timeout/32
 消费者80,呜呜呜
 要么转圈圈等待
 要么消费端报超时错误
SpringCloud入门_第56张图片

7.3.3. 故障现象和导致原因

 8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕
 80此时调用8001,客户端访问响应缓慢,转圈圈

7.3.4. 上诉结论

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

7.3.5. 如何解决?解决的要求

 超时导致服务器变慢(转圈)
 超时不再等待
 出错(宕机或程序运行出错)
 出错要有兜底
 解决
 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
 对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
 对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级

7.3.6. 服务降级

1. 降低配置

@HystrixCommand

2. 8001先从自身找问题

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

3. 8001fallback

业务类启用阶级处理:使用@HystrixCommand注解来干活。

package com.atguigu.springcloud.service.impl;

import com.atguigu.springcloud.service.PaymentService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class PaymentServiceImpl implements PaymentService {

    @Override
    public String paymentInfo_OK(Integer id) {
        return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id="+id +" \t O(∩_∩)O哈哈~";
    }

    //超时降级演示
    @HystrixCommand(fallbackMethod = "payment_TimeoutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000") //5秒钟以内就是正常的业务逻辑
    })
    @Override
    public String payment_Timeout(Integer id) {
        //int timeNumber = 3; //小于等于3秒算是正常情况
        int timeNumber = 15; //模拟非正常情况
        //int i = 1/0 ; //模拟非正常情况
        try {
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:"+Thread.currentThread().getName()+" payment_Timeout,id="+id+" \t o(╥﹏╥)o 耗时:"+timeNumber;
    }

    //兜底方法,上面方法出问题,我来处理,返回一个出错信息
    public String payment_TimeoutHandler(Integer id) {
        return "线程池:"+Thread.currentThread().getName()+" payment_TimeoutHandler,系统繁忙,请稍后再试\t o(╥﹏╥)o ";
    }
}

一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
主启动类激活
@EnableCircuitBreaker

测试超时和算数异常,都会走兜底方法——服务降级
http://localhost:8001/payment/hystrix/timeout/1
在这里插入图片描述

4. 80fallback

  1. 80订单微服务,也可以更好的保护自己,自己也依样画葫芦进行客户端降级保护。注意:服务降级可以在服务提供者侧,也可以在服务消费者侧。更多是在服务消费者侧。
  2. 题外话,切记
    我们自己配置过的热部署方式对java代码的改动明显,但对@HystrixCommand内属性的修改建议重启微服务
  3. YML
feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

  1. 主启动
    @EnableHystrix
  2. 业务类:OrderHystrixController
    //超时降级演示
    @HystrixCommand(fallbackMethod = "payment_TimeoutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")//超过1.5秒就降级自己
    })
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        //int age= 1/0;
        String result = paymentHystrixService.payment_Timeout(id);
        log.info("*******result:"+result);
        return result;
    }

    //兜底方法,上面方法出问题,我来处理,返回一个出错信息
    public String payment_TimeoutHandler(Integer id) {
        return "我是消费者80,对方支付系统繁忙请10秒后再试。或自己运行出错,请检查自己。";
    }

  1. 测试超时
    http://localhost/consumer/payment/hystrix/timeout/1
    在这里插入图片描述

5. 目前问题

每个业务方法对应一个兜底的方法,代码膨胀,代码耦合
统一通用处理和自定义独立处理的分开

6. 解决问题

  1. 每个方法配置一个???膨胀
    feign接口系列
    @DefaultProperties(defaultFallback = “”)
    SpringCloud入门_第57张图片
    说明
    @DefaultProperties(defaultFallback = “”)
    1:1 每个方法配置一个服务降级方法,技术上可以,但实际上傻X
    1:N 除了个别重要核心业务有专属,其它普通的可以通过@DefaultProperties(defaultFallback = “”)统一跳转到统一处理结果页面
    通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量
    controller配置
package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
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
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")  //全局的
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

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

    @HystrixCommand
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        int age = 10/0;
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }

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

    //下面是全局fallback方法
    public String payment_Global_FallbackMethod(){
        return "Global异常处理信息,请稍后再试,(┬_┬)";
    }
}

测试结果

在这里插入图片描述

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

服务降级,客户端去调用服务端,碰上服务端宕机或关闭
本次案例服务降级处理是在客户端80实现完成的,与服务端8001没有关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
未来我们要面对的异常
 运行
 超时
 宕机
再看我们的业务类PaymentController
修改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 #如果处理自身的容错就开启。开启方式与生产端不一样。

PaymentFeignClientService接口

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",fallback = PaymentFallbackService.class)
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);
}

3) 测试

单个eureka先启动7001
PaymentHystrixMain8001启动
正常访问测试:http://localhost/consumer/payment/hystrix/ok/31
故意关闭微服务8001
客户端自己调用提升
此时服务端provider已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器

7.3.7. 服务熔断

1. 断路器

一句话就是家里保险丝

2. 熔断是什么

熔断机制概述
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在SpringCloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状态,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand

3. 大神论文:

https://martinfowler.com/bliki/CircuitBreaker.html
SpringCloud入门_第58张图片

4. 实操

1) 修改cloud-provider-hystrix-payment8001

2) PaymentServiceImpl

com.netflix.hystrix.HystrixCommandProperties

//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否开启断路器
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),   //当在配置时间窗口内达到此数量的失败后,打开断路,默认20个
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),  //断路多久以后开始尝试是否恢复,默认5s
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
})
public String paymentCircuitBreaker(Integer id){
    if (id < 0){
        throw new RuntimeException("*****id 不能负数");
    }
    String serialNumber = IdUtil.simpleUUID();//hutool.cn工具包

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

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) 测试

自测cloud-provider-hystrix-payment8001
正确: http://localhost:8001/payment/circuit/31
错误: http://localhost:8001/payment/circuit/-31
一次正确一次错误trytry
重点测试
多次错误(狂点),然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路

5. 原理(小总结)

1) 大神结论

SpringCloud入门_第59张图片

2) 熔断类型

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

3) 官网断路器流程图

①	官网步骤
②	断路器在什么情况下开始起作用
    //服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"),   //当快照时间窗(默认10秒)内达到此数量才有资格打开断路,默认20个
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "50000"),  //断路多久以后开始尝试是否恢复,默认5s
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
    })
涉及到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值。
1、快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
2、请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断。默认20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
3、错误百分比阈值:当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果在这30次调用,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。
③断路器开启或者关闭的条件
当满足一定阀值的时候(默认10秒内超过20个请求次数)
当失败率达到一定的时候(默认10秒内超过50%请求失败)
到达以上阀值,断路器将会开启
当开启的时候,所有请求都不会进行转发
一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复45
④	断路器打开之后
1:再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallbak。通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
2:原来的主逻辑要如何恢复呢?
对于这一个问题,hystrix也为我们实现了自动恢复功能。
当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
⑤	All配置

SpringCloud入门_第60张图片

7.3.8. 服务限流

后面讲

7.4. 服务监控hystrixDashboard

7.4.1. 概述

除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stram项目实现了对以上指示的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。

7.4.2. 仪表盘9001

1. 新建Module:cloud-consumer-hystrix-dashboard9001

2. POM


<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-consumer-hystrix-dashboard9001artifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>
        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>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
project>

3. YML

server:
  port: 9001

4. HystrixDashboardMain9001+新注解@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);
    }
}

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

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

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

http://localhost:9001/hystrix
在这里插入图片描述

7.4.3. 断路器演示

1. 修改cloud-provider-hystrix-payment8001

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

/**
 *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
 *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
 *只要在自己的项目里配置上下面的servlet就可以了
 */
@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

2. 监控测试

1) 启动1个eureka

2) 观察监控窗口

9001监控8001
http://localhost:9001/hystrix
http://localhost:8001/hystrix.stream
SpringCloud入门_第61张图片
测试地址
http://localhost:8001/payment/circuit/31
http://localhost:8001/payment/circuit/-31
上述测试通过
ok
先访问正确地址,再访问错误地址,再正确地址,会发现图示断路器都是慢慢放开的
监控结果,成功
在这里插入图片描述
监控结果,失败

SpringCloud入门_第62张图片
如何看
7色

在这里插入图片描述
1圈
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实体中快速的发现故障实例和高压力实例。
1线
曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。
整图说明
SpringCloud入门_第63张图片
整图说明2

SpringCloud入门_第64张图片
搞懂一个才能看懂复杂的

SpringCloud入门_第65张图片

8. Gateway新一代网关

8.1. 概述简介

8.1.1. 官网

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

SpringCloud入门_第66张图片

8.1.2. 是什么

Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关https://github.com/Netflix/zuul/wiki
但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关代替Zull,那就是SpringCloud Geteway;

8.1.2.1. 一句话:Geteway是原Zuul1.x版的替代

SpringCloud入门_第67张图片

8.1.2.2. 概述

Gateway是在spring生态系统之上构建的API网关服务,基于Spring5,SpringBoot2和Project Reactor等技术。
Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等
SpringCloud入门_第68张图片
SpringCloud Gateway是SpringCloud的一个全新项目,基于Spring5.0+SpringBoot2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。
为了提升网关的性能,SpringCloud Gatway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通讯框架Netty。
SpringCloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/指标、和限流。

8.1.2.3. 一句话

Spring Cloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架
源码架构
SpringCloud入门_第69张图片

8.1.3. 能干嘛

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

8.1.4. 微服务架构中网关在哪里

SpringCloud入门_第70张图片

8.2. 三大核心概念

8.2.1. Route(路由)

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由

8.2.2. Predicate(断言)

参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

8.2.3. Filter(过滤)

指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。

8.2.4. 总体

SpringCloud入门_第71张图片
Web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
Predicate就是我们的匹配条件: 而Filter,就是可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。

8.3. Gateway工作流程

8.3.1. 官网总结

https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gateway-how-it-works

SpringCloud入门_第72张图片
在这里插入图片描述
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求匹配的路由,将其发送到Gateway Web Handler.
Handler再通过指定的过滤器链来将请求发送给我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
Filter在"pre"类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量控制等有着非常重要的作用

8.3.2. 核心逻辑:路由转发+执行过滤器链

8.4. 入门配置

8.4.1. 新建Module:cloud-gateway-gateway9527

8.4.2. POM


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

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-gatewayartifactId>
        dependency>
        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <version>1.0-SNAPSHOTversion>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
project>

8.4.3. YML

server:
  port: 9527
spring:
  application:
    name: cloud-gateway

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


8.4.4. 业务类

8.4.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 GateWayMain9527 {
    public static void main(String[] args) {
            SpringApplication.run( GateWayMain9527.class,args);
        }
}

8.4.6. 9527网关如何做路由映射呢???

我们目前不想暴露8001端口,希望在8001外面套一层9527

8.4.7. 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://localhost:7001/eureka


8.4.8. 测试

启动7001:cloud-eureka-server7001
启动8001:cloud-provider-payment8001
启动9527网关:cloud-gateway-gateway9527
访问说明
SpringCloud入门_第73张图片
添加网关前: http://localhost:8001/payment/get/31
添加网关后: http://localhost:9527/payment/get/31

8.5. 通过微服务名实现动态路由

默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能

8.5.1. 启动

一个eureka7001+两个服务提供者8001/8002

8.5.2. POM

8.5.3. YML

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://localhost:7001/eureka


需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。
lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri

8.5.4. 测试

http://localhost:9527/payment/lb
8001/8002两个端口切换

8.6. Predicate的使用

8.6.1. 是什么

启动我们的gatewat9527,查看启动日志

SpringCloud入门_第74张图片

8.6.2. Route Predicate Factories这个是什么东东?

SpringCloud入门_第75张图片
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapper基础框架的一部分。
Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合
Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给 Route。Spring Cloud Gateway包含许
多内置的Route Predicate Factories。

所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and 。

8.6.3. 常用的Route Predicate

1.After Route Predicate

SpringCloud入门_第76张图片
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
 - After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
• 测试:没到时间进行测试报错
SpringCloud入门_第77张图片

Before Route Predicate

• YML
 - 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

SpringCloud入门_第78张图片
• YML
 - Cookie=username,atguigu #并且Cookie是username=zhangshuai才能访问
• 不带cookies访问
在这里插入图片描述

• 带上cookies访问
 curl下载地址:https://curl.haxx.se/download.html
在这里插入图片描述

• 加入curl返回中文乱码(帖子): https://blog.csdn.net/leedee/article/details/82685636

5.Header Route Predicate

SpringCloud入门_第79张图片
• YML

  • Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
    在这里插入图片描述

6.Host Route Predicate

YML: - Host=**.atguigu.com

7.Method Route Predicate

YML:- Method=GET

8.Path Route Predicate

YML:

9. Query Route Predicate

YML: - Query=username, \d+ #要有参数名称并且是正整数才能路由

10.小总结

• All

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://localhost:7001/eureka


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

8.7. Filter的使用

8.7.1. 是什么

路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
SpringCloud Gateway内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生。
SpringCloud入门_第80张图片

8.7.2. Spring Cloud Gateway的Filter

1. 生命周期,Only Two

• pre
 在业务逻辑之前
• post
 在业务逻辑之后

2. 种类,Only Two

https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/#gatewayfilter-factories

1) GatewayFilter(31种之多)

SpringCloud入门_第81张图片

2) GlobalFilter

SpringCloud入门_第82张图片

8.7.3. 常用的GatewayFilter

AddRequestParameter
YML
SpringCloud入门_第83张图片

8.7.4. 自定义过滤器

1. 自定义全局GlobalFilter

两个主要接口介绍
impiemerts GlobalFilter ,Ordered

2. 能干嘛

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

3. 案例代码

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.util.StringUtils;
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("username");
        if(StringUtils.isEmpty(username)){
            log.info("*****用户名为Null 非法用户,(┬_┬)");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

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

4. 测试

启动

SpringCloud入门_第84张图片
正确:http://localhost:9527/payment/lb?username=z3
错误:http://localhost:9527/payment/lb?uname=z3

9. SpringCloud Sleuth分布式链路请求跟踪

9.1. 概述

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

问题
在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的服务节点调用来协同产生最后的请求结果,每一个前端请求都会形成一个复杂的分布式服务调用链路,链路中的任何一环出现高延时或错误都会引起整个请求最后的失败。
SpringCloud入门_第85张图片

9.1.2. 是什么

Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案
在分布式系统中提供追踪解决方案并且兼容支持了zipkin(负责展现)
https://github.com/spring-cloud/spring-cloud-sleuth
SpringCloud入门_第86张图片
https://cloud.spring.io/spring-cloud-sleuth/reference/html/

SpringCloud入门_第87张图片

9.1.3. 解决

SpringCloud入门_第88张图片

9.2. 搭建链路监控步骤

9.2.1. zipkin

下载
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
运行控制台
http://localhost:9411/zipkin/
术语
完整的调用链路
表示一请求链路,一条链路通过Trace Id唯一标识,Span标识发起的请求信息,各span通过parent id 关联起来。
SpringCloud入门_第89张图片
上图what
一条链路通过Trace Id唯一标识,Span标识发起的请求信息,各span通过parent id 关联起来。
SpringCloud入门_第90张图片
整个链路的依赖关系如下:

SpringCloud入门_第91张图片
名词解释
Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识
span:表示调用链路来源,通俗的理解span就是一次请求信息

9.2.2. 服务提供者

1. 修改:cloud-provider-payment8001

2. POM


<dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-zipkinartifactId>
dependency>

3. YML

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      #采样率值介于0~1之间,1表示全部采样
      probability: 1
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root

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

eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka


4. 业务类PaymentController

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

9.2.3. 服务消费者(调用

方)

1. 修改:cloud-consumer-order80

2. POM

 

            org.springframework.cloud
            spring-cloud-starter-zipkin


3. YML

server:
  port: 80

spring:
    application:
        name: cloud-order-service
    zipkin:
      base-url: http://localhost:9411
    sleuth:
      sampler:
        probability: 1

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: false
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #单机
      defaultZone: http://localhost:7001/eureka
      #集群
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版


4. 业务类PaymentController

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

9.2.4. 依次启动eureka7001/8001/80

80调用8001几次测试下

9.2.5. 打开浏览器访问: http://localhost:9411

会出现以下界面
查看
SpringCloud入门_第92张图片
查看依赖关系

SpringCloud入门_第93张图片

10. SpringCloud Alibaba入门简介

10.1. why会出现SpringCloud alibaba

Spring Cloud Netflix项目进入维护模式
https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now
SpringCloud入门_第94张图片
说明:
Spring Cloud Netflix项目进入维护模式
最近,Netflix宣布Hystrix正在进入维护模式。自2016年以来,Ribbon已处于类似状态。
虽然Hystrix和Ribbon现已处于维护模式,但它们仍然在Netflix大规模部署。
Hystrix Dashboard和Turbine已被Atlas取代。这些项目的最后一次提交是2年和4年前。Zuul1和Archaius1都被后来不兼容的版本所取代。
这不包括Eureka或并发限制模块。
什么是维护模式?
将模块置于维护模式,意味着SpringCloud团队将不会再向模块添加新功能。我们将修复block级别的bug以及安全问题,我们也会考虑并审查社区的小型pull request。
我们打算继续支持这些模块,直到Greenwich版本被普遍采用至少一年。
进入维护模式意味着什么?
Spring Cloud Netflix将不再开放新的组件
我们都知道SpringCloud版本迭代算是比较快的,因而出现了很多重大ISSU都还来不及Fix就又推出另一个Release了。
进入维护模式意思就是目前以致以后一段时间SpringCloud netflix提供的服务和功能就这么多了,不再开发新的组件和功能了。以后将以维护和Merge分支Pull Request为主。
新组件功能将以其他替代的方式实现
SpringCloud入门_第95张图片

10.2. SpringCloud alibaba带来了什么?

10.2.1. 是什么

诞生:2018.10.31,Spring Cloud Alibaba正式入驻了Spring Cloud官网孵化器,并在Maven中央库发布了第一个版本。
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
SpringCloud入门_第96张图片

10.2.2. 能干嘛

 服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

10.2.3. 去哪下

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-dependenciesartifactId>
            <version>2.2.0.RELEASEversion>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

10.2.4. 怎么玩

一整套解决方案,简单理解就是替换Netflix那一套
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: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

10.3. SpringCloud alibaba学习资料获取

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

SpringCloud入门_第97张图片
在这里插入图片描述
英文
https://github.com/alibaba/spring-cloud-alibaba
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
中文
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

11. SpringCloud Alibaba Nacos服务注册和配置中心

11.1. Nacos简介

11.1.1. 为什么叫Nacos

前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service

11.1.2. 是什么

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

11.1.3. 能干嘛

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

11.1.4. 去哪下

https://github.com/alibaba/Nacos
SpringCloud入门_第98张图片
官网文档
https://nacos.io/zh-cn/index.html
SpringCloud入门_第99张图片
SpringCloud入门_第100张图片
SpringCloud入门_第101张图片
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html

SpringCloud入门_第102张图片

11.1.5. 各种注册中心比较

SpringCloud入门_第103张图片
据说nacos在阿里巴巴内部有超过10万的实例运行,已经过了类似双十一等各种大型流量的考验
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。

11.2. 安装并运行Nacos

11.2.1. 本地Java8+Maven环境已经OK

11.2.2. 先从官网下载Nacos

https://github.com/alibaba/nacos/releases/tag/1.1.4

11.2.3. 解压安装包,直接运行bin/startup.cmd

11.2.4. 命令运行成功后直接访问

http://localhost:8848/nacos
默认账号密码都是nacos

11.2.5. 登录结果页面

SpringCloud入门_第104张图片

11.3. Nacos作为服务注册中心演示

11.3.1. 官网文档

https://spring.io/projects/spring-cloud-alibaba#learn
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
SpringCloud入门_第105张图片

11.3.2. 基于Nacos的服务提供者

1. 新建Module:cloudalibaba-provider-payment9001

2. POM

 父POM


<dependency>
  <groupId>com.alibaba.cloudgroupId>
  <artifactId>spring-cloud-alibaba-dependenciesartifactId>
  <version>2.1.0.RELEASEversion>
  <type>pomtype>
  <scope>importscope>
dependency>

 本模块POM

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    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>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    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: '*'  #默认只公开了/health和/info端点,要想暴露所有端点只需设置成星号

4. 主启动

package com.atguigu.springcloud.alibaba;

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

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

5. 业务类

package com.atguigu.springcloud.alibaba.controller;

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;

@RestController
public class PaymentController{
    @Value("${server.port}")
    private String serverPort;
 
    @GetMapping(value = "/payment/nacos/{id}")
    public String getPayment(@PathVariable("id") Long id) {
        return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
    }
}

6. 测试

http://localhost:9001/payment/nacos/1
nacos控制台
SpringCloud入门_第106张图片
nacos服务注册中心+服务提供者9001都ok了

7. 为了下一章节演示nacos的负载均衡,参照9001新建9002

新建cloudalibaba-provider-payment9002
9002其他步骤你懂的
或者取巧不想新建重复体力劳动,直接拷贝虚拟端口映射
SpringCloud入门_第107张图片
SpringCloud入门_第108张图片
SpringCloud入门_第109张图片
SpringCloud入门_第110张图片
SpringCloud入门_第111张图片

11.3.3. 基于Nacos的服务消费者

1. 新建Module:cloudalibaba-consumer-nacos-order83

2. POM

<dependencies>
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
        <dependency>
        <groupId>com.atguigu.springcloudgroupId>
        <artifactId>cloud-api-commonsartifactId>
        <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>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
dependencies>

为什么nacos支持负载均衡

SpringCloud入门_第112张图片

3. YML

server:
  port: 83

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者【可选】,注意:nacos-payment-provider含有IP和端口)
service-url:
  nacos-user-service: http://nacos-payment-provider


4. 主启动

package com.atguigu.springcloud.alibaba;

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

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

5. 业务类

ApplicationContextBean

package com.atguigu.springcloud.alibaba.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{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

6. OrderNacosController

package com.atguigu.springcloud.alibaba.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.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;

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

}

7. 测试

nacos控制台
SpringCloud入门_第113张图片
http://localhost:83/consumer/payment/nacos/1
83访问9001/9002,轮询负载OK

11.3.4. 服务注册中心对比

1. Nacos和CAP

CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。
如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足。
因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。
SpringCloud入门_第114张图片

2. Nacos支持AP和CP模式的切换

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

11.4. Nacos作为服务配置中心演示

11.4.1. Nacos作为配置中心-基础配置

1. 创建Module:cloudalibaba-config-nacos-client3377

2. POM

<dependencies>
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    dependency>
    
    <dependency>
        <groupId>com.alibaba.cloudgroupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    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>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
dependencies>

3. YML

Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
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格式的配置(yml和yaml都可以)

#${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
#nacos-config-client-dev.yaml  (一定要与file-extension值保持一致)

application.yml

spring:
  profiles:
    active: dev #表示开发环境

4. 主启动

package com.atguigu.springcloud.alibaba;

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

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

5. 业务类:ConfigClientController

package com.atguigu.springcloud.alibaba.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   //通过SpringCould原生注解@RefreshScope实现配置自动更新
public class ConfigClientController{
    @Value("${config.info}") //对应nacos配置:nacos-config-client-dev.yaml
    private String configInfo;

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

6. 在Nacos中添加配置信息

Nacos中的匹配规则
Nacos中的dataid的组成格式与SpringBoot配置文件中的匹配规则
官网 https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
SpringCloud入门_第115张图片
最后公式:
s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name{spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
nacos-config-client-dev.yaml
Nacos界面配置对应
SpringCloud入门_第116张图片
设置DataId

公式: s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name{spring.profile.active}.${spring.cloud.nacos.config.file-extension}
小总结说明
SpringCloud入门_第117张图片
历史配置:Nacos会记录配置文件的历史版本默认保留30天

7. 测试

启动前需要在nacos客户端-配置管理-配置管理栏目下有没有对应的yaml配置文件
运行cloud-config-nacos-client3377的主启动类
调用接口查看配置信息: http://localhost:3377/config/info

8. 自带动态刷新

修改Nacos中的yaml配置文件,查看配置已经刷新

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

1. 问题

多环境多项目管理
问题1
• 实际开发中,通常一个系统会准备
• dev开发环境
• test测试环境
• prod生产环境
• 如何保证指定环境启动时服务能正确读取到 Nacos上相应环境的配置文件呢?
问题2
• 一个大型分布式微服务心痛会有很多微服务子项目
• 每一个微服务项目又会相应的开发环境、测试环境、预发环境、正式环境….
• 那怎么对这些微服务配置进行管理呢?

2. Nacos的图形化管理界面

• 配置管理

SpringCloud入门_第118张图片
• 命名空间

SpringCloud入门_第119张图片

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

最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
SpringCloud入门_第120张图片
默认情况:Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT
 Nacos默认的命名空间是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的 Namespace之间是隔离的。
 Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去。Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ),给广州机房的Service微服务起一个集群名字(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。
 最后是Instance,就是微服务的实例。

4. Case

1) DataID方案

指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置
默认空间+默认分组+新建dev和test两个DataID
新建dev配置DataIDSpringCloud入门_第121张图片
新建test配置DataID

SpringCloud入门_第122张图片
通过spring.profile.active属性就能进行多环境下配置文件的读取

SpringCloud入门_第123张图片
测试
http://localhost:3377/config/info
配置是什么就加载什么
在这里插入图片描述

2) Group方案

通过Group实现环境区分
新建Group
SpringCloud入门_第124张图片
SpringCloud入门_第125张图片
在nacos图形界面控制台上面新建配置文件DataID

SpringCloud入门_第126张图片
bootstrap+application

SpringCloud入门_第127张图片
在config下增加一条group的配置即可。可配置为DEV_GROUP或TEST_GROUP

3) Namespace方案

新建dev/test的Namespace
SpringCloud入门_第128张图片
SpringCloud入门_第129张图片
回到服务管理-服务列表查看

SpringCloud入门_第130张图片
按照域名配置填写

nacos-config-client-dev.yaml

config: 
  info: 9f62d48c-ef2e-4d83-a9fb-c9db5833f93b DEFAULT_GROUP nacos-config-client-dev.yaml

SpringCloud入门_第131张图片
YML
bootstrap

# nacos配置
server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yaml #指定yaml格式的配置
        group: DEV_GROUP
        namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4

# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# nacos-config-client-dev.yaml

application

spring:
  profiles:
    active: dev # 表示开发环境
    #active: test # 表示测试环境
    #active: info

图解:

SpringCloud入门_第132张图片
测试结果:

在这里插入图片描述

11.5. Nacos集群和持久化配置(重要)

11.5.1. 官网说明

https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html

1. 官网架构图

集群部署架构图
推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面
http://ip1:port/openAPI 直连ip模式,机器挂则需要修改ip才可以使用。
http://VIP:port/openAPI 挂载VIP模式,直连vip即可,下面挂server真实ip,可读性不好。
http://nacos.com:port/openAPI 域名 + VIP模式,可读性好,而且换ip方便,推荐模式
SpringCloud入门_第133张图片

2. 上图官网翻译,真实情况

SpringCloud入门_第134张图片

3. 说明

默认Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题多的。
为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL 的存储。
SpringCloud入门_第135张图片
SpringCloud入门_第136张图片
https://nacos.io/zh-cn/docs/deployment.html

11.5.2. Nacos持久化配置解释

1. Nacos默认自带的是嵌入式数据库derby

https://github.com/alibaba/nacos/blob/develop/config/pom.xml

2. derby到mysql切换配置步骤

nacos-server-1.1.4\nacos\conf目录下找到sql脚本
nacos-mysql.sql
执行脚本
nacos-server-1.1.4\nacos\conf目录下找到application.properties

spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=nacos_devtest
db.password=youdontknow

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

4. 测试:新建配置,发现配置信息写入了MySQL数据库

11.5.3. Linux版Nacos+MySQL生产环境配置

1. 预计需要,1个nginx+3个nacos注册中心+1个mysql

2. Nacos下载linux版本

SpringCloud入门_第137张图片
https://github.com/alibaba/nacos/releases/tag/1.1.4
nacos-server-1.1.4.tar.gz

3. 集群配置步骤(重点)

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

• SQL脚本在哪里
在这里插入图片描述
SpringCloud入门_第138张图片

2.application.properties配置

spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://192.168.137.128:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=zhang3
db.password=123456

3.Linux服务器上nacos的集群配置cluster.conf

梳理出3台nacos机器的不同服务端口号
复制出cluster.conf
在这里插入图片描述
内容

192.168.137.128:3333
192.168.137.128:4444
192.168.137.128:5555

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

在这里插入图片描述

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

/mynacos/nacos/bin目录下有startup.sh
在什么地方,修改什么,怎么修改
思考
/mynacos/nacos/bin目录下有startup.sh
平时单机版的启动,都是./startup.sh即可。
但是
集群启动,我们希望可以类似其它软件的shell命令,传递不同的端口号启动不同的nacos实例。
命令:./startup.sh -p 3333表示启动端口号为3333的nacos服务器实例,和上一步的cluster.conf配置的一致。
修改内容
SpringCloud入门_第139张图片
修改前

SpringCloud入门_第140张图片
修改后
SpringCloud入门_第141张图片

修改前
在这里插入图片描述

修改后
在这里插入图片描述

执行方式
启动时,需要修改内存大小,否则,内存可能不够用。
在这里插入图片描述
SpringCloud入门_第142张图片
查看进程:ps -ef | grep nacos

SpringCloud入门_第143张图片

5.Nginx的配置,由它作为负载均衡器

修改nginx的配置文件:vim /usr/local/nginx/conf/nginx.conf
nginx.conf

upstream cluster{ 
    server 192.168.137.128:3333;
    server 192.168.137.128:4444;
    server 192.168.137.128:5555;
}

server{               
    listen 1111;
    server_name 192.168.137.128;
    location / {
         proxy_pass http://cluster;                        
    }
....省略  

启动Nginx:

在这里插入图片描述

6.截止到此处,1个Nginx+3个nacos注册中心+1个mysql

测试通过nginx访问nacos
https:// 192.168.137.128:1111/nacos/#/login
SpringCloud入门_第144张图片
新建一个配置测试

SpringCloud入门_第145张图片
linux服务器的mysql插入一条记录

SpringCloud入门_第146张图片

4. 测试

微服务cloudalibaba-provider-payment9002启动注册进nacos集群
yml
server-addr: 写你自己的虚拟机ip:1111
SpringCloud入门_第147张图片
结果

SpringCloud入门_第148张图片

5. 高可用小总结

SpringCloud入门_第149张图片

12. SpringCloud Alibaba Sentinel实现熔断与限流

12.1. Sentinel介绍

12.1.1. 官网

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

12.1.2. 是什么

一句话解释,之前我们讲解过的Hystrix
SpringCloud入门_第150张图片
SpringCloud入门_第151张图片

12.1.3. 能干嘛

SpringCloud入门_第152张图片
SpringCloud入门_第153张图片

12.1.4. 去哪下

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

12.1.5. 怎么玩

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

SpringCloud入门_第154张图片
 服务使用中的各种问题
 服务雪崩
 服务降级
 服务熔断
 服务限流

12.2. 安装Sentinel控制台

12.2.1. sentinel组件由2部分组成

Sentinel 分为两个部分:
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
 后台
 前台8080

12.2.2. 安装步骤

  1. 下载
    https://github.com/alibaba/Sentinel/releases
    下载到本地sentinel-dashboard-1.7.0.jar
  2. 运行命令
    前提
    java8环境OK
    8080端口不能被占用
    命令
    java -jar sentinel-dashboard-1.7.0.jar
  3. 访问sentinel管理界面
    http://localhost:8080
    登录账号密码均为sentinel

12.3. 初始化演示工程

12.3.1. 启动Nacos8848成功

http://localhost:8848/nacos/#/login

12.3.2. 案例

1. 创建Module:cloudalibaba-sentinel-service8401

2. POM


<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>cloudalibaba-sentinel-service8401artifactId>

    <dependencies>
        <dependency>
            <groupId>com.atguigu.springcloudgroupId>
            <artifactId>cloud-api-commonsartifactId>
            <version>${project.version}version>
        dependency>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>
        <dependency>
            <groupId>com.alibaba.cspgroupId>
            <artifactId>sentinel-datasource-nacosartifactId>
        dependency>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        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>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>4.6.3version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
project>

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,应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用HttpServer

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

SpringCloud入门_第155张图片
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints

在这里插入图片描述

4. 主启动

package com.atguigu.springcloud.alibaba;

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

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

5. 业务类FlowLimitController

package com.atguigu.springcloud.alibaba.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class FlowLimitController{
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB() {
        return "------testB";
    }
}

12.3.3. 启动Sentinel8080

java -jar sentinel-dashboard-1.7.0

12.3.4. 启动微服务8401

12.3.5. 启动8401微服务后查看sentienl控制台

空空如也,啥都没有
Sentinel采用的懒加载说明
执行一次访问即可
http://localhost:8401/testA
http://localhost:8401/testB
效果
SpringCloud入门_第156张图片
结论
sentinel8080正在监控微服务8401

12.4. 流控规则

12.4.1. 基本介绍

SpringCloud入门_第157张图片
进一步解释说明

SpringCloud入门_第158张图片
SpringCloud入门_第159张图片

12.4.2. 流控模式

1. 直接(默认)

• 直接->快速失败
 系统默认
• 测试QPS
 配置及说明
• 表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误

SpringCloud入门_第160张图片

 快速点击访问: http://localhost:8401/testA
 结果
• Blocked by Sentinel (flow limiting)
• 测试线程数

SpringCloud入门_第161张图片
 快速点击访问: http://localhost:8401/testA
 结果
• 不会出现Blocked by Sentinel (flow limiting)(线程处理请求很快)

SpringCloud入门_第162张图片
但是,在映射方法里添加sleep后,同样也会出现Blocked by Sentinel (flow limiting)默认提示信息。

 思考???
• 直接调用默认报错信息,技术方面OK but,是否应该有我们自己的后续处理?
• 类似有一个fallback的兜底方法?

  1. 关联
    • 是什么?
     当关联的资源达到阈值时,就限流自己
     当与A关联的资源B达到阈值后,就限流自己
     B惹事,A挂了
  2. 配置A
    • 设置效果:
     当关联资源/testB的QPS阀值超过1时,就限流/testA的REST访问地址,当关联资源到阀值后闲置配置的的资源名。

SpringCloud入门_第163张图片

  1. postman模拟并发密集访问testB

• 访问testB成功

SpringCloud入门_第164张图片
• postman里新建多线程集合组,将请求保存到集合组

SpringCloud入门_第165张图片• 运行线程集合组

SpringCloud入门_第166张图片

 设置并发访问参数

SpringCloud入门_第167张图片
• Run
 大批量线程高并发访问B,导致A失效了
• 运行后发现testA挂了
 点击访问http://localhost:8401/testA
 结果
• Blocked by Sentinel (flow limiting)

5. 链路

• 多个请求调用了同一个微服务
• 家庭作业试试

12.4.3. 流控效果

1. 直接->快速失败(默认的流控处理)

 直接失败,抛出异常:Blocked by Sentinel (flow limiting)
 源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

2. 预热

• 说明
 公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值
• 官网: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6#%E5%9F%BA%E4%BA%8Eqps%E5%B9%B6%E5%8F%91%E6%95%B0%E7%9A%84%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
SpringCloud入门_第168张图片

SpringCloud入门_第169张图片

 限流 冷启动
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

在这里插入图片描述
• Warmup配置
默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
 案例:阈值为10 + 预热时长设置5秒。
 系统初始化的阈值为10/3约等于3,即阈值刚开始为3;然后过了5秒后阈值才慢慢升高,恢复到10

SpringCloud入门_第170张图片

 多次点击http://localhost:8401/testB
 刚开始不行,后续慢慢OK
 应用场景
• 如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。
3. 排队等待
• 匀速排队,让请求以均匀的速度通过,阈值类型必须设置成QPS,否则无效。
• 设置含义:/testB每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

SpringCloud入门_第171张图片
• 官网

SpringCloud入门_第172张图片
• 源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
• 测试
 增加打印语句

@GetMapping("/testB")
public String testB() {
log.info(Thread.currentThread().getName()+"\t ...testB");
return "------testB";
}

 增加线程组:直接10个线程并发,排队被依次处理

SpringCloud入门_第173张图片

12.5. 降级规则

12.5.1. 官网

https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

SpringCloud入门_第174张图片

12.5.2. 基本介绍

o 整体介绍

SpringCloud入门_第175张图片

• RT(平均响应时间,秒级)
 平均响应时间 (DEGRADE_GRADE_RT):超过阈值 且 时间窗口内的请求>=5,两个条件同时满足后触发降级,窗口期过后关闭断路器
 RT 最大4900 ms,更大的需要通过启动配置项
-Dcsp.sentinel.statistic.max.rt=xxx 来配置。
 异常比例(秒级)
 QPS>=5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
 异常数(分钟级)
 异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
o 进一步说明
• Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如:调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误。
• 当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
o Sentinel的断路器是没有半开状态的
• 半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix
• 复习Hystrix

SpringCloud入门_第176张图片

12.5.3. 降级策略实战

1. RT

• 是什么

在这里插入图片描述
SpringCloud入门_第177张图片
• 测试
 代码

@GetMapping("/testD")
public String testD(){
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        log.info("testD 测试RT");
        return "------testD";
}

 配置

SpringCloud入门_第178张图片
 jmeter压测

SpringCloud入门_第179张图片
SpringCloud入门_第180张图片
 结论
永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,
如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了
后续停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK

2. 异常比例

• 是什么
在这里插入图片描述
SpringCloud入门_第181张图片
• 测试
 代码

@GetMapping("/testD")
public String testD() {
        log.info("testD 测试异常比例");
        int age = 10/0;
        return "------testD";
}

 配置

SpringCloud入门_第182张图片
 jmeter

在这里插入图片描述
 演示

在这里插入图片描述
SpringCloud入门_第183张图片
 结论
• 按照上述配置
• 单独访问一次,必然来一次报错一次(int age=10/0;),调一次错一次
在这里插入图片描述
• 开启jmeter后,直接高并发送请求,多次调用达到我们的配置条件了。
• 断路器开启(保险丝跳闸),微服务不可用了,不再报错error 而是服务降级了。

3. 异常数

• 是什么
在这里插入图片描述
时间窗口一定要大于等于60秒。

SpringCloud入门_第184张图片
• 测试
 代码

@GetMapping("/testE")
public String testE(){
    log.info("testE 测试异常数");
    int age = 10/0;
    return "------testE 测试异常数";
}

 配置
• http://localhost:8401/testE
• 第一次访问绝对报错,因为除数不能为零,我们看到error窗口,但是达到5次报错后,进入熔断后降级。
SpringCloud入门_第185张图片
 手动连续点5次后,进入降级

12.6. 热点key限流

12.6.1. 基本介绍

o 是什么

SpringCloud入门_第186张图片

12.6.2. 官网

https://github.com/alibaba/Sentinel/wiki/热点参数限流
SpringCloud入门_第187张图片

12.6.3. 承上启下复习

o 兜底方法
o 分为系统默认和客户自定义,两种
• 之前的case,限流出问题后,都是用sentinel系统默认的提示: Blocked by Sentinel(flow limiting)
• 我们能不能自定义?类似hystrix,某个方法出现问题了,就找对应的兜底降级方法?
• 结论
 从@HystrixCommand到@SentinelResource

12.6.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";  
}

  com.alibaba.csp.sentinel.slots.block.BlockException

12.6.5. 配置

o 配置

SpringCloud入门_第188张图片
在这里插入图片描述
o 默认
• @SentinelResource(value = “testHotKey”)
• 异常打到了前台用户界面,不友好

SpringCloud入门_第189张图片
o 自定义
• @SentinelResource(value = “testHotKey”,blockHandler = “deal_testHotKey”) //value值与资源名一致即可
• 方法testHostKey里面第一个参数只要QPS超过每秒1次,马上降级处理
o 测试
• error (1秒1下可以,但是,超过则降级,和p1参数有关)
http://localhost:8401/testHotKey?p1=abc
• error(1秒1下可以,但是,超过则降级,和p1参数有关)
http://localhost:8401/testHotKey?p1=abc&p2=33
• right(狂点不会触发降级,与p2参数无关)
http://localhost:8401/testHotKey?p2=abc

12.6.6. 参数例外项

o 上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
o 特殊情况
• 普通
 超过1秒钟一个后,达到阈值1后马上被限流
• 我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
• 特例
 假如当p1的值等于5时,它的阈值可以达到200
o 配置
• 添加按钮不能忘
SpringCloud入门_第190张图片
o 测试
http://localhost:8401/testHotKey?p1=5 对
http://localhost:8401/testHotKey?p1=3 错
• 当p1等于5的时候,阈值变为200
• 当p1不等于5的时候,阈值就是平常的1
o 前提条件
• 热点参数的注意点,参数必须是基本类型或者String
SpringCloud入门_第191张图片

12.6.7. 其他

o 手贱添加异常看看…
o @SentinelResource
• 处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理
o RuntimeException
• Int age = 10/0;这个是java运行时报出的运行时异常RuntimeException,@SentinelResource不管
o 总结:
• @SentinelResource主管配置出错,运行出错该走异常走异常

12.7. 系统规则

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

12.7.2. 各项配置参数说明

你可能感兴趣的:(javaee,spring,cloud,微服务,分布式)