SpringCloud学习文档

SpringCloud

1,什么是SpringCloud

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

2,环境准备

创建父工程,导入需要的依赖,也可以直接进行依赖管理

<?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>org.example</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <junit.version>4.3</junit.version>
        <lombok.version>1.18.16</lombok.version>
        <log4j.version>1.2.17</log4j.version>
    </properties>
    <!--打包方式-->
    <packaging>pom</packaging>

    <dependencyManagement>
        <dependencies>
            <!--SpringCloud的依赖-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--SpringBoot的依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.4.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.22</version>
            </dependency>
            <!--数据源-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.23</version>
            </dependency>
            <!--SpringBoot启动器-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.3</version>
            </dependency>
            <!--Junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!--Lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!--log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <!--logback-->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.3</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

创建一个子模块,继承父工程

image-20201230211346280

添加需要的依赖,当前的模块自己需要的依赖,如果父依赖中已经进行了版本配置,可以直接引用到父依赖,这里就不用写了

创建数据库

/*
SQLyog Ultimate v12.08 (64 bit)
MySQL - 8.0.20 : Database - db01
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`db01` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `db01`;

/*Table structure for table `dept` */

DROP TABLE IF EXISTS `dept`;

CREATE TABLE `dept` (
  `deptNo` bigint NOT NULL AUTO_INCREMENT,
  `deptName` varchar(60) DEFAULT NULL,
  `db_source` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`deptNo`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='部门表';

/*Data for the table `dept` */

insert  into `dept`(`deptNo`,`deptName`,`db_source`) values (1,'开发部','db01'),(2,'人事部','db01'),(3,'财务部','db01'),(4,'市场部','db01'),(5,'运维部','db01'),(6,'饭桶部','db01');



根据数据库使用idea生成实体类

image-20201230211836192

选中表右击鼠标

image-20201230212442320

选择存放路径

image-20201230212529775

实体类传输要序列化实现Serializable接口

package com.hai.springcloud.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Dept implements Serializable {

    private Long deptNo;//部门编号
    private String deptName;//部门名称
    private String dbSource;//数据来自那个数据库

    public Dept(String dbSource) {
        this.dbSource = dbSource;
    }
}

3,服务提供者

新建一个子模块作为服务提供者

image-20201231134451316

导入提供者需要的依赖及其他模块

<dependencies>
    <!--我们需要拿到资源api实体类,所以要导入我们的api-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--Junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
    <!--数据库-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--logback-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
    </dependency>
    <!--Mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test</artifactId>
    </dependency>
    <!--springBoot-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--jetty-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
    <!--devtools-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>

编写application配置

#端口配置
server:
  port: 8001


# mybatis配置
mybatis:
  type-aliases-package: com.hai.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource  #druid数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: 123456
    

MyBatis配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
    <!--下划线转驼峰命名-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>

</settings>
</configuration>

编写对应的Dao,mapper,service,controller(这里代码就不上了,上个controller)提供restful服务接口

package com.hai.springcloud.controller;

import com.hai.springcloud.pojo.Dept;
import com.hai.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author Muzi
 * @version 3.6.3
 * @package com.hai.springcloud.controller
 * @date 2020/12/31 14:24
 * @project springcloud
 **/
//提供restful服务的接口
@RestController
public class DeptController {

    private DeptService deptService;

    @Autowired
    public void setDeptService(DeptService deptService) {
        this.deptService = deptService;
    }

    @PostMapping("/dept/add")
    public boolean addDept(Dept dept) {
        return deptService.addDept(dept);
    }

    @GetMapping("/dept/get/{id}")
    public Dept getDeptById(@PathVariable("id") Long id) {
        return deptService.getDeptById(id);
    }

    @GetMapping("/dept/list")
    public List<Dept> getAllDept() {
        return deptService.getAllDept();
    }

}

浏览器访问测试:localhost:8001/dept/list

image-20201231144054364

服务提供者创建结束,接下来就需要创建消费者来对接这个提供者的接口

4,服务消费者

创建一个子模块

image-20201231144526082

导入依赖,消费这不需要操作其他层,负责调用提供者的接口就好了

<!--只需要实体类和web,不需要数据库-->
<dependencies>
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--热部署devtools-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>

配置端口为80:

server:
  port: 80

编写controller,思考怎么调用到我们服务提供者的restful接口

  • 消费者不应该有service层,应该直接调用接口,那么怎么才能调到提供者的接口呢?
    • 通过RestTemplate这个类,这个类提供了很多的网络请求方法,需要我们将其注入到容器中使用

将RestTemplate注入Spring

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate initRestTemplate(){
        return new RestTemplate();
    }
}

在controller层使用

  • 我们发现提供者的接口地址都是http://localhost:8001开头,可以将这个地址定义为一个常量

    @Controller
    public class DeptConsumerController {
    
        private RestTemplate restTemplate;
    
        private static  final String REST_URL_PREFIX = "http://localhost:8001";
    
        @Autowired
        public void setRestTemplate(RestTemplate restTemplate) {
            this.restTemplate = restTemplate;
        }
        @SuppressWarnings("all")
        @RequestMapping("/consumer/dept/add")
        public boolean addDept(Dept dept){
    
            return restTemplate.postForObject(
                    REST_URL_PREFIX+"/dept/add",
                    dept,
                    Boolean.class
            );
        }
    
        @RequestMapping("/consumer/dept/get/{id}")
        public Dept getDeptById(@PathVariable("id") Long id){
            return restTemplate.getForObject(   //请求的类型
                    REST_URL_PREFIX+"/dept/get/"+id, //请求的地址
                    Dept.class  //返回的类型
            );
    
        }
    
        @SuppressWarnings("unchecked")
        @RequestMapping("/consumer/dept/list")
        public List<Dept> getAllDept(){
    
            return restTemplate.getForObject(
                    REST_URL_PREFIX+"/dept/list",
                    List.class
            );
    
        }
        
    }
    
    

开启消费者和提供者两个服务

image-20201231151855492

访问测试

image-20201231152226119

重点使用RestTemplate这个类向其他地址发送请求

5,服务注册与发现

1,什么是Eureka?

eureka,古希腊语翻译过来就是我找到了,我发现了

  • Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。

  • Eureka包含两个组件:Eureka Server和Eureka Client。

  • Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

  • Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。

  • 在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

  • Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。

2,搭建Eureka环境

创建一个子模块

image-20201231152955328

导入eureka-server的依赖

<!--依赖-->
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--devtools-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>

编写配置application

server:
  port: 7001

  # Eureka配置
eureka:
  instance:
    hostname: localhost #Eureka服务中心的地址,实例名称
  client:
    register-with-eureka: false #是否向服务中心注册自生服务,自己就是注册中心,所以不需要
    fetch-registry: false #表示是否为注册中心,true代表客户端,false代表注册中心
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/  #通过这个url能将其他服务注册到服务中心,主机加端口号就是可视化面板
      

编写启动类启动注册中心,添加@EnableEurekaServer注解启动注册中心

@SpringBootApplication
@EnableEurekaServer //我是一个eureka中心的启动类,我可以接收启发注册的服务
public class EurekaServerRun {

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

启动时可能会出现的问题

Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat版本太高,将springBoot的版本降到2.1.3左右

访问测试

image-20201231155955826

3,服务注册

如何将服务提供者注册到Eureka服务中心?

首相服务同提供者需要成为Eureka的客户端,就需要导入它的客户端依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

在配置文件中加上eureka的配置

# Eureka的配置
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/  #配置服务中心的地址,将这个服务向eureka注册中心注册
      

在启动类上加上EnableEurekaClient注解,开启注册功能,

启动注册中心,在启动服务提供者:

image-20201231161410621

查看服务是否被注册

image-20201231161445847

4,自我保护机制

我们要是停掉已经注册的服务,注册中心会做出怎样的举动?

image-20201231162822911

可以看到,在服务因为其他原因停掉后,eureka只是给了一段提示,但是服务没有被注销,依然保留着,只不过不能访问了,

总之就是:在某个时刻微服务不能使用了,eureka不会立刻清理掉服务,一九会对该微服务的信息进行保存

  • 默认情况下,如果eurekaserver在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒),但是当网络发生分区故障时,微服务与eureka之间无法正常通信,以上行为可能变得非常危险了,因为微服务本身是健康的,此时本不应该注销这个发生故障的微服务,Eureka通过自我保护机制来解决这个问题,,当EurekaServer节点在短时间内丢失过多客户端时,那么这个接待你就会进入自我保护模式,一旦进入该模式,EurekaServer会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会删除任何微服务)当网络故障恢复后,该EurekaServer节点会自动退出自我保护模式
  • 在自我保护模式中,EurekaServer会保护注册标表中的信息,不在注销任何微服务实例,当它收到心跳数重新恢复到阈值以上时,该EurekaServer节点就会自动退出自我保护模式,它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的微服务,就是好死不如赖活着
  • 综上所述,自我保护模式是一种应对网络异常的安全保护措施,宁可保留所有的微服务,也不盲目注销任何健康的微服务,使用自我保护模式,可以上Eureka集群更加 的壮健和稳定
  • 在springcloud中,可以使用eureka-server-enable-self-preservation = false 禁用自我保护机制,不推荐关闭

5,Eureka集群

我们微服务的目的就是为了在某个节点出现故障时及时跟换,而不会导致整个系统都奔溃,但是我们的注册中心只有一个,要是注册中心出故障了呢?整个服务是不是会崩盘?这样显然是不行的,那要解决这个问题,我们就需要搭建集群。

首先要创建三个注册中心,由于电脑配置限制,这里说一下搭建步骤就行了:

  1. 搭建三个注册中心

    image-20201231174850416

  2. 让三个注册中心发生关系

    image-20201231171030850

  3. 配置各个Eureka注册中心的配置

    • eureka7001

        # Eureka配置
      eureka:
        instance:
          hostname: eureka7001.com #Eureka服务中心的地址,实例名称
        client:
          register-with-eureka: false #是否向服务中心注册自生服务,自己就是注册中心,所以不需要
          fetch-registry: false #表示是否为注册中心,true代表客户端,false代表注册中心
          service-url:
            defaultZone: http://eureka7002.com:7002/eureka/ ,http://eureka7003com:7003/eureka/
      
    • eureka7002

        # Eureka配置
      eureka:
        instance:
          hostname: eureka7002.com #Eureka服务中心的地址,实例名称
        client:
          register-with-eureka: false #是否向服务中心注册自生服务,自己就是注册中心,所以不需要
          fetch-registry: false #表示是否为注册中心,true代表客户端,false代表注册中心
          service-url:
            defaultZone: http://eureka7001.com:7001/eureka/ ,http://eureka7003com:7003/eureka/
      
    • eureka7003

        # Eureka配置
      eureka:
        instance:
          hostname: eureka7003.com #Eureka服务中心的地址,实例名称
        client:
          register-with-eureka: false #是否向服务中心注册自生服务,自己就是注册中心,所以不需要
          fetch-registry: false #表示是否为注册中心,true代表客户端,false代表注册中心
          service-url:
            defaultZone: http://eureka7002.com:7002/eureka/ ,http://eureka7001com:7001/eureka/
      
  4. 将服务提供者向三个注册中心注册

    # Eureka的配置
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:7001/eureka/ ,http://localhost:7002/eureka/ ,http://localhost:7003/eureka/ 
    
  5. 启动服务:

    image-20201231175725025

  6. 访问测试:

    image-20201231171859719

搭建完集群之后,即使某个注册中心出现故障,也不会出现整个系统崩盘的情况,出现配置不生效的情况,改本机的host文件做伪装即可

6,CAP原则

image-20210102181816896

  • CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
    • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
    • 可用性(A):保证每个请求不管成功或者失败都有响应。
    • 分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。

CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足。

因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。通常使用类似 memcached 之类的 NOSQL 作为实现手段。虽然 memcached 也可以是分布式集群环境的,但是对于一份数据来说,它总是存储在某一台 memcached 服务器上。如果发生网络故障或是服务器死机,则存储在这台服务器上的所有数据都将不可访问。由于数据是存储在内存中的,重启服务器,将导致数据全部丢失。当然也可以自己实现一套机制,用来在分布式 memcached 之间进行数据的同步和持久化,但是实现难度是非常大的 。

6,负载均衡(Ribbon)

1,什么是Ribbon?

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

2,什么是负载均衡?

做web开发都会接触到负载均衡,这里我们就不细说了。

(摘自百度百科)负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。

负载均衡主要分为软件负载和硬件负载,在微服务盛行的现在,软件负载在微服务里成为主流,netflix的ribbon就是其中之一

3,Ribbon实现负载均衡

Ribbon用作于负载均衡,是属于客户端的,控制客户端访问哪台服务器

在消费者模块导入依赖

<!--Ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
<!--eureka-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

编写配置

  #Eureka配置
eureka:
  client:
    register-with-eureka: false   #作为消费者,客户端,不需要注册到注册中心,
    service-url:    #去哪里获取服务,去注册中心
      defaultZone: http://eureka7003.com:7003/eureka/,http://eureka7001.com:7001/eureka/,http://eureka7002com:7002/eureka/

配置负载均衡

在原先的RestTemplate配置上添加@LoadBalanced注解

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

修改原先的controller

//原先是写死的localhost:8001,现在通过服务名去访问,客户端不在关心地址和端口号的问题
private static  final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

在著启动类上加@EnableEurekaClient注解,启动五个服务

image-20201231184339938

访问测试:

image-20201231184554966

4,使用Ribbon实现负载均衡

创建三个数据库

/*
SQLyog Ultimate v12.08 (64 bit)
MySQL - 8.0.20 : Database - db01
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`db01` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `db01`;

/*Table structure for table `dept` */

DROP TABLE IF EXISTS `dept`;

CREATE TABLE `dept` (
  `deptNo` bigint NOT NULL AUTO_INCREMENT,
  `deptName` varchar(60) DEFAULT NULL,
  `db_source` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`deptNo`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='部门表';

/*Data for the table `dept` */

insert  into `dept`(`deptNo`,`deptName`,`db_source`) values (1,'开发部','db01'),(2,'人事部','db01'),(3,'财务部','db01'),(4,'市场部','db01'),(5,'运维部','db01'),(6,'饭桶部','db01');

数据库

image-20201231185133674

创建三个服务提供者:

image-20201231185855614

修改相应的端口号和url查询不同的数据库

启动测试,电脑配置原因,启动三个提供者一个eureka一个消费者,已经到尽头了

image-20201231190801962

三个服务已经成功注册

启动消费者测试

image-20201231191047790

同一个请求查询不同的数据库,完成负载均衡

我的电脑感觉很疲惫

image-20201231191442739

4,自定义Ribbon负载均衡算法

IRule接口的实现类都是负载均衡的算法

image-20201231192031240

  • IRule接口的实现类都是负载均衡的算法
    • RandomRule 随机
    • RoundRobinRule 轮询
    • AvailabilityFilteringRule 可用性过滤,会先跳过跳闸,访问故障的服务进行轮询
    • RetryRule 会按照指定轮询获取服务,如果服务获取失败,会在指定的时间内重试

自定义负载均衡算法,首先照搬一个算法的源码出来改改,(注意,算法的包不能和启动类同级,会被扫描到)

随机的算法

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.netflix.loadbalancer;

import com.netflix.client.config.IClientConfig;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class RandomRule extends AbstractLoadBalancerRule {
    public RandomRule() {
    }

    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;

            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }

                List<Server> upList = lb.getReachableServers();
                List<Server> allList = lb.getAllServers();
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }

                int index = this.chooseRandomInt(serverCount);
                server = (Server)upList.get(index);
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

我们定义的算法,每个微服务访问三次自动转到下一个微服务

public class ThreeRoundRule extends AbstractLoadBalancerRule {
    private int total = 0;//每个服务访问的次数
    private int currentServer = 0;  //当前是那个服务

    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;//没有可用服务返回null
        } else {
            Server server = null;

            while (server == null) {
                if (Thread.interrupted()) {
                    return null;//线程中断返回null
                }

                List<Server> upList = lb.getReachableServers();//获得活着的服务
                List<Server> allList = lb.getAllServers();//获得所有的服务
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;//无可用服务返回null
                }

                if (total < 3) {
                    server = upList.get(currentServer);
                    total++;
                } else {
                    total = 0;
                    currentServer++;
                    if (currentServer > upList.size()) {
                        currentServer = 0;
                    }
                    server = upList.get(total);
                }

                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

将我们的算法注入到Bean中(这个Bean的配置类是不与启动类同级的)

@Configuration
public class RibbonConfig {

    @Bean
    public IRule init(){
        return new ThreeRoundRule();
    }

}

在启动类上添加注解

name是服务名,configuration是我们算法的.class
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = ThreeRoundRule.class)

启动测试

image-20201231200620130

5,使用Feign实现负载均衡

1,什么是feign?

百度百科对其英文释义:创造或虚构,假装;装作;作假。根据其意思我们可以联想:在本地创建一个虚假的接口以连接真实,用一句话来总结,梦想照进现实。
Feign是通过动态代理的方式创建代理类,当服务启动时(是服务启动时还是调用时不大确定,有待考证,哪位大佬知道的麻烦留言感激不尽)创建动态代理类,Feign集成了Ribbon和RestTemplate是一个轻量级的 RESTful 的 HTTP 服务客户端,实现了复杂均衡和Rest调用的框架,其负载均衡的默认方法是轮询法,可以通过配置类进行修改。
Feign是通过Java注解的方式底层封装HttpClient进行发送Http请求发起远程调用。
其工作流程如下所示(图盗的,懒的画):

image-20210102182527556

2,feign实现

创建一个子模块

image-20201231202428688

删除我们原来的算法,使用feign默认的轮询

在新建的feign模块和api模块加上feign的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

在api添加service接口

@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")//value是服务名
@Component
public interface DeptClientService{
    /**
     * @date: 2020/12/31 14:12
     * @param: [dept]
     * @return: boolean
     * @author: Muzi
     * @discription: 增加一个部门
     **/
    @PostMapping("/dept/add")
    boolean addDept(Dept dept);

    /**
     * @date: 2020/12/31 14:14
     * @param: [id]
     * @return: com.hai.springcloud.pojo.Dept
     * @author: Muzi
     * @discription: 根据Id查询部门
     **/
    @GetMapping("/dept/get/{id}")
    Dept getDeptById(@PathVariable("id")Long id);

    /**
     * @date: 2020/12/31 14:14
     * @param: []
     * @return: java.util.List<com.hai.springcloud.pojo.Dept>
     * @author: Muzi
     * @discription: 查询所有部门
     **/
    @GetMapping("/dept/list")
    List<Dept> getAllDept();
}

将我们的feign的controller改掉

@RestController
public class DeptConsumerController {

    private DeptClientService deptClientService;

    @Autowired
    public void setDeptClientService(DeptClientService deptClientService) {
        this.deptClientService = deptClientService;
    }

    @RequestMapping("/consumer/dept/add")
    public boolean addDept(Dept dept){
        return this.deptClientService.addDept(dept);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept getDeptById(@PathVariable("id") Long id){
       return this.deptClientService.getDeptById(id);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> getAllDept(){
        return this.deptClientService.getAllDept();
    }

}

在启动类上开启feign

@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients(basePackages = {"com.hai.springcloud"})
public class FeignConsumerRun {
    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerRun.class,args);
    }
}

启动测试

image-20201231204146446

7,服务熔断(Hystrix)

1,分布式系统面临的问题

  1. 网络本身的不可靠性,因此每次网络通信都会伴随着网络不可用的风险(光纤、路由、DNS等硬件设备或系统的不可用),都会导致最终分布式系统无法顺利进行一次网络通信,另外,即使分布式系统各节点之间的网络通信能够正常执行,其延时也会大于单机操作,存在巨大的延时差别,也会影响消息的收发过程,因此消息丢失和消息延迟变的非常普遍。

  2. 网络分区,网络之间出现了网络不连通,但各个子网络的内部网络是正常的,从而导致整个系统的网络环境被切分成了若干个孤立的区域,分布式系统就会出现局部小集群,在极端情况下,这些小集群会独立完成原本需要整个分布式系统才能完成的功能,包括数据的事务处理,这就对分布式一致性提出非常大的挑战。

  3. 节点故障,节点故障是分布式系统下另一个比较常见的问题,指的是组成分布式系统的服务器节点出现的宕机或"僵死"现象,
    根据经验来说,每个节点都有可能出现故障,并且经常发生.

  4. 三态,分布式系统每一次请求与响应存在特有的“三态”概念,即成功、失败和超时。
    分布式系统中,由于网络是不可靠的,虽然绝大部分情况下,网络通信能够接收到成功或失败的响应,但当网络出现异常的情况下,就会出现超时现象,通常有以下两种情况:

    • 由于网络原因,该请求并没有被成功的发送到接收方,而是在发送过程就发生了丢失现象。
    • 该请求成功的被接收方接收后,并进行了处理,但在响应反馈给发送方过程中,发生了消息丢失现象。

2,服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

3,什么是Hystrix?

简介

在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是依赖服务,有的时候某些依赖服务出现故障也是很常见的。 Hystrix 可以让我们在分布式系统中对服务间的调用进行控制,加入一些调用延迟或者依赖故障的容错机制。Hystrix 通过将依赖服务进行资源隔离,进而阻止某个依赖服务出现故障时在整个系统所有的依赖服务调用中进行蔓延;同时Hystrix 还提供故障时的 fallback 降级机制。 总而言之,Hystrix 通过这些方法帮助我们提升分布式系统的可用性和稳定性。

hystrix的设计原则

  • 对依赖服务调用时出现的调用延迟和调用失败进行控制和容错保护。
  • 在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延。比如某一个服务故障了,导致其它服务也跟着故障。
  • 提供 fail-fast(快速失败)和快速恢复的支持。
  • 提供 fallback 优雅降级的支持。
  • 支持近实时的监控、报警以及运维操作。

举个例子:

有一个分布式系统,服务A依赖于服务B,服务B依赖于服务C/D/E。在这样一个成熟的系统内,比如说最多可能只有100个线程资源。正常情况下,40个线程并发调用服务C,各30个线程并发调用 D/E。

调用服务 C,只需要 20ms,现在因为服务C故障了,比如延迟,或者挂了,此时线程会吊住2s左右。40个线程全部被卡住,由于请求不断涌入,其它的线程也用来调用服务 C,同样也会被卡住。这样导致服务B的线程资源被耗尽,无法接收新的请求,甚至可能因为大量线程不断的运转,导致自己宕机。服务A也挂了。

Hystrix可以对其进行资源隔离,比如限制服务B只有40个线程调用服务C。当此40个线程被hang住时,其它60个线程依然能正常调用工作。从而确保整个系统不会被拖垮。

4,什么是服务熔断?

服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。

当请求正常时,看起来是这样的

image-20210102183353709

当其中有一个系统有延迟时,它可能阻塞整个用户请求:

img

在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(PS:意味着后续再有请求将无法立即提供服务)

img

复杂分布式体系结构中的应用程序有许多依赖项,每个依赖项在某些时候都不可避免地会失败。如果主机应用程序没有与这些外部故障隔离,那么它有可能被他们拖垮。

5,服务熔断实现

创建一个服务提供者,让具有Hystrix熔断机制

image-20201231212034021

在原有依赖上添加hystrix依赖

<!--hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

修改端口配置

#端口配置
server:
  port: 9001


# mybatis配置
mybatis:
  type-aliases-package: com.hai.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml
#spring的配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource  #druid数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: 123456

# Eureka的配置
eureka:
  client:
    service-url:
      defaultZone: http://eureka7003.com:7003/eureka/,http://eureka7001.com:7001/eureka/,http://eureka7002com:7002/eureka/  #配置服务中心的地址,将这个服务向eureka注册中心注册
  instance:
    instance-id: 部门服务提供者-01-Hystrix  #修改eureka上这个注册的服务描述信息

修改controller

//提供restful服务的接口
@RestController
public class DeptController {

    private DeptService deptService;

    @Autowired
    public void setDeptService(DeptService deptService) {
        this.deptService = deptService;
    }


    @GetMapping("/dept/get/{id}")
    @HystrixCommand(fallbackMethod = "hystrixGetDeptById")//容灾机制,失败调用备用方法
    public Dept getDeptById(@PathVariable("id") Long id) {
        Dept dept = deptService.getDeptById(id);
        if (dept==null){
            throw new NullPointerException();
        }
        return dept;
    }

    /**
     * @date: 2020/12/31 21:26
     * @param: [id]
     * @return: com.hai.springcloud.pojo.Dept
     * @author: Muzi
     * @discription: getDeptById 的熔断机制,在他发生故障时,我将顶替他为客户做出响应
     **/
    public Dept hystrixGetDeptById(@PathVariable("id")Long id){
        return new Dept()
                .setDeptNo(id)
                .setDbSource("not found this database in MySQL")
                .setDeptName("this deptName is not exist , the information from hystrix");
    }

}

在测试类添加注解@EnableCircuitBreaker,开启断路器

@EnableEurekaClient //在服务启动后自动注册到eureka注册中心
@SpringBootApplication
@EnableCircuitBreaker//开启服务熔断
public class ProviderRunHystrix_9001 {
    public static void main(String[] args) {
        SpringApplication.run(ProviderRunHystrix_9001.class,args);
    }
}

启动测试:

image-20201231214026794

更友好的反馈信息

6,什么是服务降级?

当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。降级:是利用有限资源,保障系统核心功能高可用、有损的架构方法。有限资源;核心高可用;有损;架构方法。

例如双十一时,用户的;点击量是极大的,所以每到双11时,支付宝的旗下产品会出现暂时不可用的情况,例如退款服务可能要在双十二过后才能恢复等,

7,服务降级实现

服务熔断是基于服务端的,而服务降级是基于客户端的,所有我们应该对api这个服务进行改动

之前我们通过feign在api服务提供了一个远程调用的接口,而我们的服务都是去调用那个接口,所以在服务关停时,我们应该反馈信息给用户,告诉用户当前服务不可用,

实现需要定义一个失败回调的类去实现fallbackFactory这个接口

代码:

@Component
public class DeptClientServiceFallback implements FallbackFactory {

    @Override
    public DeptClientService create(Throwable throwable) {
        return new DeptClientService() {
            @Override
            public boolean addDept(Dept dept) {
                return false;
            }

            @Override
            public Dept getDeptById(Long id) {
                return new Dept().setDeptNo(id).setDeptName("此服务已做出降级响应,当前服务不可用").setDbSource("没有数据");
            }

            @Override
            public List<Dept> getAllDept() {
                return null;
            }
        };
    }
}

在feignapi配置文件中开启服务降级

      #开启服务降级
feign:
  hystrix:
    enabled: true

在做降级接口的注解加上降级的实现类

@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallback.class)//value是服务名

启动服务测试

image-20210102151404711

8,Hystrix可视化监控面板(dashboard)

首先我们需要创建一个消费子模块,

image-20210102165328268

在原有依赖上添加依赖

<!--hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--hystrix-dashboard-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

编写启动类并开启监控

@SpringBootApplication
@EnableHystrixDashboard     //开启服务面板
public class DeptHystrixDashboardRu_6001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptHystrixDashboardRu_6001.class,args);
    }
}

服务端需要通过actuator完善监控信息

<!--actuator-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

服务端需要注入一个streamServlet的Bean

@Bean
public ServletRegistrationBean servletRegistrationBean(){
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
    servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
    return servletRegistrationBean;
}

启动服务测试

image-20210102155143566

查看监控(若出现空ping或者监控页面处于loading情况时,将8001换成9001的提供商)

image-20210102163024997

dashboard可视化面板解读

image-20210102163358893

说白了这就是一个对请求进行监控的页面,多少个请求,请求处于什么状态等。。

8,Zuul路由网关

1,什么是Zuul?

Zuul是Netflix的基于JVM的路由器和服务器端负载均衡器。

为什么需要Zuul?

一个项目是由多个微服务组成的,每一个微服务对应的地址和端口(ip、port)都是不一样的。这个对于客户端来说,访问会变得繁琐。这时,就需要统一的URL来负责反向代理这些请求,实现路由功能。

2,Zuul的实现

创建一个子模块

image-20210102165625938

添加zuul的依赖

<!--zuul-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

编写application配置

server:
  port: 5001

#spring
spring:
  application:
    name: springcloud-zuul
    #eureka
eureka:
  client:
    service-url:
     defaultZone: http://eureka7003.com:7003/eureka/,http://eureka7001.com:7001/eureka/,http://eureka7002com:7002/eureka/
  instance:
    instance-id: zuul网关服务


#zull
zuul:
  prefix: /hai    #公共的同意访问前缀
  routes:
    mydept.serviceId: springcloud-provider-dept  #将访问的url内容进行替换,替换成下面的内容
    mydept.path: /mydept/**
  ignored-services: springcloud-provider-dept   #隐藏springcloud-provider-dept微服务的名字,禁止通过微服务名字去访问,所有请求必须走路由网关,隐藏所有就写"*" 这个通配符

编写启动类并开启网关的代理服务

@SpringBootApplication
@EnableZuulProxy        //开启zuul代理
public class ZuulRun {
    public static void main(String[] args) {
        SpringApplication.run(ZuulRun.class,args);
    }
}

修改本机host文件伪装域名

127.0.0.1       www.hai.com

启动测试:url地址不在是原先的localhost而是我们配置号的zuul:http://www.hai.com:5001/hai/mydept/dept/get/3

image-20210102173221498

9,config配置中心

1,概述

分布式系统面临的配置文件的问题

微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中可能会出现大量的微服务,由于每个服务都需要必要的配置信息才能运行,所以一套集中式的,动态的配置管理设施是必不可少的,SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,那上百的配置文件修改起来,岂不是要死人。

image-20210102224107692

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

Spring Cloud Config为客户端和服务端两部分:

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

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

Spring Cloud Config分布式配置中心能干嘛?

  • 集中管理配置文件
  • 不同环境,不同配置,动态化的配置更新,分环境部署,
  • 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心同意拉取配置自己的信息
  • 当配置文件发生改变时,服务不需要重启,即可感知到配置的变化,并应用新的部署
  • 将配置信息以rest接口的形式暴露

Git与Spring CloudConfig整合(准备好自己git账号及仓库)

我的仓库地址:https://gitee.com/haijinmuzi/springcloud-config.git

克隆我们的仓库

image-20210102225822312

创建一个配置文件到我们所拉取仓库中

spring:
  profiles:
    active: dev
      


---
spring:
  profiles: dev
  application:
    name: springcloud-config-dev


---
spring:
  profiles: dev
  application:
    name: springcloud-config-dev

将这个文件push到我们的远程仓库中

image-20210102230629525

查看文件

image-20210102230654063

2,Server服务端

创建一个配置模块

image-20210102230916208

导入依赖

<!--config-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

编写模块的配置文件

server:
  port: 3001


spring:
  application:
    name: springcloud-config-server
  #连接远程仓库
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/haijinmuzi/springcloud-config.git  #https的仓库地址

编写启动类添加@EnableConfigServer启动

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

启动测试

image-20210102232023266

成功连接到git仓库并获取配置数据

3,Client客户端

新建一个模块作为客户端

image-20210102233445973

新建一个配置文件push到远程仓库

spring:
  profiles:
    active: dev



---
server:
  port:8221
#spring
spring:
  profiles: dev
  application:
    name: springcloud-config-dev
#eureka
eureka:
  client:
    service-url: 
      defaultZone: http://eureka7001.com:7001/eureka/
---
server:
  port:8222
spring:
  profiles: test
  application:
    name: springcloud-config-test
#eureka
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

提交

image-20210102233222808

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
</dependencies>

编写配置bootstrap.yml

spring:
  application:
    name: config-client
    
  cloud:
    config:
      name: config-client #需要拿的文件名,不需要后缀
      profile: dev
      label: master # 从那个分支拿
      uri: http://localhost:3001  #是我们服务配置中心的地址
      

编写一个测试类

@RestController
public class ConfigClientController {

    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaService;
    @Value("${server.port}")
    private String port;
    
    @RequestMapping("/config")
    public String test(){
        return "applicationName"+applicationName+
                "eurekaService"+eurekaService+
                "port"+port;
    }
}

编写启动类启动测试

image-20210103003711514

可能会遇到的问题:Could not resolve placeholder ‘xxxx‘ in value “${xxxx}“

解决方案:

  1. 将spring-cloud-starter-config降级到2.1.1左右的版本,
  2. 指定端口
  3. 将application.yml改为bootstrap.yml

application:
name: springcloud-config-test
#eureka
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/


提交

[外链图片转存中...(img-yJdlglRe-1609607968268)]

导入依赖

```xml
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
</dependencies>

编写配置bootstrap.yml

spring:
  application:
    name: config-client
    
  cloud:
    config:
      name: config-client #需要拿的文件名,不需要后缀
      profile: dev
      label: master # 从那个分支拿
      uri: http://localhost:3001  #是我们服务配置中心的地址
      

编写一个测试类

@RestController
public class ConfigClientController {

    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaService;
    @Value("${server.port}")
    private String port;
    
    @RequestMapping("/config")
    public String test(){
        return "applicationName"+applicationName+
                "eurekaService"+eurekaService+
                "port"+port;
    }
}

编写启动类启动测试

[外链图片转存中…(img-BPJULfDL-1609607968269)]

可能会遇到的问题:Could not resolve placeholder ‘xxxx‘ in value “${xxxx}“

解决方案:

  1. 将spring-cloud-starter-config降级到2.1.1左右的版本,
  2. 指定端口
  3. 将application.yml改为bootstrap.yml

Git源码地址

  • 1
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 1
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:博客之星2020 设计师:CSDN官方博客 返回首页
评论 1

打赏作者

七号男技师

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值