尚硅谷Springcloud学习笔记

在这里插入图片描述springCloud 和 springCloud Alibaba 目前是最主流的微服务框架组合。

版本选择:

选用 springboot 和 springCloud 版本有约束,不按照它的约束会有冲突。

工程建造

父工程

pom.xml

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

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

  <groupId>com.atguigu.springcloud</groupId>
  <artifactId>cloud2020</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>

  <modules>
    <module>8081</module>
      <module>cloud-comsumer-order80</module>
      <module>cloud-api-commons</module>
  </modules>

  <!-- 统一管理jar包版本 -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.complier.source>1.8</maven.complier.source>
    <maven.complier.target>1.8</maven.complier.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>


  <!-- dependencyManagement 子块继承之后,子module不用写 groupId 和 version -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-project-info-reports-plugin</artifactId>
        <version>3.0.0</version>
      </dependency>

      <!-- 下面三个基本是微服务架构的标配 -->
      <!--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>,import只能用在dependencyManagement块中,它将spring-boot-dependencies
        中dependencyManagement下的dependencies插入到当前工程的dependencyManagement中,所以不存在依赖传递。-->
        <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 阿里巴巴-->
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.1.0.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!--mysql-->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
        <scope>runtime</scope>
      </dependency>
      <!-- druid-->
      <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>
      <!--junit-->
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
      </dependency>
      <!--log4j-->
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

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

</project>

调配环境:
1、java版本
在这里插入图片描述2、注解生效激活
在这里插入图片描述

3、编码
在这里插入图片描述

第一个微服务架构

  1. 建模块 module
  2. 改 pom
  3. 写yml
  4. 主启动
  5. 业务类

提供者

这里面的 lombok 这个包,引入以后,实体类不用再写set 和 get
可以如下写实体类:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

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

cloud-provider-payment8001 子工程的pom文件:

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

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


    <dependencies>
     
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <!--不写就跟主父配置版本一样-->
            <version>1.1.16</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>

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:3306/jsp?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 6723237zh


mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities  # mybatis引用的实体类的共同前缀(所有Entity 别名类所在包)

主启动类

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

热部署配置

  1. 在具体子模块里添加
<dependency>    
    <groupId>org.springframework.boot</groupId>    
    <artifactId>spring-boot-devtools</artifactId>    
    <scope>runtime</scope>    
    <optional>true</optional>
</dependency>
  1. 添加plus到父工程的pom文件
 <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>
  1. 把这4个打勾
    在这里插入图片描述
  2. shift + ctrl + alt + / 四个按键一块按,选择1.Registry项:
    在这里插入图片描述
  3. 需要重启才能有效

消费者

  1. 消费者现在只模拟调用提供者的Controller方法,没有持久层配置,只有Controller和实体类
    当然也要配置主启动类和启动端口 80

  2. pom.xml

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

    <artifactId>cloud-comsumer-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.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.16</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. 然后把CommonResult 和 Payment 两个 实体类也复制过来

  2. 两个子模块之间,消费者调用提供者的服务需要通过 RestTemplate ,如下:
    ApplicationContextConfig 内容:

package com.dkf.springcloud.config;

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

@Configuration
public class ApplicationContextConfig {

    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
  1. 第4之后注入contrroller类才能跨子模块调用
@RestController
@Slf4j
public class OrderController {

	//远程调用的 地址
    public static final String PAYMENT_URL = "http://localhost:8001";

    @Resource
    private RestTemplate restTemplate;

    //消费者这里都用GetMapping 不用post
    @GetMapping("/comsumer/payment/create")
    public CommonResult<Payment> create(Payment payment){

        //提交用postForObject
        //param1 请求地址,param2 请求参数, param3 返回类型
        
        return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
    }

    @GetMapping("/comsumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){

        //获取用getForObject
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
    }
}

工程重构

上面 两个子项目,有多次重复的 导入 jar,和重复的 Entity 实体类。可以把 多余的部分,加入到一个独立的模块中,将这个模块打包,并提供给需要使用的 module

  1. 新建一个 cloud-api-commons 子模块
  2. 将 entities 包里面的实体类复制到这个子模块中,也将 pom 文件中,重复导入的 jar包放到这个新建的 模块的 pom 文件中。如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-api-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>
        <!--
             关于这个hutool 是个功能强大的工具包,官网:https://hutool.cn/
        -->
    </dependencies>

</project>
  1. 将此子模块clear,然后install, 自动就发布到maven仓库。

  2. 将 提供者 和 消费者 两个项目中的 entities 包删除。

  3. 将 打包到 maven 仓库的 cloud-api-commons 模块,引入到 提供者 和 消费者的 pom 文件中,如下所示

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

服务注册中心

如果是上面只有两个微服务,通过 RestTemplate ,是可以相互调用的,但是当微服务项目的数量增大,就需要服务注册中心。目前没有学习服务调用相关技术,使用 SpringCloud 自带的 RestTemplate 来实现RPC

Eureka

官方停更不停用,以后可能用的越来越少。

概念和理论

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

Server模块

server 模块使用 7001端口,下面是pom文件需要的依赖:

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

    </dependencies>

yml

server:
  port: 7001

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

启动类

//(exclude = DataSourceAutoConfiguration.class)启动时不启用 DataSource的自动配置检查
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//服务注册中心
@EnableEurekaServer
public class EurekaMain7001 {

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

提供者

这里的提供者,还是使用 上面的 cloud-provider-payment8001 模块,做如下修改:

  1. 在 pom 文件的基础上引入 eureka 的client包,pom 的全部依赖如下所示:
<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.16</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>
  1. 主启动类 加上注解 : @EnableEurekaClient
  2. yml 文件添加关于 Eureka 的配置:
eureka:
  client:
    # true 代表向服务注册中心注册自己
    register-with-eureka: true
    #  是否从EurekaServer抓取已有的注册信息,默认true,单节点无所谓,集群必须true并不需要去检索服务
    fetch-registry: true
    # 设置与 Eureka Server 交互的地址,查询服务 和 注册服务都依赖这个地址
    service-url:
      defaultZone: http://localhost:7001/eureka/

消费者

这里的消费者 也是上面 的 cloud-customer-order80 模块

  1. 修改 pom 文件,加入Eureka 的有关依赖, 全部 pom 依赖如下:
	 <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.16</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>

  1. 主启动类 加上注解 : @EnableEurekaClient
  2. yml 文件必须添加的内容:
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka/
spring:
  application:
    name: cloud-order-service

Eureka集群

在这里插入图片描述

Eureka 集群的原理,就是 相互注册,互相守望

进入windows下的路径 C:\Windows\System32\drivers\etc
修改hosts文件如下

# localhost name resolution is handled within DNS itself.
	127.0.0.1       localhost
	::1             localhost
	127.0.0.1       eureka7001.com
	127.0.0.1       eureka7002.com

修改没有权限的话,鼠标右键hosts文件->属性->安全,再如下图操作
在这里插入图片描述
现在创建 cloud-eureka-server7002 ,也就是第二个 Eureka 服务注册中心,pom 文件和 主启动类,与第一个Server一致。

现在修改这两个 Server 的 yml 配置:

7001 端口的Server yml文件:

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com  # eureka 服务器的实例地址

  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
    ## 一定要注意这里的地址,这是搭建集群的关键
    # 把7001注册进7002 ,相互注册,相互守望
      defaultZone: http://eureka7002.com:7002/eureka/

7002 端口的Server yml文件:

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com  # eureka 服务器的实例地址

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

eureka.instance.hostname 才是启动以后 本 Server 的注册地址,而 service-url 是 map 类型,只要保证 key:value 格式就行,它代表 本Server 指向了那些 其它Server 。利用这个,就可以实现Eureka Server 相互之间的注册,从而实现集群的搭建。

提供者集群

为提供者,即 cloud-provider-payment8001 模块创建集群,新建模块为 cloud-provider-payment8002,项目结构一致,pom文件依赖一样

8002 yml

server:
  port: 8002

#服务名和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/jsp?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 1234567

eureka:
  client:
    # true 代表向服务注册中心注册自己
    register-with-eureka: true
    #  是否从EurekaServer抓取已有的注册信息,默认true,单节点无所谓,集群必须true并不需要去检索服务
    fetch-registry: true
    # 设置与 Eureka Server 交互的地址,查询服务 和 注册服务都依赖这个地址
    service-url:
      #defaultZone: http://localhost:7001/eureka/
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities  # mybatis引用的实体类的共同前缀(所有Entity 别名类所在包)

8001,8002控制类加serverPort 提示访问到的具体端口

@RestController
//@Slf4j 日志输出log.info()
@Slf4j
public class PaymentController {
    @Autowired
    private PaymentService paymentService;

    //获取配置文件的端口号
    @Value("${server.port}")
    private String serverPort;

    @PostMapping("/payment/create")
    //如果不用@RequestBody,跨模块传入的数据会无效,插入数据库虽返回成功,但内容是null
    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,"插入失败,serverPort:"+serverPort,null);
        }
    }

    /**
     *  @PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形参中@PathVariable(“xxx“)
     * 不加@PathVariable("id") 前端传入的数值将接收不到,id默认为null
     * 且不可用@Param 替代
     */
    @GetMapping("/payment/get/{id}")
    public CommonResult 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,"没有对应记录,serverPort:"+serverPort,null);
        }
    }

}

修改消费者80 的配置类如下

@Configuration
public class ApplicationContextConfig {
    @Bean
    //底层是HttpClient 远程调用
    //负载均衡机制
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    
}

修改消费者80 的控制类

//public static final String PAYMENT_URL = "http://localhost:8001";//多个服务不能写死
    //写Eureka下的服务名,配置负载均衡机制后 它会自动分配给8001或8002
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

然后运行测试。

actuator信息配置

修改 在Eureka 注册中心显示的 主机名:
显示微服务所在 的主机地址:
在这里插入图片描述

服务发现Discovery

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

  1. 例子,在8001 主启动类上添加注解:@EnableDiscoveryClient
  2. 在 Controller 里面打印信息:
//通过注册发现 暴露给对方一些自己的服务信息
 @Resource
  private DiscoveryClient discoveryClient;
  
 @GetMapping(value = "/payment/discovery")
    public Object discovery(){
        List<String> services = discoveryClient.getServices();//服务列表 比如CLOUD-PAYMENT-SERVICE 等
        for (String element : services) {
           log.info("***element:"+element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");//某个服务的实例列表 比如8001等
        for (ServiceInstance instance : instances) {
            log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
        }
        return this.discoveryClient;

    }

访问后查看控制台
在这里插入图片描述

Eureka 自我保护机制

故障现象:
在这里插入图片描述
为什么有自我保护机制:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

禁止自我保护:

在 Eureka Server 7001的模块中的 yml 文件 eureka下 进行配置:

在这里插入图片描述

修改 Eureka Client 模块的 心跳间隔时间:
在这里插入图片描述

Zookeeper

临时性的服务节点,约定的心跳时间内无反应直接删除

springCloud 整合 zookeeper

在这里插入图片描述

提供者

创建一个提供者,和之前的一样即可,使用 8004端口

pom文件如下:

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

    <dependencies>
        <!--springcloud 整合 zookeeper 组件-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </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>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.dkf.cloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

主启动类:

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

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

Controller 打印信息:

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

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

如果 zookeeper 的版本和导入的jar包版本不一致,启动就会报错,由jar包冲突的问题。

解决这种冲突,需要在 pom 文件中,排除掉引起冲突的jar包,添加和服务器zookeeper版本一致的 jar 包,

但是新导入的 zookeeper jar包 又有 slf4j 冲突问题,于是再次排除引起冲突的jar包

<!--springcloud 整合 zookeeper 组件-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
    <!-- 排除与zookeeper版本不一致到导致 冲突的 jar包 -->
    <exclusions>
        <exclusion>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 添加对应版本的jar包 -->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
    <!-- 排除和 slf4j 冲突的 jar包 -->
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

yml文件:

server:
  port: 8004

spring:
  application:
    name: cloud-provider-service
  cloud:
    zookeeper:
      connect-string:192.168.50.03:2181

消费者

创建测试zookeeper作为服务注册中心的 消费者 模块 cloud-customerzk-order80

主启动类、pom文件、yml文件和 提供者 的类似

config类,注入 RestTemplate

@SpringBootConfiguration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getTemplate(){
        return new RestTemplate();
    }
}

controller层也是和之前类似:

@RestController
@Slf4j
public class CustomerZkController {

    public static final String INVOKE_URL="http://cloud-provider-service";

    @Resource
    private RestTemplate restTemplate;

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

关于 zookeeper 的集群搭建,目前使用较少,而且在 yml 文件中的配置也是类似,以列表形式写入 zookeeper 的多个地址即可,而且zookeeper 集群,在 hadoop的笔记中也有记录。总而言之,只要配合zookeeper集群,以及yml文件的配置就能完成集群搭建

Consul

consul也是服务注册中心的一个实现,是由go语言写的。官网地址: https://www.consul.io/intro

中文地址: https://www.springcloud.cc/spring-cloud-consul.html

功能:
在这里插入图片描述

安装并运行

下载地址:https://www.consul.io/downloads.html

打开下载的压缩包,只有一个exe文件,实际上是不用安装的,在exe文件所在目录打开dos窗口使用即可。

使用开发模式启动:consul agent -dev

访问8500端口,即可访问首页

提供者

新建提供者模块:cloud-providerconsul-service8006

pom 文件:

	<artifactId>cloud-providerconsul-service8006</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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.dkf.cloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

yml 文件:

server:
  port: 8006
spring:
  application:
    name: consul-provider-service
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:    # 指定注册对外暴露的服务名称
        service-name: ${spring.application.name}

主启动类:

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

controller也是简单的写一下就行。

消费者

新建 一个 在80端口的 消费者模块。pom和yml和提供者的类似,主启动类不用说,记得注入RestTemplate

controller层:

@RestController
public class CustomerConsulController {

    public static final String INVOKE_URL="http://consul-provider-service";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/customer/payment/consul")
    public String paymentInfo(){
        String result = restTemplate.getForObject(INVOKE_URL + "/payment/consul",String.class);
        return result;
    }
}

如果提示错,可以把RestTemplate写在启动类上
springcloud 出现A component required a bean of type ‘org.springframework.web.client.RestTemplate’ that could not be found.
在这里插入图片描述

对比总结

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

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

在这里插入图片描述

服务调用

都是使用在 client端,即有 ”消费者“ 需求的模块中。

Ribbon

我们这里提前启动好之前在搭建的 eureka Server 集群(5个模块)
在这里插入图片描述

简介

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

在这里插入图片描述在这里插入图片描述
上面在eureka时,确实实现了负载均衡机制,那是因为 eureka-client包里面自带着ribbon:

一句话,Ribbon 就是 负载均衡 + RestTemplate 调用。实际上不止eureka的jar包有,zookeeper的jar包,还有consul的jar包都包含了他,就是上面使用的服务调用。
在这里插入图片描述

如果自己添加,在 模块的 pom 文件中引入

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

对于RestTemplate 的一些说明:

有两种请求方式:post和get ,还有两种返回类型:object 和 Entity
在这里插入图片描述

如果使用 ForObject 得到的就是提供者返回的对象,而如果要使用 ForEntity 得到时 ResponstEntity对象,使用getBody()才能得到提供者返回的数据。

//使用forEnriry示例:
 @GetMapping("/comsumer/payment/getForEntity/{id}")
    public CommonResult<Payment> getForEntity(@PathVariable("id") Long id){

        //获取用getForObject
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
        if (entity.getStatusCode().is2xxSuccessful()){
            log.info(entity.getStatusCode()+"\t"+entity.getHeaders());
            return entity.getBody();
        }else{
            return new CommonResult<Payment>(444,"失败");

        }

负载均衡

Ribbon 自带7种负载均衡规则类型:
在这里插入图片描述7种规则的 类和接口关系:
在这里插入图片描述

在这里插入图片描述

注意上面说的,而Springboot主启动类上的 @SpringBootApplication 注解,包含了@ComponentScan注解,会自动扫描当前包及子包,所以注意不要放在SpringBoot主启动类的包内。

创建包:
在这里插入图片描述
在myrule这个包下新建 MySelfRule类:


@Configuration
public class MySelfRule {
    @Bean
    public IRule myrule(){
        return new RandomRule(); //负载均衡规则定义为随机
    }
}

然后在主启动类上添加如下注解 @RibbonClient:



import com.dkf.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
	   //指定该负载均衡规则对哪个提供者服务使用    加载自定义规则的配置类
@RibbonClient(name="CLOUD-PROVIDER-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {

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

轮询算法原理

在这里插入图片描述

OpenFeign

概述

在这里插入图片描述

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

使用

新建一个消费者募模块。feign自带负载均衡配置,所以不用手动配置

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

主启动类:

@SpringBootApplication
@EnableFeignClients   //关键注解
public class CustomerFeignMain80 {
    public static void main(String[] args) {
        SpringApplication.run(CustomerFeignMain80.class, args);
    }
}

新建一个service

@Component
@FeignClient(value = "CLOUD-PROVIDER-SERVICE")  //服务名称,要和eureka上面的一致才行
public interface PaymentFeignService {

    //这个就是provider 的controller层的方法定义。
    @GetMapping(value = "/payment/{id}")
    public CommonResult getPaymentById(@PathVariable("id")Long id);

}

Controller层:

//使用起来就相当于是普通的service。
@RestController
public class CustomerFeignController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("customer/feign/payment/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        return paymentFeignService.getPaymentById(id);
    }
}

Openfeign默认超时等待为一秒,在消费者里面配置超时时间
接口里的方法和路径与 8001等服务的controller层一致
在这里插入图片描述

超时控制

在这里插入图片描述

开启日志打印

首先写一个config配置类:
在这里插入图片描述

然后在yml文件中开启日志打印配置
在这里插入图片描述

中级部分

主要是服务降级、服务熔断、服务限流的开发思想和框架实现

Hystrix 断路器

官方地址:https://github.com/Netflix/Hystrix/wiki/How-To-Use

概述

在这里插入图片描述在这里插入图片描述
服务降级:

服务器忙碌或者网络拥堵时,不让客户端等待并立刻返回一个友好提示,fallback
在这里插入图片描述

服务熔断:

在这里插入图片描述

服务限流:

在这里插入图片描述

上面的技术不论是消费者还是提供者,根据真实环境都是可以加入配置的。

案例

首先构建一个eureka作为服务中心的单机版微服务架构 ,这里使用之前eureka Server 7001模块,作为服务中心

新建 提供者 cloud-provider-hystrix-payment8001 模块:

pom 文件:

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

Main:

@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {

        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }

Service:

@Service
public class PaymentService {

    //正常访问
    public String paymentInfo_OK(Integer id){
        return "线程池: "+ Thread.currentThread().getName()+"paymentInfo_OK,id "+id;
    }

   
    public String paymentInfo_TimeOut(Integer id){
        //int  n=10/0;//
        int time = 3;
        try {
            TimeUnit.SECONDS.sleep(time);//睡3秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池: "+ Thread.currentThread().getName()+"paymentInfo_TimeOut,id "+id+"  耗时(秒)"+time;
    }

 

controller层:

@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

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

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

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

}

模拟高并发

下载安装jmeter 压力测试器
下载安装操作博客:https://editor.csdn.net/md/?articleId=114655397
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
从测试可以看出,当模拟的超长请求被高并发以后,访问普通的小请求速率也会被拉低。

新建消费者 cloud-customer-feign-hystrix-order80 模块:以feign为服务调用,eureka为服务中心的模。

测试可见,当启动高并发测试时,消费者访问也会变得很慢,甚至出现超时报错。

解决思路:在这里插入图片描述

服务降级

一般服务降级放在客户端,即 消费者端 ,但是提供者端一样能使用。

首先提供者,即8001 先从自身找问题,设置自身调用超时的峰值,峰值内正常运行,超出峰值需要有兜底的方法处理,作服务降级fallback

首先 对 8001 的service进行配置(对容易超时的方法进行配置) :

//====服务降级
        //非正常访问
        //value = "3000" 规定3秒内走方法 String paymentInfo_TimeOut()
        //访问出错或 时间超3秒则执行 paymentInfo_TimeOutHandler()
        @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
        })
        public String paymentInfo_TimeOut(Integer id){
            //int  n=10/0;//
            int time = 3;
            try {
                TimeUnit.SECONDS.sleep(time);//睡3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "线程池: "+ Thread.currentThread().getName()+"paymentInfo_TimeOut,id "+id+"  耗时(秒)"+time;
        }

        public String paymentInfo_TimeOutHandler(Integer id){
            return "线程池: "+ Thread.currentThread().getName()+"失败,id "+id+"  哭了";
        }

主启动类添加注解: @EnableCircuitBreaker

然后对 80 进行服务降级:很明显 service 层是接口,所以我们对消费者,在它的 controller 层进行降级

	 @HystrixCommand(fallbackMethod = "paymentInfo_timeoutHandler", commandProperties = {
            //设置峰值,超过 3 秒,就会调用兜底方法
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    @GetMapping("/customer/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id){
        log.info("paymentInfo_timeout");
        return orderService.paymentInfo_Timeout(id);
    }

	//兜底方法,注意,兜底方法参数随意
    public String paymentInfo_timeoutHandler(@PathVariable("id")Integer id){
        log.info("paymentInfo_timeout--handler");
        return "访问 payment 失败----人工报错";
    }

主启动类添加注解: @EnableCircuitBreaker

完成测试! 注意,消费者降级设置的超时时间和提供者的没有任何关系,就算提供者峰值是 5 秒,而消费者峰值是 3秒,那么消费者依然报错。就是每个模块在服务降级上,都是独立的。

全局服务降级

上面的降级策略,很明显造成了代码的杂乱,提升了耦合度,而且按照这样,每个方法都需要配置一个兜底方法,很繁琐。现在将降级处理方法(兜底方法)做一个全局的配置,设置共有的兜底方法和独享的兜底方法。

问题-每个方法配置一个,解决:

@RestController
@Slf4j
//全局配置降级方法的注解
@DefaultProperties(defaultFallback = "paymentInfo_timeoutHandler")
public class OrderController {
    .....
    // 不写自己的 fallbackMethod 属性,就使用全局默认的
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    @GetMapping("/customer/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id")Integer id){
        ......
    }

    //兜底方法
    public String paymentInfo_timeoutHandler(){
        log.info("paymentInfo_timeout--handler");
        return "访问 payment 失败----人工报错";
    }
}

问题-跟业务逻辑混合,解决(解耦):

在这种方式一般是在客户端,即消费者端,首先上面再controller中添加的 @HystrixCommand 和 @DefaultProperties 两个注解去掉。就是保持原来的controller

  1. yml文件配置
server:
  port: 80
spring:
  application:
    name: cloud-customer-feign-hystrix-service
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

# 用于服务降级 在注解@FeignClient 中添加 fallback 属性值
feign:
  hystrix:
    enabled: true  # 在feign中开启 hystrix
  1. 修改service 接口:
@Component											// 这里是重点
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = OrderFallbackService.class)
public interface OrderService {

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

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

}
  1. fallback 指向的类:
package com.dkf.springcloud.service;

import org.springframework.stereotype.Component;

@Component						//注意这里,它实现了service接口
public class OrderFallbackService implements  OrderService{


    @Override
    public String paymentInfo_OK(Integer id) {
        return "OrderFallbackService --发生异常";
    }

    @Override
    public String paymentInfo_Timeout(Integer id) {
        return "OrderFallbackService --发生异常--paymentInfo_Timeout";
    }
}

服务熔断

实际上服务熔断 和 服务降级 没有任何关系

服务熔断,有自我恢复的功能

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

在这里插入图片描述配置参数的来源:
在这里插入图片描述
以 8001 项目为示例:

service层的方法设置服务熔断:

//====   服务熔断

    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "9"),//请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间10秒
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")//在10秒内请求9次失败率达到60% 跳闸
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        if (id<0){
            throw new RuntimeException("**** id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();//用了hutool工具包
        return Thread.currentThread().getName()+"调用成功,  流水号 : " +serialNumber;
    }

    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
        return "服务降级兜底函数 id不能负数 id: "+id;
    }

controller:

 //====   服务熔断

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

关于解耦以后的全局配置说明:

例如上面提到的全局服务降级,并且是feign+hystrix整合,即 service 实现类的方式,如何做全局配置?

上面有 做全局配置时,设置超时时间的方式,我们可以从中获得灵感,即在yml文件中 进行熔断配置:

hystrix:
command:
 default:
   circuitBreaker:
     enabled: true
     requestVolumeThreshold: 10
     sleepWindowInMilliseconds: 10000
     errorThresholdPercentage: 60

Hystrix DashBoard

在这里插入图片描述新建模块 cloud-hystrix-dashboard9001 :

pom 文件:

	<dependencies>
        <!-- hystrix Dashboard-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <!-- 常规 jar 包 -->
        <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>

yml文件只需要配置端口号,主启动类加上这样注解:@EnableHystrixDashboard

启动测试:访问 http://ocalhost:9001/hystrix

监控实战

下面使用上面 9001 Hystrix Dashboard 项目,来监控 8001 项目 Hystrix 的实时情况:

修改Main8001

SpringBootApplication
@EnableEurekaClient
//激活Hystrix使用
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {

        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }


    /**
     * 此配置是为了服务监控而配置,与服务容错无关,springvloud升级后的坑
     * ServletRegistrationBean 因为springboot的默认路径不是 "/hystrix.stream"
     * 只要在自己的项目里配置下面的servlet就可以了
     */
    @Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(streamServlet);
        servletRegistrationBean.setLoadOnStartup(1);
        servletRegistrationBean.addUrlMappings("/hystrix.stream");
        servletRegistrationBean.setName("HystrixMetricsStreamServlet");
        return servletRegistrationBean;
    }
}

在这里插入图片描述
在这里插入图片描述# 服务网关

Gateway

内容过多,开发可参考 https://docs.spring.io/ 官网文档

简介

在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述微服务架构图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

在这里插入图片描述Gateway 基于WebFlux
在这里插入图片描述Gateway 三大核心概念:
在这里插入图片描述在这里插入图片描述总结:
在这里插入图片描述### 入门配置

新建模块 cloud-gateway-gateway9527

现在实现,通过Gateway (网关) 来访问其它项目,这里选择之前8001项目,要求注册进Eureka Server 。其它没要求。

pom文件:

<dependencies>
        <!--gateway-->
        <!-- gateway和web不能同时存在,即web相关jar包不能导入 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client gateWay作为网关,也要注册进服务中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- gateway和web不能同时存在,即web相关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>
        <dependency>
            <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

    </dependencies>

yml文件:

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

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

# 注册进 eureka Server
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/
    register-with-eureka: true
    fetch-registry: true

主启动类

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

访问测试:1 启动eureka Server,2 启动 8001 项目,3 启动9527(Gateway项目)

可见,当我们访问 http://localhost:9527/payment/get/1 时,即访问网关地址时,会给我们转发到 8001 项目的请求地址,以此作出响应。

加入网关前:http://localhost:8001/payment/get/31

加入网关后:http://localhost:9527/payment/get/31

在这里插入图片描述
上面是以 yml 文件配置的路由,也有使用config类配置的方式:

@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator customRouteLocator (RouteLocatorBuilder routeLocatorBuilder){

        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        //访问localhost:9527/guonei,会转发到 http://news.baidu.com/guonei
        routes.route("path-route",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }

}

动态配置

这里所谓的动态配置就是利用服务注册中心,来实现 负载均衡 的调用 多个微服务。

注意,这是GateWay 的负载均衡

对yml进行配置:

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

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

# uri: lb://CLOUD-PROVIDER-SERVICE  解释:lb 属于GateWay 的关键字,代表是动态uri,即代表使用的是服务注册中心的微服务名,它默认开启使用负载均衡机制 

下面可以开启 8002 模块,并将它与8001同微服务名,注册到 Eureka Server 进行测试。

Predicate

注意到上面yml配置中,有个predicates 属性值。

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

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

在这里插入图片描述

Filter

主要是配置全局自定义过滤器

自定义全局过滤器配置类:

@Component
public class GateWayFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
       @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("******* come in MyLogGatewayFilter: " + new Date());
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if (uname == null){
            log.info("****** 非法用户");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

    //返回值是加载顺序,一般全局的都是第一位加载
    @Override
    public int getOrder() {
        return 0;
    }
}

服务配置

概述

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述### 服务端配置

首先在github上新建一个仓库 springcloud-config

然后使用git命令克隆到本地,命令:git clone https://github.com/LZXYF/springcloud-config

注意上面的操作不是必须的,只要github上有就可以,克隆到本地只是修改文件。

新建 cloud-config-center3344 模块:

pom文件:

	<dependencies>
        <!-- config Server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

        <!--eureka-client config Server也要注册进服务中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
       <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
          
    </dependencies>

yml 配置:

server:
  port: 3344
spring:
  application:
    name: cloud-config-center  # 注册进eureka Server 的微服务名
  cloud:
    config:
      server:
        git:
          uri: https://github.com/LZXYF/springcloud-config # github 仓库位置
          ## 搜索目录
          search-paths:
            - springcloud-config
          # 读取的分支
          label: master
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

主启动类:

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

添加模拟映射:【C:\Windows\System32\drivers\etc\hosts】文件中添加: 127.0.0.1 config-3344.com

启动微服务3344,访问http://config-3344.com:3344/master/config-dev.yml 文件(注意,要提前在git上弄一个这文件)

文件命名和访问的规则:

在这里插入图片描述

客户端配置

这里的客户端指的是,使用 Config Server 统一配置文件的项目。既有之前说的消费者,又有提供者

新建 cloud-config-client-3355 模块:

pom文件:

	<dependencies>
        <!-- config Client 和 服务端的依赖不一样 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <!--eureka-client config Server也要注册进服务中心-->
        <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>
       <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
           
    </dependencies>

bootstrap.xml
在这里插入图片描述在这里插入图片描述
内容:

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    # config 客户端配置
    config:
      label: master # 分支名称
      name: config # 配置文件名称,文件也可以是client-config-dev.yml这种格式的,这里就写 client-config 
      profile: dev # 使用配置环境
      uri: http://config-3344.com:3344  # config Server 地址
      # 综合上面四个 即读取配置文件地址为: http://config-3344.com:3344/master/config-dev.yml

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

主启动:

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

controller层,测试读取配置信息

package com.dkf.springcloud.controller;

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

@RestController
public class ConfigClientController {

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

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

启动测试完成!如果报错,注意github上的 yml 格式有没有写错!

动态刷新

问题:
在这里插入图片描述

github上面配置更新了,config Server 项目上是动态更新的,但是,client端的项目中的配置,目前还是之前的,它不能动态更新,必须重启才行。

解决:

  1. client端一定要有如下依赖:
    在这里插入图片描述

  2. client 端增加 yml 配置如下,即在 bootstrap.yml 文件中:

# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: "*"
  1. 在controller 上添加如下注解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ediUUnJ-1615816878116)(images\1597650236798.png)]

到此为止,配置已经完成,但是测试仍然不能动态刷新,需要下一步。

  1. 一般运维工程师 向 client 端发送一个 POST 请求

如 curl -X POST “http://localhost:3355/actuator/refresh”

两个必须:1.必须是 POST 请求,2.请求地址:http://localhost:3355/actuator/refresh

成功!

但是又有一个问题,就是要向每个微服务发送一次POST请求,当微服务数量庞大,又是一个新的问题。

就有下面的消息总线!

消息总线

Bus

在这里插入图片描述

安装RabbitMQ

在windows 上安装RabbitMQ

  1. 安装RabbitMQ的依赖环境 Erlang 下载地址: http://erlang.org/download/otp_win64_21.3.exe
  2. 安装RabbitMQ 下载地址: http://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
  3. 进入 rabbitMQ安装目录的sbin目录下,打开cmd窗口,执行 【rabbitmq-plugins enable rabbitmq_management】
  4. 访问【http://localhost:15672/】,输入密码和账号:默认都为 guest

广播式刷新配置

还是按照之前的 3344(config Server)和 3355(config client)两个项目来增进。

首先给 config Server 和 config client 都添加如下依赖:

	<!-- 添加rabbitMQ的消息总线支持包 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>

config Server 的yml文件增加如下配置:

# rabbitMq的相关配置
rabbitmq:
  host: localhost
  port: 5672  # 这里没错,虽然rabbitMQ网页是 15672
  username: guest
  password: guest
# rabbitmq 的相关配置2 暴露bus刷新配置的端点
management:
  endpoints:
    web:
      exposure:
        include: 'bus-refresh'

config Client 的yml文件修改成如下配置:(注意对齐方式,和config Server不一样)

spring:
  application:
    name: config-client
  cloud:
    # config 客户端配置
    config:
      label: master         # 分支名称
      name: client-config       # 配置文件名称
      profile: test      # 使用配置环境
      uri: http://config-3344.com:3344  # config Server 地址
  # 综合上面四个 即读取配置文件地址为: http://config-3344.com:3344/master/config-dev.yml
  # rabbitMq的相关配置
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

可在github上修改yml文件进行测试,修改完文件,向 config server 发送 请求:

【curl -X POST “http://localhost:3344/actuator/bus-refresh”】

注意,之前是向config client 一个个发送请求,但是这次是向 config Server 发送请求,而所有的config client 的配置也都全部更新。

定点通知

在这里插入图片描述

持续更新中。。。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值