springcloud学习笔记上半部分

 

目录

 

前言

跟随周阳老师的讲解步入java学习路线的最后一门课。
gitee

1 入门

1.1 版本介绍及选择

  1. springboot目前官网稳定版已经更新到了2.7.4版本。
    在这里插入图片描述
  • 其中2: 表示的主版本号,表示是我们的SpringBoot第二代产品,当功能模块有较大更新或者整体架构发生变化时,主版本号会更新。

  • 其中7: 表示的是次版本号,增加了一些新的功能但是主体的架构是没有变化的,是兼容的,只是局部的一些变动。。

  • 其中4: 表示的一般是bug的修改或者是小的变动。

RELEASE:希腊字母版本号。此版本号用户标注当前版本的软件处于哪个开发阶段

所以2.7.4.RELEASE合起来就是springboot的第二代版本的第7个小版本的第4次bug修复版本。

  • CURRENT:当前推荐的版本。
  • GA:稳定版,可用于生产
  • PRE :里程碑版/预览版本
  • SNAPSHOT : 快照
  • M1 M2 M3 : M是milestone的简写 里程碑的意思
  • snapshot: 快照
  • alpha : 内测
  • beta : 公测
  • SR : 修正版(稳定版发布之后,存在问题,进行修正的版本)
  1. springcloud目前官网稳定版本已经更新到2021.0.4
    在这里插入图片描述
  2. 可以通过访问这个网址来查询版本对应关系。
    在这里插入图片描述
  3. 本次版本选择。
工具版本
cloud Hoxton.SR1
boot 2.2.2.RELEASE
cloud alibaba 2.1.0.RELEASE
java java8
maven 3.5及以上
Mysql 5.7及以上

如果我们只用springboot,可以直接使用最新的。但是我们使用的是springcloud和springboot搭配使用,所以我们必须要按照官网给出的版本来。
在这里插入图片描述

  1. 2.x版本常用的组件pom
    在这里插入图片描述

1.2 cloud停更之后工具替换

  1. 以前
    在这里插入图片描述
  2. 现在(2020年之后)
    在这里插入图片描述

在这里插入图片描述
3. 相关参考资料
官网:springcloud —Hoxton.SR1
springcloud中文社区
官网:springboot 2.2.2.RELEASE


2 微服务架构编码构建

2.1 约定 > 配置 > 编码

在这里插入图片描述

在这里插入图片描述

2.2 IDEA新建project工作空间

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

[1] 父工程步骤

1 创建maven工程 springcloud2020

几个注意点(实测以前IDEA2021.3没有设置这个,项目也是正常运行,下面这些设置都是按照周阳老师脑图设置的)

  1. 我们先要选择好编码方式。我们默认都选择UTF-8格式。
    在这里插入图片描述
  2. 开启注解生效

在这里插入图片描述
3. java版本编译选择Java8
在这里插入图片描述
4. File Type 过滤
在这里插入图片描述
对于我们的项目,我们有时候不想一些无关紧要的文件显示在项目中,干扰我们编写。那么这个时候我们就可以进行一些设置来使得工程中不显示。

[2] 父工程POM

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

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

    <groupId>com.atguigu.springcloud</groupId>
    <artifactId>mscloud03</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
 
    <!-- 统一管理jar包版本 -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>
 
    <!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version  -->
    <dependencyManagement>
        <dependencies>
            <!--spring boot 2.2.2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Hoxton.SR1-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud alibaba 2.1.0.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <optional>true</optional>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

</project>

注意

  1. Maven中的DependencyManagement和Dependencies
    dependencyManagement

Maven 使用dependencyManagement 元素来提供了一种管理依赖版本号的方式。 通常会在一个组织或者项目的最顶层的父POM中看到dependencyManagement 元素。 使用pom.xml 中的dependencyManagement元素能让所有在子项目中引用一个依赖而不用显式的列出版本号。 Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement 元素的项目,然后它就会使用这个 dependencyManagement 元素中指定的版本号。

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

  • dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。  
    
  • 如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,
    才会从父项目中继承该项,并且version和scope都读取自父pom;
  • 如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
    
  1. maven中跳过单元测试
    我们在进行打包的时候,可以跳过单元测试,减少打包时间。
    有两种方式可以实现。
    第一种方式
    pom.xml中进行配置
<build><!-- maven中跳过单元测试 -->
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skip>true</skip>
            </configuration>
        </plugin>
    </plugins>
</build>

第二种方式
通过IDEA工具中的MAVEN模块。
在这里插入图片描述

[3] 父工程创建完成执行mvn:install将父工程发布到仓库方便子工程继承

注意
mysql版本说明

com.mysql.jdbc.Driver和mysql-connector-java 5一起用。
com.mysql.cj.jdbc.Driver和mysql-connector-java 6 一起用。
com.mysql.cj.jdbc.Driver是mysql-connector-java 6 中的特性,相比mysql-connector-java 5 多了一个时区:serverTimezone,把数据源配置的驱动改一下就好了
org.gjt.mm.mysql.Driver是当时最好的MySQL JDBC,但不是MySQL公司的,然后MySQL将MM的JDBC驱动 收为官方的JDBC驱动,所以将驱动的package也改了,但还保留了org.gjt.mm.mysql.Driver这个路径的引用,也就是你使用新版的JDBC驱动时还可以通过这个来引用,打开下载的新版JDBC驱动的jar文件可以看到,只有一个文件的目录是org.gjt.mm.mysql,就是为了兼容而设计的。

2.3 Rest微服务工程构建

构建步骤

我们要遵循一个规则进行构建。

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

2.3.1 cloud-provider-payment8001 微服务提供者支付Module模块

[1] 建module

我们在父工程下新建module,命名为cloud-provider-payment8001,创建好之后我们可以去父工程中查看。

在这里插入图片描述

[2] 改pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <artifactId>mscloud03</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8001</artifactId>


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

</project>

从子工程中我们可看出pom.xml中没有写版本号了,因为在父工程中已经约定了。

[3] 写YML

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包 com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:13306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: abc123


mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

我们编写yml,首先就是要进行端口号的设置,然后是应用名设置。

[4] 主启动类

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

[5] 业务类

1. 建表SQL
CREATE TABLE `payment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `serial` varchar(200) DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
2. entities

1.主实体类Payment

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable
{
    private Long id;
    private String serial;
}

2.Json封装体CommonResult
这个类是专门和前端交互的类。对于前后端分离的业务来说,前端不管后端具体业务逻辑,只需要成功和失败以及需要的数据。所以我们后端要将业务类的结果封装为一个可以供前端识别的类。业务操作是真正的业务类,后端来操作。返回的是封装类。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>
{
    private Integer code;
    private String  message;
    private T data;

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

接口PaymentDao

@Mapper  //import org.apache.ibatis.annotations.Mapper;
public interface PaymentDao
{
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}

mapper/PaymentMapper.xml

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

<mapper namespace="com.atguigu.springcloud.dao.PaymentDao">

<!--    public  int create(Payment payment);-->
    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial) values(#{serial});
    </insert>

    <resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <id column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>
<!--    public Payment getPaymentById(@Param("id") Long id);
        对于查询来说,要返回一个类型,我们在工作中常用的是一个resultMap来应对复杂的场景,而resultType适合较为简单的场景。
        -->
   <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
       select * from payment where id=#{id};
   </select>

</mapper>

4. service

PaymentService

public interface PaymentService
{
    public int create(Payment payment);

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

}

PaymentServiceImpl

@Service
public class PaymentServiceImpl implements PaymentService
{
    @Resource
    private PaymentDao paymentDao;


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

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

5. controller
@RestController
@Slf4j
 public class PaymentController {

    @Resource
    PaymentService paymentService;

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


    @PostMapping("/payment/create")   //使用rest风格  get 获取  delete 删除 post  新增 put 修改
    public CommonResult create(@RequestBody Payment payment){
        int result = paymentService.create(payment);
        log.info("*****插入结果:"+result);

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

    @GetMapping("/payment/get/{id}")   //使用rest风格  get 获取  delete 删除 post  新增 put 修改
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        log.info("*****查询结果:"+payment);

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

[6] 测试

  1. 对于getmapping这种get请求,我们可以使用http://localhost:8001/payment/get/31这个来进行测试。
  2. 对于post请求,我们要借助postman或者apifox这类工具来进行模拟。
  3. 对于以后一个父工程下有很多子工程,那么运行的话不能每次都在run这个模块下,我们可以通过一个仪表盘来进行统一管理。(IDEA2021.3之后没有RunDashborad),通过Service来进行。
    开启方法:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这个里面选择springboot,即可。

2.3.2 热部署Devtools

对于开发中,我们不可能每次修改代码之后都手动进行启动和停止,我们一般都是进行热部署。代码修改之后就进行自动编译。这样可以省去一大部分时间,让我们专注于 编写业务逻辑。

具体步骤

  1. pom.xml中添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
  1. 父工程pom.xml中添加依赖
<build>
	<!--这个finalName标签可要可不要-->
    <finalName>你自己的工程名字</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <fork>true</fork>
                <addResources>true</addResources>
            </configuration>
        </plugin>
    </plugins>
</build>
  1. 开启 Enabling automatic build
    在这里插入图片描述
    在这里插入图片描述
    ABCD四个都选。
  2. Update the value of
    在这里插入图片描述
    在这里插入图片描述
  3. 重启IDEA

2.3.3 cloud-consumer-order80 微服务消费者订单Module模块

[1] 建module

[2] 改pom

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

    <artifactId>cloud-consumer-order80</artifactId>

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

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

</project>

[3] 写YML

server:
  port: 80

[4] 主启动类

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

[5] 业务类

  1. entities
    主实体类Payment
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable
{
    private Long id;
    private String serial;
}

Json封装体CommonResult

public class CommonResult<T>
{
    private Integer code;
    private String message;
    private T data;

    public CommonResult()
    {
    }

    public CommonResult(Integer code, String message, T data)
    {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    public CommonResult( Integer code,String message) {
        this( code, message,null);
    }

    public CommonResult(T data) {
        this(200, "操作成功", data);
    }

    //setter--getter
    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}
  1. 首说RestTemplate
    是什么?

RestTemplate提供了多种便捷访问远程Http服务的方法,
是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集

官网地址

使用

使用restTemplate访问restful接口非常的简单粗暴无脑。 (url, requestMap, ResponseBean.class)这三个参数分别代表 REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。

  1. config配置类
@Configuration
public class ApplicationContextConfig
{
    @Bean
    public RestTemplate restTemplate()
    {
        return new RestTemplate();
    }
}

通过使用@bean注解将RestTemplate注入到容器中。

  1. controller
@RestController
public class OrderController
{

    public static final String PaymentSrv_URL = "http://localhost:8001";

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/create") //客户端用浏览器是get请求,但是底层实质发送post调用服务端8001
    public CommonResult create(Payment payment)
    {
        return restTemplate.postForObject(PaymentSrv_URL + "/payment/create",payment,CommonResult.class);
    }


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

[6] 测试

  1. http://localhost/consumer/payment/get/1
  2. http://localhost/consumer/payment/create?serial=aaaaa1

2.3.4 工程重构

[1] 发现问题

在这里插入图片描述
我们可以看出实体类重复了,增加了代码冗余。所以我们可以将实体类代码抽取出来。

[2] 新建cloud-api-commons

在父工程下,将抽取出来的代码放到一个子模块中。

[3] POM

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

    <artifactId>cloud-api-commons</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>

</project>

[4] entities

1.主实体类Payment

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable
{
    private Long id;
    private String serial;
}

2.Json封装体CommonResult

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>
{
    private Integer code;
    private String  message;
    private T data;

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

[5] maven命令clean install

在这里插入图片描述
打包放到本地仓库中,供其他模块使用。

[6] 订单80和支付8001分别改造

1.删掉实体类包
2. 在pom中引入公共模块
**80 和81 **

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

2.3.5 最后工程样图

在这里插入图片描述
在这里插入图片描述

3 Eureka服务注册与发现

3.1 Eureka基础知识

3.1.1 什么是服务治理

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

3.1.2 什么是服务注册

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

3.1.3 Eureka两组件

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

3.2 单机Eureka构建步骤

3.2.1 IDEA生成eurekaServer端服务注册中心

类似物业公司

[1] 建module cloud-eureka-server7001

[2] 改POM

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

    <artifactId>cloud-eureka-server7001</artifactId>

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

</project>

Spring Boot Actuator 模块提供了生产级别的功能,比如健康检查,审计,指标收集,HTTP 跟踪等,帮助我们监控和管理Spring Boot 应用。
这个模块是一个采集应用内部信息暴露给外部的模块,上述的功能都可以通过HTTP 和 JMX 访问。

注意: 1.X和2.X的对比说明

以前的老版本(当前使用2018)
<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
现在新版本(当前使用2020.2)
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

[3] 写yml

server:
  port: 7001

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

[4] 主启动

@SpringBootApplication
@EnableEurekaServer   # 表示当前这个模块是eureka服务端
public class EurekaMain7001
{
    public static void main(String[] args)
    {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}

[5] 测试

  1. http://localhost:7001/
  2. 在这里插入图片描述
    这里显示没有服务发现,原因是还没有服务注册进来,所以没有被发现啦。

3.2.2 EurekaClient端 cloud-provider-payment8001

会被注册进EurekaServer成为服务提供者provider,类似尚硅谷学校对外提供授课服务

[1] 建module cloud-provider-payment8001

[2] 改pom

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

    <artifactId>cloud-provider-payment8001</artifactId>


    <dependencies>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

注意 1.x和2.x的区别

以前老版本,别再使用
<dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
现在新版本,当前使用
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

[3] 写yml

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:13306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: abc123

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


mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

[4] 主启动

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

[5] 测试

  1. 先要启动EurekaServer
  2. http://localhost:7001/
    在这里插入图片描述
  3. 微服务注册名配置说明
    在这里插入图片描述

[6] 自我保护机制

在这里插入图片描述
我们看到出现红色的这一段文字,说明进入了eureka保护机制。

3.2.3 EurekaClient端 cloud-consumer-order80

[1] 建module cloud-consumer-order80

[2] 改pom

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

    <artifactId>cloud-consumer-order80</artifactId>

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

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

</project>

[3] 写yml

server:
  port: 80

spring:
    application:
        name: cloud-order-service

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

[4] 主启动

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

[5] 测试

  1. 先要启动EurekaServer,7001服务
  2. 再要启动服务提供者provider,8001服务
  3. eureka服务器
    在这里插入图片描述
  4. http://localhost/consumer/payment/get/31

3.3 集群Eureka构建步骤

3.3.1 Eureka集群原理说明

在这里插入图片描述

问题:微服务RPC远程服务调用最核心的是什么
高可用,试想你的注册中心只有一个only one, 它出故障了那就呵呵( ̄▽ ̄)"了,会导致整个为服务环境不可用,所以

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

3.3.2 EurekaServer集群环境构建步骤

在这里插入图片描述

[1] 新建cloud-eureka-server7002

既然是集群,那么参考cloud-eureka-server7001,新建cloud-eureka-server7002。

[2] 改Pom

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

    <artifactId>cloud-eureka-server7002</artifactId>



    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

</project>

[3] 修改映射配置

  1. 找到C:\Windows\System32\drivers\etc路径下的hosts文件
  2. 修改映射配置添加进hosts文件
    127.0.0.1 eureka7001.com
    127.0.0.1 eureka7002.com

[4] 写yml

以前单机

server:
  port: 7001

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

现在集群 7001

server:
  port: 7001


eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称 
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7002.com:7002/eureka/

7002

server:
  port: 7002


eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名称
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

需要修改的点是:hostname + defaultZone ,其实就是相互注册,相互守望。

[5] 主启动

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

3.3.3 将支付服务8001微服务发布到上面2台Eureka集群配置中

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456


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

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

修改的点其实就是defaultZone,从以前一个地址,变为两个地址。

3.3.4 将订单服务80微服务发布到上面2台Eureka集群配置中

server:
  port: 80

spring:
    application:
        name: cloud-order-service

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

修改的点其实就是defaultZone,从以前一个地址,变为两个地址。

3.3.5 测试

  1. 先要启动EurekaServer,7001/7002服务
  2. 再要启动服务提供者provider,8001
  3. 再要启动消费者,80
  4. http://localhost/consumer/payment/get/31 测试 ok

3.3.6 支付服务提供者8002集群环境构建

[1] 新建cloud-provider-payment8002

我们前面将服务端搭建了集群,那么客户端也需要搭建集群。针对客户端搭建集群,主要集中在服务提供者。

参考cloud-provider-payment8001 新建cloud-provider-payment8002

[2] 改pom

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

    <artifactId>cloud-provider-payment8002</artifactId>

    <dependencies>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

[3] 写yml

server:
  port: 8002

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

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


mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities    # 所有Entity别名类所在包

[4] 主启动

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

[5] 业务类

直接从8001粘

[6] 修改8001/8002的Controller

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

    @Resource
    private PaymentService paymentService;

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

        if(result > 0)
        {
            return new CommonResult(200,"插入成功,返回结果"+result+"\t 服务端口:"+serverPort,payment);
        }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,"查询成功"+"\t 服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
        }
    }
}

在这里插入图片描述
其实就加了这一段代码。

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

    @Resource
    private PaymentService paymentService;

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

        if(result > 0)
        {
            return new CommonResult(200,"插入成功,返回结果"+result+"\t 服务端口:"+serverPort,payment);
        }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,"查询成功"+"\t 服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
        }
    }
}

和8001加入的代码是一样的。

3.3.7 负载均衡

[1] bug

我们进行上面测试会发现一个bug.原因是:订单服务访问地址不能写死。

//public static final String PAYMENT_SRV = "http://localhost:8001";
 
// 通过在eureka上注册过的微服务名称调用
public static final String PAYMENT_SRV = "http://CLOUD-PAYMENT-SERVICE";

以前单机版是写死了的,现在是集群版,一个名称下可能有多个,所以通过写名称。通过eureka去拿。

[2] @LoadBalanced注解

使用@LoadBalanced注解赋予RestTemplate负载均衡的能力。

[3] ApplicationContextBean

@Configuration
public class ApplicationContextBean
{
    @Bean
    @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

3.3.8 测试

  1. 先要启动EurekaServer,7001/7002服务
  2. 再要启动服务提供者provider,8001/8002服务
  3. http://localhost/consumer/payment/get/31
  4. 结果

负载均衡效果达到
8001/8002端口交替出现

  1. Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了。

3.4 actuator微服务信息完善(以单机版为例)

3.4.1 主机名称:服务名称修改

在这里插入图片描述
含有主机名称

[1]修改cloud-provider-payment8001

在这里插入图片描述

[2] 修改之后

在这里插入图片描述

3.4.2 访问信息有IP信息提示

[1] 当前问题:没有IP提示

[2] 修改cloud-provider-payment8001

在这里插入图片描述

[3] 修改之后

在这里插入图片描述

3.5 服务发现Discovery

对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息。

3.5.1 修改cloud-provider-payment8001的Controller

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

    @Resource
    private PaymentService paymentService;

    @Resource
    private DiscoveryClient discoveryClient;

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

        if(result > 0)
        {
            return new CommonResult(200,"插入成功,返回结果"+result+"\t 服务端口:"+serverPort,payment);
        }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,"查询成功"+"\t 服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
        }
    }

    @GetMapping(value = "/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            System.out.println(element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance element : instances) {
            System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
                    + element.getUri());
        }
        return this.discoveryClient;
    }
}

相较于以前,加入了private DiscoveryClient discoveryClient; 然后写了一个方法来进行测试。

3.5.2 修改8001主启动类

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient //服务发现 为了让当前微服务对接(注册或发现服务)注册中心
public class PaymentMain8001
{
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

3.5.3 测试

  1. 先要启动EurekaServer
  2. 再启动8001主启动类,需要稍等一会儿
  3. http://localhost:8001/payment/discovery

3.6 Eureka自我保护

3.6.1 故障现象

概述

保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,
Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。

如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT.
RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE

在这里插入图片描述

3.6.2 导致原因

[1] 为什么会产生Eureka自我保护机制?

为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除。

[2] 什么是自我保护模式?

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。

在这里插入图片描述

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着。

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存
属于CAP里面的AP分支

3.6.3 怎么禁止自我保护

[1] 注册中心eureakeServer端7001

  1. 出厂默认,自我保护机制是开启的 eureka.server.enable-self-preservation=true

  2. 使用eureka.server.enable-self-preservation = false 可以禁用自我保护模式
    在这里插入图片描述

  3. 关闭效果
    在这里插入图片描述

  4. 在eurekaServer端7001处设置关闭自我保护机制

[2] 生产者客户端eureakeClient端8001

  1. 默认
    eureka.instance.lease-renewal-interval-in-seconds=30
    eureka.instance.lease-expiration-duration-in-seconds=90

  2. 配置
    在这里插入图片描述

  3. 测试

  • 7001和8001都配置完成
  • 先启动7001再启动8001
  • 在这里插入图片描述
  • 先关闭8001
    在这里插入图片描述
  • 我们看到马上被删除了。

4 Zookeeper服务注册与发现

Eureka停更
在这里插入图片描述

4.1 SpringCloud整合Zookeeper代替Eureka

4.1.1 注册中心Zookeeper

[1] zookeeper是一个分布式协调工具,可以实现注册中心功能

[2] 关闭Linux服务器防火墙后启动zookeeper服务器

[3] zookeeper服务器取代Eureka服务器,zk作为服务注册中心

4.1.2 服务提供者

[1] 新建cloud-provider-payment8004

[2] 改pom

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

    <artifactId>cloud-provider-payment8004</artifactId>


    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合zookeeper客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

[3] 写YML

#8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
  port: 8004
#服务别名----注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.111.144:2181

[4] 主启动

@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class PaymentMain8004
{
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentMain8004.class,args);
    }

}
 

[5] controller

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

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

[6] 启动8004注册进zookeeper

  1. 启动后我们发现一个问题,这个问题的原因是jar包冲突。
    在这里插入图片描述
    在这里插入图片描述
    jar包冲突的原因是我们自己引入的依赖是2.2.0RELEASE版本,而前面引入的curatorjar包中带了一个3.5.3的zkjar包。所以我们需要进行修改。

  2. 修改过的pom.xml
    在这里插入图片描述

[7] 验证测试

  • http://localhost:8004/payment/zk
    在这里插入图片描述
  • 获得json串后用在线工具查看试试
    在这里插入图片描述
  • 临时节点
    通过我们的验证分析,得出节点为临时节点,而非持久节点。

在这里插入图片描述

4.1.3 服务消费者

[1] 新建cloud-consumerzk-order80

[2] 改pom

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

    <artifactId>cloud-consumerzk-order81</artifactId>


    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- SpringBoot整合zookeeper客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!--先排除自带的zookeeper-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加zookeeper3.4.9版本-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

[3] 写yml

server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  cloud:
  #注册到zookeeper地址
    zookeeper:
      connect-string: 192.168.111.144:2181

[4] 主启动

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

[5] 业务类

  1. 配置Bean
@Configuration
public class ApplicationContextBean
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}
  1. Controller
@RestController
public class OrderZKController
{
    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/consumer/payment/zk")
    public String paymentInfo()
    {
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/zk", String.class);
        System.out.println("消费者调用支付服务(zookeeper)--->result:" + result);
        return result;
    }

}

[6] 验证测试

在这里插入图片描述
2. 访问地址验证测试
http://localhost/consumer/payment/zk

5 Consul服务注册与发现

5.1 Consul简介

5.1.1 是什么

官网
在这里插入图片描述

  • Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。

  • 提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。

  • 它具有很多优点。包括: 基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows。

5.1.2 能干嘛

Spring Cloud Consul 具有如下特性:
在这里插入图片描述

  • 服务发现 :提供HTTP和DNS两种发现方式。
  • 健康监测 : 支持多种方式,HTTP、TCP、Docker、Shell脚本定制化监控。
  • KV存储 : Key、Value的存储方式。
  • 多数据中心 : Consul支持多数据中心
  • 可视化Web界面

5.1.3 去哪下

https://www.consul.io/downloads

5.1.4 怎么玩

https://www.springcloud.cc/spring-cloud-consul.html

5.2 安装并运行Consul

  1. 官网安装说明
    https://learn.hashicorp.com/consul/getting-started/install.html
  2. 下载完成后只有一个consul.exe文件,硬盘路径下双击运行,查看版本号信息.
    3.
  3. 使用开发模式启动
  • consul agent -dev
  • 通过以下地址可以访问Consul的首页:http://localhost:8500
  • 结果页面
  • 在这里插入图片描述

5.3 服务提供者

5.3.1 新建Module支付服务provider8006

cloud-providerconsul-payment8006

5.3.2 POM

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

    <artifactId>cloud-providerconsul-payment8006</artifactId>


    <dependencies>
        <!--SpringCloud consul-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

5.3.3 YML

###consul服务端口号
server:
  port: 8006

spring:
  application:
    name: consul-provider-payment
####consul注册中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

5.3.4 主启动类

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

5.3.5 业务类

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

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

5.3.6 验证测试

http://localhost:8006/payment/consul
在这里插入图片描述

5.4 服务消费者

5.4.1 新建Module消费服务order80

cloud-consumerconsul-order80

5.4.2 POM

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

    <artifactId>cloud-consumerconsul-order82</artifactId>


    <dependencies>
         <!--SpringCloud consul-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

5.4.3 yml

###consul服务端口号
server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
####consul注册中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

5.4.4 主启动类

@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
public class OrderConsulMain80
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderConsulMain80.class,args);
    }
}

5.4.5 配置Bean

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

5.4.6 Controller

@RestController
public class OrderConsulController
{
    public static final String INVOKE_URL = "http://cloud-provider-payment"; //consul-provider-payment

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/payment/consul")
    public String paymentInfo()
    {
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul", String.class);
        System.out.println("消费者调用支付服务(consule)--->result:" + result);
        return result;
    }
}

5.4.7 验证测试

在这里插入图片描述
2. 访问测试地址
http://localhost/consumer/payment/consul

5.5 三个注册中心异同点

在这里插入图片描述
在这里插入图片描述

5.5.1 CAP

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

5.5.2 经典CAP图

最多只能同时较好的满足两个。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

在这里插入图片描述

[1] AP(Eureka)

AP架构
当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
结论:违背了一致性C的要求,只满足可用性和分区容错,即AP

在这里插入图片描述

[2] CP(Zookeeper/Consul)

CP架构
当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性
结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
在这里插入图片描述

6 Ribbon负载均衡服务调用

6.1概述

6.1.1 是什么

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具

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

6.1.2 官网资料

  1. https://github.com/Netflix/ribbon/wiki/Getting-Started

  2. Ribbon目前也进入维护模式
    在这里插入图片描述

  3. 未来替换方案
    在这里插入图片描述

6.1.3 能干嘛

LB(负载均衡)

  1. 是什么
    简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。常见的负载均衡有软件Nginx,LVS,硬件 F5等。
  2. Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别
  • Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。

  • Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。

集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;

进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

  • 前面我们讲解过了80通过轮询负载访问8001/8002
  • 一句话 : 负载均衡+RestTemplate调用

6.2 Ribbon负载均衡演示

6.2.1 架构说明

在这里插入图片描述

Ribbon在工作时分成两步
第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server.
第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

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

6.2.2 POM

之前写样例时候没有引入spring-cloud-starter-ribbon也可以使用ribbon.

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

猜测spring-cloud-starter-netflix-eureka-client自带了spring-cloud-starter-ribbon引用,
证明如下: 可以看到spring-cloud-starter-netflix-eureka-client 确实引入了Ribbon
在这里插入图片描述

6.2.3 二说RestTemplate的使用

[1] 官网地址

在这里插入图片描述

[2] getForObject方法/getForEntity方法

  • 返回对象为响应体中数据转化成的对象,基本上可以理解为Json ----- 常用
    在这里插入图片描述

  • 返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
    -

  • postForObject/postForEntity
    在这里插入图片描述

[3] GET请求方法

<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);
 
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> T getForObject(URI url, Class<T> responseType);
 
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
 
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> ResponseEntity<T> getForEntity(URI var1, Class<T> responseType);

[4] POST请求方法

<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
 
<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> T postForObject(URI url, @Nullable Object request, Class<T> responseType);
 
<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
 
<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType);

6.3 Ribbon核心组件IRule

6.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的可用性选择服务器

6.3.2 如何替换

[1] 修改cloud-consumer-order80

[2] 注意配置细节

官方文档明确给出了警告:
这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,
否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

在这里插入图片描述

[3] 新建package

com.atguigu.myrule
在这里插入图片描述

[4] 上面包下新建MySelfRule规则类

@Configuration
public class MySelfRule
{
    @Bean
    public IRule myRule()
    {
        return new RandomRule();//定义为随机
    }
}

[5] 主启动类添加@RibbonClient

@SpringBootApplication
@EnableEurekaClient
//在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效,形如:
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration=MySelfRule.class)
public class OrderMain80
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderMain80.class,args);
    }
}

[6] 测试

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

6.4 Ribbon负载均衡算法

6.4.1 原理

负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。

List instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”);

如: List [0] instances = 127.0.0.1:8002
   List [1] instances = 127.0.0.1:8001

8001+ 8002 组合成为集群,它们共计2台机器,集群总数为2, 按照轮询算法原理:

当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
如此类推…

6.4.2 手写本地负载均衡器

[1] 7001/7002集群启动

[2] 8001/8002微服务改造

修改controller
在这里插入图片描述
加入这一段代码。

[3] 80订单微服务改造

  1. ApplicationContextBean去掉注解@LoadBalanced
    在这里插入图片描述
  2. 创建LoadBalancer接口
public interface LoadBalancer
{
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
  1. 创建上面接口的实现类
@Component
public class MyLB implements LoadBalancer
{
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement()
    {
        int current;
        int next;
        do
        {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        } while(!this.atomicInteger.compareAndSet(current, next));
        System.out.println("*****next: "+next);
        return next;
    }


    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances)
    {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}
  1. 修改OrderController
    在这里插入图片描述
    在这里插入图片描述
    红色部分为新增项。

[4] 测试

http://localhost/consumer/payment/lb

结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


7 OpenFeign服务接口调用

7.1 概述

7.1.1 OpenFeign是什么

官网解释:
https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
在这里插入图片描述
总结

  1. Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可。
  2. GitHub的地址:https://github.com/spring-cloud/spring-cloud-openfeign。

7.1.2 能干嘛

在这里插入图片描述

7.1.3 Feign和OpenFeign两者区别

在这里插入图片描述

7.2 OpenFeign使用步骤

7.2.1 接口+注解

微服务调用接口+@FeignClient

7.2.2 新建cloud-consumer-feign-order80

从官网我们知道Feign在消费端使用(一般是这样)。
在这里插入图片描述

7.2.3 改pom

<dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般基础通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

7.2.4 YML

server:
  port: 80

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

7.2.5 主启动

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

使用注解 : @EnableFeignClients ----激活

7.2.6 业务类

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

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

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService
{
    @GetMapping(value = "/payment/get/{id}")
    CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}

服务提供者8001的controller中有一个@GetMapping(value = "/payment/get/{id}")这个相应的实现类(微服务提供者对外暴露的方法),是在这个CLOUD-PAYMENT-SERVICE微服务下,通过使用@FeignClient解析@GetMapping(value = "/payment/get/{id}")并绑定CLOUD-PAYMENT-SERVICE这个微服务下的暴露的接口,那么当消费者中的PaymentFeignService.getPaymentById被我们消费者通过url地址/consumer/payment/get/{id}访问的时候,会调用被@FeignClient绑定的微服务下的服务提供者对外暴露的接口,从而进行实现。
下图是官网实现原理,看懂既可明白为什么要进行。
在这里插入图片描述

@FeignClient :使用

[3] 控制层Controller

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

7.2.7 测试

  1. 先启动2个eureka集群7001/7002
  2. 再启动2个微服务8001/8002
  3. 启动OpenFeign启动
  4. http://localhost/consumer/payment/get/31
  5. Feign自带负载均衡配置项

7.2.8 小总结

在这里插入图片描述

7.3 OpenFeign超时控制

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

[1] 服务提供方8001故意写暂停程序

在这里插入图片描述
在服务提供方8001的controller中添加上面这一段代码。让线程暂停3秒钟。

[2] 服务消费方80添加超时方法PaymentFeignService

在这里插入图片描述

[3] 服务消费方80添加超时方法OrderFeignController

在这里插入图片描述

[4] 测试

  1. http://localhost/consumer/payment/feign/timeout
  2. 错误页面
    在这里插入图片描述

7.3.2 OpenFeign默认等待1秒钟,超过后报错

7.3.3 是什么

默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错。
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
yml文件中开启配置

[1] OpenFeign默认支持Ribbon

在这里插入图片描述

7.3.4 YML文件里需要开启OpenFeign客户端超时控制

在这里插入图片描述

7.4 OpenFeign日志打印功能

7.4.1 日志打印功能

7.4.2 是什么

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

7.4.3 日志级别

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

7.4.4 配置日志bean

@Configuration
public class FeignConfig
{
    @Bean
    Logger.Level feignLoggerLevel()
    {
        return Logger.Level.FULL;
    }
}

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

在这里插入图片描述

7.4.6 后台日志查看

在这里插入图片描述

8 Hystrix断路器

8.1 概述

8.1.1 分布式系统面临的问题

在这里插入图片描述
在这里插入图片描述

8.1.2 是什么

在这里插入图片描述

8.1.3 能干嘛

  1. 服务降级
  2. 服务熔断
  3. 接近实时的监控

8.1.4 官网资料

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

8.1.5 Hystrix官宣,停更进维

在这里插入图片描述
网址

  1. 被动修复bugs
  2. 不再接受合并请求
  3. 不再发布新版本

8.2 Hystrix重要概念

8.2.1 服务降级

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

8.2.2 服务熔断

  1. 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
  2. 就是保险丝 ---- 服务的降级->进而熔断->恢复调用链路

8.2.3 服务限流

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

8.4 hystrix案例

8.4.1 构建

[1] 新建cloud-provider-hystrix-payment8001

[2] 写pom

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

    <artifactId>cloud-provider-hystrix-payment8001</artifactId>

    <dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

[3] 写YML

server:
  port: 8001

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

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      defaultZone: http://eureka7001.com:7001/eureka

[4] 主启动

@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
public class PaymentHystrixMain8001
{
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

[5] 业务类

  1. service
@Service
public class PaymentService
{
    /**
     * 正常访问,一切OK
     * @param id
     * @return
     */
    public String paymentInfo_OK(Integer id)
    {
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O";
    }

    /**
     * 超时访问,演示降级
     * @param id
     * @return
     */
    public String paymentInfo_TimeOut(Integer id)
    {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O,耗费3秒";
    }
}

  1. controller
@RestController
@Slf4j
public class PaymentController
{
    @Autowired
    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) throws InterruptedException
    {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("****result: "+result);
        return result;
    }
}

[6] 正常测试

  1. 启动eureka7001
  2. 启动cloud-provider-hystrix-payment8001
  3. 访问
  • success的方法
    - http://localhost:8001/payment/hystrix/ok/31

  • 每次调用耗费5秒钟
    - http://localhost:8001/payment/hystrix/timeout/31

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

8.4.2 高并发测试

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

[2] Jmeter压测测试

  1. 开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务
    2.
    在这里插入图片描述

  2. 再来一个访问
    http://localhost:8001/payment/hystrix/ok/31

  3. 看演示结果

  • 两个都在自己转圈圈
  • 为什么会被卡死
    - tomcat的默认的工作线程数被打满 了,没有多余的线程来分解压力和处理。

[3] Jmeter压测结论

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

[4] 增加测试压力,80新建加入

1 新建模块 cloud-consumer-feign-hystrix-order80
2 改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mscloud03</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>


    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般基础通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


</project>
3 写YML
server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/
4 主启动
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}
5 业务类
  1. PaymentHystrixService
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService
{
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
  1. OrderHystirxController
@RestController
@Slf4j
public class OrderHystirxController
{
    @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;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
}
6 正常测试

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

7 高并发测试
  1. 2W个线程压8001
  2. 消费端80微服务再去访问正常的Ok微服务8001地址
  3. http://localhost/consumer/payment/hystrix/ok/32
  4. 消费者80,o(╥﹏╥)o
  • 要么转圈圈等待
  • 要么消费端报超时错误
    在这里插入图片描述

8.4.3 故障现象和导致原因

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

8.4.4 上诉结论

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

8.4.5 如何解决?解决的要求

[1] 超时导致服务器变慢(转圈)

  • 超时不再等待

[2] 出错(宕机或程序运行出错)

  • 出错要有兜底

[3] 解决

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

8.4.6 服务降级

[1] 降级配置 @HystrixCommand

[2] 8001先从自身找问题

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

[3] 8001fallback

1 业务类启用

在这里插入图片描述
@HystrixCommand报异常后如何处理

  • 一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的,fallbackMethod调用类中的指定方法.
  • 图示在这里插入图片描述
2 主启动类激活

添加新注解@EnableCircuitBreaker 开启断路器功能.

[4] 80fallback

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

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

3 YML

在这里插入图片描述

4 主启动

在这里插入图片描述
@EnableHystrix

5 业务类

在这里插入图片描述

[5] 目前存在的问题

每一个业务方法都有一个对应的兜底的方法,代码膨胀。我们要实现统一和自定义的分开。

[6] 解决问题

1 解决冗余,代码膨胀问题

使用@DefaultProperties(defaultFallback = "")这个注解的形式来设置默认的兜底方法。
@DefaultProperties(defaultFallback = "")

1:1 每个方法配置一个服务降级方法,技术上可以,实际上傻X

1:N 除了个别重要核心业务有专属,其它普通的可以通过@DefaultProperties(defaultFallback = “”) 统一跳转到统一处理结果页面

通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量。
在这里插入图片描述

controller配置
在这里插入图片描述

2 解决耦合度高的问题

我们发现前面虽然设置了默认方法,但是却是和业务逻辑混在一起的,所以我们需要进行解耦。

  1. 服务降级,客户端去调用服务端,碰上服务端宕机或关闭
  2. 本次案例服务降级处理是在客户端80实现完成的,与服务端8001没有关系。只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。
  3. 未来我们需要面对的异常:运行超时/宕机
  4. 再看我们的业务类PaymentController
    在这里插入图片描述
  5. 修改cloud-consumer-feign-hystrix-order80
  6. 根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理
  7. PaymentFallbackService类实现PaymentFeignClientService接口
    在这里插入图片描述
  8. YML
    在这里插入图片描述
  9. PaymentFeignClientService接口
    10.
  10. 测试
  • 单个eureka先启动7001
  • PaymentHystrixMain8001启动
  • 正常访问测试 http://localhost/consumer/payment/hystrix/ok/31
  • 故意关闭微服务8001 — 模拟宕机
  • 客户端自己调用提示
    * 此时服务端provider已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器

8.4.7 服务熔断

[1] 断路器是什么?

一句话就是家里的保险丝

[2] 熔断是什么

我们可以参考微服务理论的提出者的文章。https://martinfowler.com/bliki/CircuitBreaker.html

[3] 实操

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

//=========服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//开启断路器
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数超过峰值10次,熔断器将会关闭
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间范围
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//失败率达到多少后跳闸
        //在10s内的10次请求中,有超过6次失败就跳闸,开启断路器。之后一段时间请求成功率上去,再慢慢半开状态,最后关闭断路器。
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id)
{
    if(id < 0)
    {
        throw new RuntimeException("******id 不能负数");
    }
    String serialNumber = IdUtil.simpleUUID();

    return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
{
    return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   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] 测试

  1. 自测cloud-provider-hystrix-payment8001
  2. 正确 http://localhost:8001/payment/circuit/31
  3. 错误 http://localhost:8001/payment/circuit/-31
  4. 一次正确一次错误trytry
  5. 重点测试 多次错误,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行

[5] 原理(小总结)

1 大神结论

在这里插入图片描述

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

在这里插入图片描述

  1. 官网步骤
    在这里插入图片描述
  2. 断路器在什么情况下开始起作用
    在这里插入图片描述
  3. 断路器开启或者关闭的条件
  4. 断路器打开之后
    在这里插入图片描述
  5. 有兴趣的可以看看所有的配置有哪些
//========================All
@HystrixCommand(fallbackMethod = "str_fallbackMethod",
        groupKey = "strGroupCommand",
        commandKey = "strCommand",
        threadPoolKey = "strThreadPool",

        commandProperties = {
                // 设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离
                @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
                // 当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
                @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
                // 配置命令执行的超时时间
                @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
                // 是否启用超时时间
                @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                // 执行超时的时候是否中断
                @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
                // 执行被取消的时候是否中断
                @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
                // 允许回调方法执行的最大并发数
                @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
                // 服务降级是否启用,是否执行回调函数
                @HystrixProperty(name = "fallback.enabled", value = "true"),
                // 是否启用断路器
                @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
                // 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候,
                // 如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
                // 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过
                // circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50,
                // 就把断路器设置为 "打开" 状态,否则就设置为 "关闭" 状态。
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                // 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后,
                // 会将断路器置为 "半开" 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 "打开" 状态,
                // 如果成功就设置为 "关闭" 状态。
                @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
                // 断路器强制打开
                @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
                // 断路器强制关闭
                @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
                // 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
                @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
                // 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据
                // 设置的时间窗长度拆分成多个 "桶" 来累计各度量值,每个"桶"记录了一段时间内的采集指标。
                // 比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
                @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
                // 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
                @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
                // 该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
                @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
                // 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
                @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
                // 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数,
                // 就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行,
                // 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
                @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
                // 该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。
                @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
                // 是否开启请求缓存
                @HystrixProperty(name = "requestCache.enabled", value = "true"),
                // HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
                @HystrixProperty(name = "requestLog.enabled", value = "true"),
        },
        threadPoolProperties = {
                // 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
                @HystrixProperty(name = "coreSize", value = "10"),
                // 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,
                // 否则将使用 LinkedBlockingQueue 实现的队列。
                @HystrixProperty(name = "maxQueueSize", value = "-1"),
                // 该参数用来为队列设置拒绝阈值。 通过该参数, 即使队列没有达到最大值也能拒绝请求。
                // 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue
                // 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
                @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
        }
)
public String strConsumer() {
    return "hello 2020";
}
public String str_fallbackMethod()
{
    return "*****fall back str_fallbackMethod";
}

8.5 hystrix工作流程

8.5.1 网址

网址

8.5.2 Hystrix工作流程

  1. 官网步骤图
    在这里插入图片描述

  2. 步骤说明
    在这里插入图片描述

8.6 服务监控hystrixDashboard

8.6.1 概述

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

8.6.2 仪表盘9001

[1] 新建cloud-consumer-hystrix-dashboard9001

[2] POM

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

    <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>



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

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

[3] YML

 
server:
  port: 9001

[4] HystrixDashboardMain9001+新注解@EnableHystrixDashboard

在这里插入图片描述

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

在这里插入图片描述

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

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

8.6.3 断路器演示(服务监控hystrixDashboard)

[1] 修改cloud-provider-hystrix-payment8001

注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径
在这里插入图片描述
如果不这样设置,会报Unable to connect to Command Metric Stream. 404错误。

[2] 监控测试

1 启动1个eureka或者3个eureka集群均可
2 观察监控窗口
  1. 9001监控8001
    在这里插入图片描述
  • 填写监控地址
  • http://localhost:8001/hystrix.stream
  1. 测试地址
  • http://localhost:8001/payment/circuit/31
  • http://localhost:8001/payment/circuit/-31
  • 上述测试通过 ok
  • 先访问正确地址,再访问错误地址,再正确地址,会发现图示断路器都是慢慢放开的。
    - 监控结果,成功
    在这里插入图片描述
    - 监控结果,失败
    在这里插入图片描述
  1. 如何看
  • 7色
  • 1圈

在这里插入图片描述

  • 1线

曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。

  • 整图说明

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


9 Gateway新一代网关

9.1 三大核心概念

9.1.1 Route(路由)

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

9.1.2 Predicate(断言)

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

9.1.3 Filter(过滤)

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

9.1.4 总体

在这里插入图片描述

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

9.2 Gateway工作流程

9.2.1 官网总结

在这里插入图片描述

9.2.2 核心逻辑

路由转发+执行过滤器链

9.3 入门配置

9.3.1 新建Module cloud-gateway-gateway9527

9.3.2 改pom

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

    <artifactId>cloud-gateway-gateway9527</artifactId>

    <dependencies>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--一般基础配置类-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

9.3.3 写YML

server:
  port: 9527

spring:
  application:
    name: cloud-gateway

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

9.3.4 主启动类

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

9.3.5 9527网关如何做路由映射

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

9.3.6 YML新增网关配置

在这里插入图片描述

 
server:
  port: 9527

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

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

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka
 

9.3.7 测试

  1. 启动7001
  2. 启动8001 ----- cloud-provider-payment8001
  3. 启动9527网关
  4. 访问说明

在这里插入图片描述

  1. 添加网关前 http://localhost:8001/payment/get/31
  2. 添加网关后 http://localhost:9527/payment/get/31

9.3.8 YML配置说明

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

[1] 在配置文件yml中配置 (见前面的步骤)

[2] 代码中注入RouteLocator的Bean

  1. 官网案例
    在这里插入图片描述
  2. 百度国内新闻网址,需要外网 : http://news.baidu.com/guonei
  3. 自己写一个
  • 百度新闻
  • 业务需求

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

  1. 编码
  • cloud-gateway-gateway9527
  • 业务实现 config

@Configuration
public class GateWayConfig
{
    /**
     * 配置了一个id为route-name的路由规则,
     * 当访问地址 http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder)
    {
        RouteLocatorBuilder.Builder routes = builder.routes();

        routes.route("path_route_atguigu", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();

        return routes.build();

    }
    @Bean
    public RouteLocator customRouteLocator2(RouteLocatorBuilder builder)
    {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_atguigu2", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }
}
 

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

9.4.1

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

9.4.2 启动

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

9.4.3 POM

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

9.4.4 YML

在这里插入图片描述
注意老师这个笔记服务名称写的是小写,应该是大写。因为微服务名称是大写的。

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://CLOUD-PAYMENT-SERVICE       #匹配后提供服务的路由地址     
          #在CLOUD-PAYMENT-SERVICE 下面找,只要找到/payment/get/这个,为true,那么就访问。
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

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

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka
 
 

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

9.4.5 测试

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

在这里插入图片描述

9.5 Predicate的使用

9.5.1 是什么?

启动我们的getway9527,发现下面是这样的。
在这里插入图片描述

9.5.2 Route Predicate Factories

在这里插入图片描述
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。
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。

9.5.3 常用的Route Predicate

[1] After Route Predicate

在这里插入图片描述
After后面的时间串是如何来的?

package com.atguigu.test;


import java.time.ZoneId;
import java.time.ZonedDateTime;

/**
 * @auther zzyy
 * @create 2019-12-02 17:37
 */
public class ZonedDateTimeDemo
{
    public static void main(String[] args)
    {
        ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
        System.out.println(zbj);
//        ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
//        System.out.println(zny);
    }
}

yml文件修改:
在这里插入图片描述

[2] Before Route Predicate

在这里插入图片描述
在这里插入图片描述

[3] Between Route Predicate

Yml文件修改:
在这里插入图片描述

[4] Cookie Route Predicate

在这里插入图片描述
Cookie Route Predicate需要两个参数,一个是 Cookie name ,一个是正则表达式。
路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行.

  • 不带cookies访问

在这里插入图片描述

我们也可以通过curl命令行的方式进行地址访问,以前我们用的是postman图形化界面,curl就是命令行界面。

curl http://localhost:9588/paymentInfo

curl http://localhost:9527/payment/lb

  • 带上cookies访问
    在这里插入图片描述

curl http://localhost:9588/paymentInfo --cookie “username=zzyy” curl
http://localhost:9527/payment/lb --cookie “username=zzyy”

注意:加入curl返回中文乱码
解决办法:https://blog.csdn.net/leedee/article/details/82685636
YML
在这里插入图片描述

[5] Header Route Predicate

在这里插入图片描述

两个参数:一个是属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。
在这里插入图片描述
访问方式:
curl http://localhost:9588/paymentInfo -H “X-Request-Id:123”
在这里插入图片描述
curl http://localhost:9527/payment/lb -H “X-Request-Id:123”

[6] Host Route Predicate

在这里插入图片描述
Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。
它通过参数中的主机地址作为匹配规则。
YML
在这里插入图片描述
在这里插入图片描述

[7] Method Route Predicate

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

[8] Path Route Predicate

在这里插入图片描述
YML
在这里插入图片描述

[9] Query Route Predicate

在这里插入图片描述
支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。
YML
在这里插入图片描述

http://localhost:9527/payment/lb?username=31

http://localhost:9527/payment/lb?username=-31

[10] 小总结

所有的YML
在这里插入图片描述

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

9.6 Filter的使用

9.6.1 是什么

在这里插入图片描述

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

9.6.2 Spring Cloud Gateway的Filter

[1] 生命周期,Only Two

在这里插入图片描述

[2] 种类,Only Two

  • GatewayFilter
    网址:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-addrequestparameter-gatewayfilter-factory
    在这里插入图片描述
    有31种之多。
  • GlobalFilter
    -

9.6.3 常用的GatewayFilter

AddRequestParameter

  • YML
    在这里插入图片描述

9.6.4 自定义过滤器

自定义全局GlobalFilter

[1] 两个主要接口介绍

implements GlobalFilter,Ordered

[2] 能干嘛

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

[3] 案例代码

@Component //必须加,必须加,必须加
public class MyLogGateWayFilter implements GlobalFilter,Ordered
{
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
    {
        System.out.println("time:"+new Date()+"\t 执行了自定义的全局过滤器: "+"MyLogGateWayFilter"+"hello");

        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if (uname == null) {
            System.out.println("****用户名为null,无法登录");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

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

[4] 测试

  1. 启动
    在这里插入图片描述
  2. 正确 http://localhost:9527/payment/lb?uname=z3
  3. 错误
  • 没有参数uname
  • http://localhost:9527/payment/lb 无法正常使用转发

10 SpringCloud Config 分布式配置中心

10.1 概述

10.1.1 分布式系统面临的—配置问题

微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。

SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理…

10.1.2 是什么

在这里插入图片描述

10.1.3 能干嘛

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

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

  1. 官网

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

10.2 Config服务端配置与测试

10.2.1 新建一个名为springcloud-config的新Repository

  1. 本文章采用gitee来配置。因为github试了很多次都不行。
  2. 由上一步获得刚新建的git地址
    https://gitee.com/HXLDhxld/springcloud-config.git
  3. 本地硬盘目录上新建git仓库并clone
    4.
  4. 本地地址:E:\javacode\springcloud\springcloud-config
  5. git命令
    git clone https://gitee.com/HXLDhxld/springcloud-config.git
  6. 此时在本地E盘符下E:\javacode\springcloud\springcloud-config
    7.
  • 表示多个环境的配置文件
  • 保存格式必须为UTF-8
  • 如果需要修改,此处模拟运维人员操作git和github
    - git add .
    - git commit -m "init yml"
    - git push origin master

10.2.2 新建模块服务端 3344

[1] 新建Module模块cloud-config-center-3344

它即为Cloud的配置中心模块cloudConfig Center

[2] POM

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

    <artifactId>cloud-config-center-3344</artifactId>


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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


</project>

[3] YML

server:
  port: 3344

spring:
  application:
    name:  cloud-config-center #注册进Eureka服务器的微服务名
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/HXLDhxld/springcloud-config.git #Gitee上面的git仓库名字  换成gitee
#          uri: https://github.com/hxld/springcloud-config.git #GitHub上面的git仓库名字  实测连接超时,不行
          ####搜索目录
    search-paths:
            - springcloud-config
      ####读取分支
  label: master  #gitee是master分支
#  label: main  #github是main分支 实测连接超时


#服务注册到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka

[4] 主启动类

ConfigCenterMain3344

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

@EnableConfigServer

[5] windows下修改hosts文件,增加映射

127.0.0.1 config-3344.com

[6] 测试

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

  1. 启动微服务3344
  2. http://config-3344.com:3344/master/config-dev.yml

10.2.3 读取配置规则

官网:
在这里插入图片描述

[1] /{label}/{application}-{profile}.yml

  1. master分支
    http://config-3344.com:3344/master/config-dev.yml
    http://config-3344.com:3344/master/config-test.yml
    http://config-3344.com:3344/master/config-prod.yml
  2. dev分支
    http://config-3344.com:3344/dev/config-dev.yml
    http://config-3344.com:3344/dev/config-test.yml
    http://config-3344.com:3344/dev/config-prod.yml

[2] /{application}-{profile}.yml

http://config-3344.com:3344/config-dev.yml
http://config-3344.com:3344/config-test.yml
http://config-3344.com:3344/config-prod.yml
http://config-3344.com:3344/config-xxxx.yml(不存在的配置)

[3] /{application}/{profile}[/{label}]

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

[4] 重要配置细节总结

        /{name}-{profiles}.yml

/{label}-{name}-{profiles}.yml

label:分支(branch)
name :服务名
profiles:环境(dev/test/prod)

成功实现了用SpringCloud Config通过GitHub获取配置信息

10.3 Config客户端配置与测试

10.3.1 新建cloud-config-client-3355

10.3.2 POM

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

    <artifactId>cloud-config-client-3355</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

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


</project>

10.3.3 bootstrap.yml

[1] 是什么

在这里插入图片描述

[2] 内容

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    #Config客户端配置
    config:
      label: master #分支名称
      name: config #配置文件名称
      profile: dev #读取后缀名称   上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
      uri: http://localhost:3344 #配置中心地址k

#服务注册到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka

说明
在这里插入图片描述

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

10.3.5 主启动

类ConfigClientMain3355

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

10.3.6 业务类

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

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

10.3.7 测试

[1] 启动Config配置中心3344微服务并自测

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

[2] 启动3355作为Client准备访问

http://localhost:3355/configInfo

成功实现了客户端3355访问SpringCloud Config3344通过GitHub获取配置信息

10.3.8 问题

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

10.4 Config客户端之动态刷新

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

10.4.1 动态刷新

[1] 修改3355模块

POM引入actuator监控

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

[2] 修改YML,暴露监控端口

在这里插入图片描述

[3] @RefreshScope业务类Controller修改

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

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

[4] 此时修改github—> 3344 ---->3355

  1. http://localhost:3355/configInfo
  2. 3355改变没有??? ---- 没有改变

[5] 如何解决

  1. 需要运维人员发送Post请求刷新3355
  • 必须是POST请求
  • curl -X POST "http://localhost:3355/actuator/refresh"
    -
  1. 再次访问http://localhost:3355/configInfo 成功
  2. 成功实现了客户端3355刷新到最新配置内容,避免了服务重启

[6] 问题

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

后记

文章分为两部分。这个是上半部分。
springcloud续集

posted @ 2022-10-26 18:01  hxld  阅读(39)  评论(0编辑  收藏  举报