尚硅谷 周阳老师 SpringCloud 学习笔记

十二、服务配置 Spring Cloud Config 配置中心

1. 概述
1.1 分布式系统面临的配置问题

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

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

1.2 是什么

SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置

1.3 怎么玩

SpringCloud Config分为服务端和客户端两部分。服务端也称为分布式配置中心,他是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密/信息等访问接口。

客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息配置服务器,默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。

1.4 能干嘛

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

1.5 与GitHub整合配置

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

2. Config服务端配置与测试

用自己的账号在GitHub上新建一个名为springcloud-config的新Repository。
由上一步获得刚新建的git地址:xxxxx
本地硬盘目录上新建git创库并clone: git clone xxxx
此时,在之前本地创建的目录SpringCloud2020\springcloud-config下,有多个表示多个环境的配置文件,保存格式必须为UTF-8。如果需要修改,此处模拟运维人员操作git和gitee

git add .
git commit -m "init yml"
git push origin master
2.1 新建Module模块cloud-config-center-3344

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

2.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>cloud2020</artifactId>
        <groupId>org.example</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>

        <!--eureka-client-->
        <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>
        </dependency>
    </dependencies>

</project>
2.3 application.yml
server:
  port: 3344

spring:
  application:
    name: cloud-config-center  #注册进Eureka服务器的微服务名
  cloud:
    config:
      server:
        git:
          uri: xxxxxxxxxxxxxxxx   #gitee上的仓库名  配置服务器为各个不同的微服务应用的所有环境提供了一个中心化的外部配置
          #若仓库是私有的,需要配置用户名和密码
          username: xxx
          password: xxx
          # 搜索目录
          search-paths:
            - springcloud-config
      # 读取分支
      label: master

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

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

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

2.5 windows下修改hosts文件,增加映射

在这里插入图片描述

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

1.启动微服务 3344
2.http://config-3344.com:3344/master/config-dev.yml
在这里插入图片描述

在这里插入图片描述

2.7 匹配值读取规则
2.7.1 /{label}/{application}-{profile}.yml

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
在这里插入图片描述

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.7.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 (不存在的配置)
在这里插入图片描述

2.7.3 /{application}/{profile}[/{lable}]

http://config-3344.com:3344/config-dev.yml/master
http://config-3344.com:3344/config-test.yml/master
http://config-3344.com:3344/config-prod.yml/master
在这里插入图片描述

2.7.4 配置细节总结

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

3.Config客户端配置与测试
3.1 新建cloud-config-client-3355
3.1.1 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>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-config-client-3355</artifactId>
    <dependencies>
        <!--config 客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <!--eureka-client-->
        <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>
        </dependency>
    </dependencies>


</project>

config 客户端:

<!--config 客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

config 服务端:

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
3.1.2 bootstrap.yml

bootstrap.yml是什么:
application.yml是用户级的资源配置项,bootstrap.yml是系统级的,优先级更高。

SpringCloud会创建一个"BootStrap Context",作为Spring应用的"Application Context"的父上下文。初始化的时候,“BootStrap Context” 负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的 “Environment”。

"BootStrap Context"属性有高优先级,默认情况下,它们不会被本地配置覆盖。“BootStrap Context” 和 "Application Context"有着不同的约定,所以增加了一个 ‘bootstrap.yml’ 文件,保证 ‘Bootstrap Context’ 和 ‘Application Context’ 配置的分离。

要将Client模块下的application.yml文件改为bootstrap.yml,这是很关键的,因为bootstrap.yml是比application.yml先加载。bootstrap.yml 优先级高于 application.yml。

bootstrap.yml 内容:

server:
  port: 3355

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

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

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

3.1.3 主启动类 ConfigClientMain3355
package com.atguigu.springcloud;

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

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

3.1.4 业务类
package com.atguigu.springcloud.controller;

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

@RestController
public class ConfigClientController {

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

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


在这里插入图片描述

3.1.5测试

启动Config配置中心3344微服务并自测
启动3355作为Client准备访问

在这里插入图片描述
直接访问配置中心:
在这里插入图片描述
访问client客户端:http://localhost:3355/configInfo
在这里插入图片描述
成功实现了客户端3355访问Spring Cloud Config3344通关gitee获取配置信息。
问题随之而来,分布式配置动态刷新问题:
Linux运维修改gitee上的配置文件内容
刷新3344,发现ConfigServer配置中心立即响应,立即变化
刷新3355,发现ConfigClient客户端没有响应,没有变化
3355没有变化,除非自己重启或者重新加载
难道每次运维修改配置文件,客户端都需要重启吗?不可能!

4.Config客户端之动态刷新

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

4.1 动态刷新 步骤 修改3355模块
4.1.1 POM引入actuator监控

需要引入actuator依赖(之前是引入了的)

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
4.1.2 修改YML,暴露监控端口
server:
  port: 3355

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

eureka:
  client:
    register-with-eureka: true  #表示将自己注册进Eureka Server
    fetch-registry: true        #表示是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka   #集群版
      
management:                     #  暴露监控端点
  endpoints:
    web:
      exposure:
        include: "*"

4.1.3 业务类Controller修改

在Controller类上添加@RefreshScope注解,实现刷新功能

package com.atguigu.springcloud.controller;

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

@RestController
@RefreshScope
public class ConfigClientController {

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

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

4.1.4 测试

修改gitee上配置文件内容
在这里插入图片描述
再次访问3344 http://config-3344.com:3344/config-dev.yml
在这里插入图片描述
配置中心获取的配置文件仍然随着变化

访问Config 客户端 http://localhost:3355/configInfo
在这里插入图片描述
3355还是没有随着gitee配置文件变化而变化

4.1.5 如何解决

需要运维人员发送post请求刷新3355:
1.必须是POST请求
2.curl -X POST “http://localhost:3355/actuator/refresh” (X 大写)

在这里插入图片描述
在这里插入图片描述
实现了客户端3355刷新到最新的配置内容,避免了服务重启。

4.1.6 还有什么问题?

假设有多个微服务客户端3355、3366、3377 …
每个微服务都要去手动执行一次post请求手动刷新吗?
可否广播,一次通知,处处生效?
下一章,消息总线 SpringCloud Bus

十三、消息总线 SpringCloud Bus

1.概述

对上一讲的加深和扩充,实现分布式自动刷新配置功能。SpringCloud Bus配合SpringCloud Config使用可以实现配置的动态刷新。

是什么?
Bus支持两种消息代理:RabbitMQ 和 Kafka
SpringCloud Bus是用来将分布式系统节点与轻量级消息系统链接起来的框架,它整合了Java的事件处理机制和消息中间件的功能。SpringCloud Bus目前支持RabbitMQ 和 Kafka

能干嘛?
SpringCloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以作为微服务间的通信通道。

为什么被称为总线?
什么是总线:在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为总线。在总线上的各个实例,都可以方便的广播一些需要让其他连接在该主题上的实例都知道的消息。

基本原理:ConfigClient实例都监听MQ中同一个topic(默认是springCloudBus)。当一个服务刷新数据的时候,他会把这个消息放入topic,这样其他监听同一个Topic的服务就能得到通知,然后去更新自身的配置。

2、RabbitMQ环境配置

安装Erlang,下载地址:链接:https://pan.baidu.com/s/1uzYQJ3q-AYKimUXAHA8log
提取码:yyds
下载并安装。

进入RabbbitMQ安装目录的sbin目录下:
在这里插入图片描述
输入以下命令,启动管理功能:

 rabbitmq-plugins enable rabbitmq_management

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

通过上图的RabbitMQ Service - start 启动RabbitMQ,访问地址查看是否安装成功:http://localhost:15672/

在这里插入图片描述
访问成功:
在这里插入图片描述
输入账号密码:guest/guest登录
在这里插入图片描述

3.SpringCloud Bus动态刷新全局广播

必须先具备良好的RabbitMQ环境,再添加一个3366 config client

3.1 新建config client 3366
3.1.1 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>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

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

        <!--config 客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <!--eureka-client-->
        <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>
        </dependency>
    </dependencies>

</project>
3.1.2 bootstrap.yml
server:
  port: 3366

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

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

management:                     #  暴露监控端点
  endpoints:
    web:
      exposure:
        include: "*"

3.1.3 主启动类
package com.atguigu.springcloud;

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

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

3.1.4 controller

和3355基本一样

package com.atguigu.springcloud.controller;

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

@RestController
@RefreshScope
public class ConfigClientController {

    @Value("${config.info}")
    private String configInfo;
    
    @Value("${server.port}")
    private String serverPort;
    
    @GetMapping("/configInfo")
    public String getConfigInfo() {
        return "serverPort: "+serverPort+"\t\n\n configInfo: "+configInfo;
    }
}
3.1.5 设计思想

1)利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
图1:
在这里插入图片描述

2)利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置
图2:
在这里插入图片描述

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

3.1.6 给cloud-config-center-3344配置中心服务端添加消息总线支持

服务端 3344 pom.xml 添加消息总线RabbitMQ支持

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

3344 application.yml

server:
  port: 3344

spring:
  application:
    name: cloud-config-center  #注册进Eureka服务器的微服务名
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/xxxxxxxxxx   #gitee上的仓库名  配置服务器为各个不同的微服务应用的所有环境提供了一个中心化的外部配置
          # 搜索目录
          search-paths:
            - springcloud-config
          username: 13888888888
          password: 66666666666
      # 读取分支
      label: master
  rabbitmq:   #rabbitMQ相关配置
    host: localhost
    port: 5672
    username: guest
    password: guest

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

## rabbitmq相关配置,暴露bus刷新配置的端点
management:
  endpoints:
    web:
      exposure:
        include: 'bus-refresh'

3.1.7 给cloud-config-client-3355 客户端添加消息总线支持

客户端 3355 pom.xml 添加消息总线RabbitMQ支持

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

客户端 3355 bootstrap.yml 配置

server:
  port: 3355

spring:
  application:
    name: config-client
  cloud:
    config:      #Config客户端配置
      label: master       #分支名称
      name: config        #配置文件名称
      profile: dev        #读取后缀名称  上述综合:master分支上config-dev.yml的配置文件被读取 http://config-3344.com:3344/master/config-dev.yml
      uri: http://localhost:3344   #配置中心地址
  #  rabbitMQ相关配置 15672是web管理界面的端口 5672是MQ访问的端口
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

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

management:                     #  暴露监控端点
  endpoints:
    web:
      exposure:
        include: "*"
3.1.8 给cloud-config-client-3366 客户端添加消息总线支持

省略,与3.1.7添加配置相同

3.1.9 测试

启动RabbitMQ、Eureka7001、7002,配置中心3344,配置客户端3355、3366
在这里插入图片描述

运维工程师:
1.修改Gitee上配置文件
2.发送POST请求:

curl -X POST "http://localhost:3344/actuator/bus-refresh"

一次发送,处处生效

配置中心:

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

config 客户端:

http://localhost:3355/configInfo
http://localhost:3366/configInfo

修改gitee上配置文件配置,分别刷新3344配置中心,3355、3366 客户端,此时只有3344配置中心的配置随着修改gitee上配置变化。

执行post请求:
在这里插入图片描述
再次刷新 3355、3366 客户端 时,配置已与gitee上配置一致,达到了一次修改,广播通知,处处生效的效果

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

4. SpringCloud Bus 动态刷新定点通知

不想通知全部客户端,只想定点通知。
比如:只通知3355,不通知3366
简单一句话,指定某具体的实例生效而不是全部。
公式:

http://localhost:3344(配置中心的端口)/actuator/bus-refresh/{destination}

/bus/refresh请求不再发送到具体的服务实例上,而是发给config server,并通过destination参数指定需要配置更新的服务或实例。

案例:我们这里以刷新运行在3355端口的config-client为例,只通知3355,不通知3366。
修改gitee上配置文件配置,执行post请求:

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

destination就是微服务名加端口号
在这里插入图片描述
此时分别刷新client3355、3366,只有3355的配置跟随gitee上配置变化了。

通知总结:
在这里插入图片描述

十四、SpringCloud Stream消息驱动

1. 是什么

一句话:屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。
什么是SpringCloudStream:官方定义SpringCloud Stream是一个构建消息驱动微服务的框架。应用程序通过 inputs 或者 outputs 来与Spring Cloud Stream中binder对象交互。通过我们配置来binding(绑定),而SpringCloud Stream中的binder对象负责与消息中间件交互,所以,我们只需要搞清楚如何与SpringCloud Stream交互就可以方便的使用消息驱动的方式。
通过使用Spring Integration 来连接消息代理中间件以实现消息事件驱动。SpringCloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念。
目前仅支持RabbitMQ、Kafka。

中文指导手册:https://m.wang1314.com/doc/webapp/topic/20971999.html

2. SpringCloud Stream的设计思想
2.1 标准MQ

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

2.2 为什么用Cloud Stream

比方说我们用到了RabbitMQ和Kafka,由于这两个消息中间件的架构上的不同,像RabbitMQ有exchange,kafka有Topic和Partitions分区,这些中间件的差异性给我们实际项目开发给我们造成了一定的困扰,我们如果用了两个消息队列的其中一种,后面的业务需求,我们想往另外一种消息队列进行迁移,这时候无疑就是一个灾难性的,一大堆东西都需要重新推倒重新做,因为它跟我们的系统耦合了,这时候springcloud stream给我们提供了一种解耦的方式。

2.2.1 stream凭什么可以统一底层差异

在没有绑定器这个概念的情况下,我们的springboot应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,他们的实现细节上会有较大的差异性,通过定义绑定器Binder作为中间层,完美地实现了应用程序与消息中间件细节之间的隔离。通过向应用程序暴露统一的Channel通道,使得应用程序不需要再考虑各种不同的消息中间件实现。

2.2.2 Binder

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

SpringCloudStream处理架构:
在这里插入图片描述

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

Topic主题进行广播,在RabbitMQ就是Exchange,在Kafka中就是Topic

3.SpringCloud Stream标准流程套路

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

4. 编码API和常用注解

在这里插入图片描述

组成说明
Middleware中间件,目前只支持RabbitMQ和Kafka
BinderBinder是应用与中间件之间的封装,目前实现了Kafka和RabbitMQ的Binder。通过Binder可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMQ的exchange),这些都可以通过配置文件来实现
@Input注解标识输入通道,通过该输入通道接收到的消息进入应用程序
@Output注解标识输出通道,发布的消息将通过该通道离开应用程序
@StreamListener监听队列,用于消费者对队列的消息接收
@EnableBinding指信道channel和exchange绑定在一起
5. 案例说明

需要RabbitMQ环境以及OK,工程中新建三个子模块:
1.cloud-stream-rabbitmq-provider8801,作为生产者进行发消息
2.cloud-stream-rabbitmq-consumer8802,消息接收模块
3.cloud-stream-rabbitmq-consumer8803,消息接收模块

6. 消息驱动之生产者

新建Module cloud-stream-rabbitmq-provider8801

6.1 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>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-stream-rabbitmq-provider8801</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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
        <!--基础配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

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

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

</project>
6.2 application.yml
server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders:   #在此处配置要绑定的RabbitMQ的服务信息
        defaultRabbit: # 表示定义的名称,用于binding整合
          type: rabbit # 消息组件类型
          environment: #设置rabbitMQ的相关的环境配置
            spring: 
              rabbitmq: 
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # 服务的整合处理
        output: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为json,文本则设置为:text/plain
          binder: defaultRabbit  #  设置要绑定的消息服务的具体类型
          
eureka:
  client:
    register-with-eureka: true  #表示将自己注册进Eureka Server
    fetch-registry: true        #表示是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka   #集群版
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30s)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5s的间隔 (默认是90s)
    instance-id: send-8801.com # 在信息列表时显示主机名称
    prefer-ip-address: true    # 访问的路径变为ip地址
6.3 主启动类StreamMQMain8801
package com.atguigu.springcloud;

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

@SpringBootApplication
public class StreamMQMain8801 {
    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8801.class, args);
    }
}
6.4 业务类
6.4.1 发送消息的接口和实现类

接口:

package com.atguigu.springcloud.service;

public interface IMessageProvider {
    String send();
}

实现类:

package com.atguigu.springcloud.service.impl;

import com.atguigu.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;

import javax.annotation.Resource;
import java.util.UUID;

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

    @Override
    public String send() {

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

6.4.2 Controller
package com.atguigu.springcloud.controller;


import com.atguigu.springcloud.service.IMessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class SendMessageController {
    
    @Resource
    private IMessageProvider messageProvider;
    
    @GetMapping(value = "/sendMessage")
    public String sendMessage() {
        return messageProvider.send();
    }
}

6.4.3 测试

启动Eureka7001、7002,启动rabbitMQ,启动8801,然后访问:/sendMessage

服务已经注册到Eureka中:
在这里插入图片描述
rabbitMQ:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
控制台:
在这里插入图片描述
测试成功!

7. 消息驱动之消费者

新建Module cloud-stream-rabbitmq-consumer8802

7.1 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>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-stream-rabbitmq-consumer8802</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.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>
        <!--基础配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

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

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

</project>
7.2 application.yml
server:
  port: 8802

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

eureka:
  client:
    register-with-eureka: true  #表示将自己注册进Eureka Server
    fetch-registry: true        #表示是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka   #集群版
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30s)
    lease-expiration-duration-in-seconds: 5 # 如果现在超过了5s的间隔 (默认是90s)
    instance-id: receive-8802.com # 在信息列表时显示主机名称
    prefer-ip-address: true    # 访问的路径变为ip地址
7.3 主启动类StreamMQMain8802
package com.atguigu.springcloud;

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

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

7.4 业务类
package com.atguigu.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

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

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

7.5 测试

启动Eureka7001、7002,RabbitMQ,消息生产者8801,消息消费者8820
Eureka服务注册信息:
在这里插入图片描述
访问http://localhost:8801/sendMessage 发送消息
8801消息生产者控制台信息:
在这里插入图片描述
8802消费者控制台信息:
在这里插入图片描述

8. 分组消费与持久化

依照8802,再创建一个消息消费者8803 cloud-stream-rabbitmq-consumer8803

启动:
RabbitMQ
Eureka 7001、7002 服务注册
8801 消息生产
8802 消息消费
8803 消息消费
在这里插入图片描述

8.1 运行后有两个问题

1.重复消费问题
2.消息持久化问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
目前是8802/8803同时都收到了消息,存在重复消费的问题

如何解决:分组和持久化属性group
生产实际案例
比如在如下场景中,订单系统我们做集群部署,都会从RabbitMQ中获取订单信息,那如果一个订单同时被两个服务获取到,那么就会造成数据错误,我们需要避免这种情况。

这时我们就可以使用Stream中的消息分组来解决
在这里插入图片描述
注意在Stream中处于同一个group中的多个消费者是竞争关系,就能够保证消息只会被其中一个应用消费一次。不同组是可以进行全面消费的(重复消费),同一个组内有竞争关系,只有其中一个可以消费。

8802与8803处于不同的组:
在这里插入图片描述
重复消费导致原因:微服务没有分组的话,默认每一微服务的组都不一样,组流水号都不一样,每一个组都会把消息消费一次,导致重复消费。

8.2 分组

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

8.2.1 8002、8003分成不同的组

group: atguiguA、atguiguB
8802修改yml:
在这里插入图片描述

8803修改yml:
在这里插入图片描述
重启8802、8803服务后:
在这里插入图片描述
此时,8001发送消息时,因为8002、8003处于不同的组,所以都会收到消息。

8.2.2 8002、8003分成相同的组

group: atguiguA

修改配置文件:
在这里插入图片描述
在这里插入图片描述
测试,此时8001发送消息:
在这里插入图片描述
8002收到的消息:
在这里插入图片描述
8003收到的消息:
在这里插入图片描述

结论:同一个组的多个微服务实例,每次只会有一个实例拿到消息。

8.3 持久化

通过上述内容,解决了重复消费的问题,再看看持久化。
停止8802/8803并去除8002的分组group: atguiguA,8803的分组group: atguiguA 没有去掉。
8801先发送4条消息到rabbitmq:
在这里插入图片描述

先启动8802,无分组属性配置,后台没有打印出来消息:
结果:没有!
在这里插入图片描述
发生了消息丢失!

再启动8803,有分组属性配置,后台打出来了MQ上的消息
在这里插入图片描述

总结:自定义组能够持久化,默认组不能持久化。

十五、SpringCloud Sleuth 分布式请求链路跟踪

1. 概述

为什么会出现这个技术?需要解决哪些问题?
在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的服务节点调用来协同产生最后的请求结果,每一个前段请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延时或者错误都会引起整个请求最后的失败。

是什么:
SpringCloud Sleuth提供了一套完整的服务跟踪的解决方案,在分布式系统中提供追踪解决方案并且兼容支持zipkin。

2. 搭建链路监控步骤
2.1 zipkin
2.1.1 下载

SpringCloud 从F版起已经不需要自己构建Zipkin Server了,只需要调用jar包即可。
下载 zipkin-server-2.12.9-exec.jar:
链接:https://pan.baidu.com/s/12f4EfxOHUhPbVb_-kW8zVQ
提取码:yyds

2.1.2 运行jar

下载完成后,运行jar:

java -jar zipkin-server-2.12.9-exec.jar

在这里插入图片描述
启动成功!

2.1.3 运行控制台

http://localhost:9411/zipkin
在这里插入图片描述
完整的调用链路:表示一条请求链路,一条链路通过TraceId唯一标识,Span标识发起的请求信息,各span通过parent id 关联起来。
在这里插入图片描述
一条链路通过Trace id唯一标识,Span 标识发起的请求信息,各span通过parent id关联起来。
在这里插入图片描述
整个链路的依赖关系如下:
在这里插入图片描述
名词解释:
Trace: 类似于树结构的Span集合,表示一条调用链路,存在唯一标识。
span: 表示调用链路来源,通俗的理解span就是一次请求信息。

3. 服务提供者 cloud-provider-payment8001修改

pom.xml ,添加如下依赖:
在这里插入图片描述
application.yml,添加如下配置:
在这里插入图片描述

PaymentController添加如下代码:
在这里插入图片描述

4. 服务调用方 cloud-consumer-order80 修改

pom.xml添加如下依赖:
在这里插入图片描述
application.yml 修改:
在这里插入图片描述
业务类 OrderController修改,添加以下代码:
在这里插入图片描述

5. 测试

依次启动eureka7001、7002,8001,80,然后80调用8001测试:http://localhost/consumer/payment/zipkin

打开 http://localhost:9411/zipkin ,出现以下界面:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
依赖关系:
在这里插入图片描述

十六、SpringCloud Alibaba 简介

1. SpringCloud Alibaba 带来了什么

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

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

引入依赖:

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

怎么玩:
Sentinel: 阿里巴巴开源产品,把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Nacos: 阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
RocketMQ: Apache RocketMQ 基于Java的高性能、高吞吐量的分布式消息和流计算平台。
Dubbo: Apache Dubbo 是一款高性能的RPC框架。
Seata: 阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
Alibaba Cloud OSS : 阿里云对象存储服务 (Object Storage Service,简称OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务,可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

2. SpringCloud alibaba 学习资料获取

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

英文:
1. https://github.com/alibaba/spring-cloud-alibaba
2. https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html

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

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

1. Nacos 简介
1.1 为什么叫Nacos?

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

1.2 是什么

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

1.3 能干嘛

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

1.4 去哪下

https://github.com/alibaba/nacos
https://nacos.io/zh-cn/

官网文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html

1.5 各注册中心比较
服务注册与发现框架CAP模型控制台管理社区活跃度
EurekaAP支持低(2.x 闭源)
ZookeeperCP不支持
ConsulCP支持
NacosAP支持

据说Nacos在阿里巴巴内部有超过10万的实例运行,已经过了类似双十一等各种大型流量的考验。

2. 安装并运行Nacos
  1. 本地Java+Maven环境已ok

  2. 先从官网下载Nacos

  3. 解压文件bin目录下,执行 startup.cmd
    在这里插入图片描述

  4. 命令运行成功后直接访问 http://localhost:8848/nacos,默认的账号密码都是nacos
    在这里插入图片描述
    在这里插入图片描述

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

文档1
文档2

3.1 基于Nacos的服务提供者

新建Module cloudalibaba-provider-payment9001

3.1.1 pom.xml

父pom.xml (SpringCloud2020 的pom):
在这里插入图片描述
本模块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>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-provider-payment9001</artifactId>
    <dependencies>
        <!--springcloud alibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-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>
3.1.2 application.yml
server:
  port: 9001
spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  #配置nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'
3.1.3 主启动类
package com.atguigu.springcloud.alibaba;

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

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

3.1.4 业务类
package com.atguigu.springcloud.alibaba.controller;

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

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

3.1.5 测试

访问 http://localhost:9001/payment/nacos/1
在这里插入图片描述
nacos控制台:
在这里插入图片描述
nacos服务注册中心 + 服务提供者都ok了

为了下一章节演示nacos的负载均衡,参照9001新建9002:
新建 cloudalibaba-provider-payment9002,或者取巧不想新建重复体力劳动,通过虚拟映射直接拷贝虚拟端口映射:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
启动后:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2 基于Nacos的服务消费者

新建Module cloudalibaba-consumer-nacos-order83

3.2.1 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>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-consumer-nacos-order83</artifactId>
    <dependencies>
        <!--springcloud alibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency><!--引入自定义的api通用包-->
            <groupId>org.example</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--SpringBoot 整合web 组件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

为什么Nacos支持负载均衡:集成了Ribbon
在这里插入图片描述

3.2.2 application.yml
server:
  port: 83
spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
# 消费者将要去访问的微服务名称(注册进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider
3.2.3 主启动类
package com.atguigu.springcloud.alibaba;

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

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

3.2.4 业务类

用到了Ribbon,需要写一个配置类:

package com.atguigu.springcloud.alibaba.config;

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

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced//restTemplate只管调用,本身不具备负载均衡功能,要用@LoadBalanced注解赋予restTemplate负载均衡的功能
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

controller:

package com.atguigu.springcloud.alibaba.controller;

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

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderNacosController {
    @Resource
    private RestTemplate restTemplate;
    
    @Value("${service-url.nacos-user-service}")
    private String serverURL;
    
    @GetMapping(value = "/consumer/payment/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Integer id) {
        return restTemplate.getForObject(serverURL + "/payment/nacos/" + id ,String.class);
    }
}

3.2.5 测试

启动Nacos,payment9001、9002,order83
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述9001、9002 轮询负载

3.2.6 服务注册中心对比

在这里插入图片描述

在这里插入图片描述
Nacos支持AP和CP模式的切换:
C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。

何时选择何种模式:一般来说,如果不需要存储服务级别的信息且服务实例是通过Nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如spring cloud和Dubbo服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。

如果需要在服务级别编辑或者存储配置信息,那么CP是必须的,K8S服务和DNS服务则适用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

切换模式命令:

curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP
4. Nacos作为服务配置中心演示
4.1 Nacos作为配置中心——基础配置

新建Module cloudalibaba-config-nacos-client3377

4.1.1 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>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-config-nacos-client3377</artifactId>
    <dependencies>
        <!--nacos config-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--nacos discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--web + actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用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>
4.1.2 yml

配置两个yml文件:Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application。

bootstrap.yml:

server:
  port: 3377

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

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

application.yml:

spring:
  profiles:
    active: dev  # 表示开发环境
4.1.3 主启动类
package com.atguigu.springcloud.alibaba;

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

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

4.1.4 业务类

controller:

package com.atguigu.springcloud.alibaba.controller;

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

@RestController
@RefreshScope //支持Nacos的动态刷新功能 通过springcloud原生注解@RefreshScope实现配置自动更新
public class ConfigClientController {
    
    @Value("${config.info}")
    private String configInfo;
    
    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}

4.1.5 在nacos中添加配置信息

Nacos中的匹配规则:
理论:
Nacos中的dataid的组成格式及与SpringBoot配置文件中的匹配规则
官网:https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
在这里插入图片描述

实操
设置DataId,公式 ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
在这里插入图片描述

根据公式得到的DataId:nacos-config-client-dev.yaml(注意是yaml,不是yml,否则启动服务会报错)
在这里插入图片描述

4.1.6 测试

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

在这里插入图片描述

4.1.7 自带动态刷新

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

4.2 Nacos作为配置中心——分类配置
4.2.1 分布式开发中的问题——多环境多项目管理

问题一:
实际开发中,通常一个系统会准备
dev开发环境
test测试环境
prod生产环境
如何保证指定环境启动时服务能够正确读取到Nacos上相应的配置文件呢?

问题二:
一个大型分布式微服务系统会有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境…
那么怎么对这些微服务配置进行管理呢?

4.2.2 Nacos的图形化管理界面

配置管理:
在这里插入图片描述

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

4.3 三种方案加载配置 Case
4.3.1 Data lD方案

指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置
默认空间+默认分组+新建dev和test两个DataID:

  1. 新建dev配置DataID
    在这里插入图片描述
  2. 新建test配置DataID
    在这里插入图片描述

同一个group的俩个DataID:
在这里插入图片描述
通过spring.profile.active属性就能进行多环境下配置文件的读取
在这里插入图片描述
测试:
在这里插入图片描述
在这里插入图片描述

4.3.2 Group方案

通过Group实现环境区分,新建相同名字不同Group的配置文件:
在这里插入图片描述
测试:
在这里插入图片描述
在这里插入图片描述

在bootstrap.yml中将group改为DEV_GROUP后:

在这里插入图片描述

4.3.3 NameSpace 方案

新建dev/test的NameSpace
在这里插入图片描述
回到服务管理——服务列表查看:
在这里插入图片描述

按照域名配置填写:
在这里插入图片描述

5. Nacos集群和持久化配置 (重要)
5.1 官网说明

https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
https://nacos.io/zh-cn/docs/deployment.html
官网集群架构图:
在这里插入图片描述
官网架构图翻译:
在这里插入图片描述
说明:
默认Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。

Nacos支持三种部署模式

  • 单机模式——用于测试和单机试用
  • 集群模式——用于生产环境,确保高可用
  • 多集群模式——用于多数据中心场景

在这里插入图片描述

spring.datasource.platform=mysql

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

再以单机模式启动nacos,nacos所有写嵌入式数据库的数据都写到了mysql

5.2 Nacos持久化配置解释

Nacos默认自带的是嵌入式数据库derby,derby到mysql切换配置步骤:

  1. \nacos\conf目录下找到sql脚本:nacos-mysql.sql,执行脚本
  2. \nacos\conf目录下找到application.properties,将以下配置添加到文件中 (对应改为自己的用户名密码)
spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=nacos_devtest
db.password=youdontknow
  1. 启动Nacos,可以看到是个全新的空记录界面,以前的信息是记录进derby.

注:nacos1.1.4不支持mysql8,因为里面的驱动是mysql5的驱动,所以需要下载源码更换mysql驱动重新编译。
重新编译教程:https://blog.csdn.net/m0_46157986/article/details/111084850

5.3 Linux版nacos+Mysql生产环境配置

预计需要,1个Nginx+3个nacos注册中心+1个mysql
Nacos下载Linux版本:

确保在环境中安装使用:

  1. 64bit OS Linux/Unix/Mac,推荐使用Linux
  2. 64bit JDK 1.8+
  3. Maven 3.2.x+
  4. 3个或3个以上Nacos节点才能构成集群

下载nacos-server-1.1.4.tar.gz 解压即可

5.4 集群配置步骤
5.4.1.Linux服务器上mysql数据库配置

SQL脚本在哪:
在这里插入图片描述
在Linux的mysql中,执行SQL脚本。

5.4.2 application.properties配置

位置:
在这里插入图片描述
在这里插入图片描述

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

梳理出3台nacos集群的不同服务端口号:
3333 4444 5555

复制出cluster.conf
在这里插入图片描述
内容:
这个IP不一定是127.0.0.1,必须是Linux命令hostname -I 能识别的ip
在这里插入图片描述
在这里插入图片描述

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

平时单机版的nacos启动,都是./startup.sh即可,但是集群启动,我们希望可以类似其他的shell命令,传递不同的端口号启动不同的nacos实例。
命令:./startup.sh -p 3333 表示启动端口号为3333的nacos服务器实例,和上一步cluster.conf配置的一致。
脚本修改内容
修改前:

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

修改后:
在这里插入图片描述在这里插入图片描述
启动方式:

./startup.sh -p 3333
5.4.5 Nginx的配置,由它作为负载均衡器

nginx安装:https://www.cnblogs.com/zym-k/p/14967540.html
修改nginx的配置文件
在这里插入图片描述

按照指定启动:

/usr/local/nginx/sbin目录下启动nginx

./nginx -c /usr/local/nginx/conf/nginx.conf
5.4.6 截止到此,1个Nginx+3个nacos注册中心+1个mysql完成

保证mysql、nginx启动,启动nacos3333、4444、5555
启动时可以打开nacos的日志,查看是否启动报错:tail -f /mynacos/logs
在这里插入图片描述
在这里插入图片描述
此处若返回的不是3,先关闭虚拟机,将内存增大为4G。

测试通过nginx访问nacos: http://192.168.234.135:1111/nacos/#/login
注意需要关闭linux防火墙或者开启1111端口!
Linux防火墙相关命令:https://www.cnblogs.com/niuben/p/13168708.html
在这里插入图片描述
集群信息:
在这里插入图片描述

添加一个配置测试:
在这里插入图片描述
数据库中信息:
在这里插入图片描述

5.4.7 测试——微服务cloudalibaba-provider-payment9002启动注册进nacos

修改yaml:
在这里插入图片描述

把集群三个nacos都启动后再启动9002,否则启动失败
在这里插入图片描述

5.4.8 高可用小结

在这里插入图片描述

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值