SpringCloud的简单使用

目录

微服务是什么

微服务化:
核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库;

微服务架构:
微服务架构是一种模式,它提倡将单一应用程序划分成一组小的服务,服务之间相互协调、相互配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间代用轻量级的通信机制相互协作(通常是基于HTTP协议的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外尽量避免统一的、集中式的管理服务机制,堆具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。

微服务强调的是服务的大小,关注的某一个点,具体解决某一个问题;
微服务架构强调的是一个整体,将微服务良好的整合起来;

微服务的优缺点

优点

  • 每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能或业务需求;
  • 开发简单、开发效率高,一个服务可能就转移的干一件事;
  • 微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的;
  • 微服务能使用不同的语言开发;
  • 易于和第三方继承,微服务允许容易切灵活的方式继承自动部署,通过持续集成工具;
  • 微服务只是业务逻辑的代码,不会喝HTML、CSS或其他界面组件混合;
  • 每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库;

缺点

  • 开发人员要处理分布式系统的复杂性
  • 多服务运维难度、随着服务的增加,运维的压力也在增大
  • 系统部署依赖
  • 服务间通信成本
  • 数据一致性
  • 系统集成测试
  • 性能监控

SpringCloud

是什么

分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶;

基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,吃了基于NetFlix的开源组件做高度抽象封装外,还有一些选型中立的开源组件;

利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具。包括配置管理,服务发现,断路器,路由,微代理。时间总线,全局锁,决策竞选,分布式会话等,它们都可以用SpringBoot的开发风格做到一键启动和部署;

SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架;

案例

父工程构建

主要是定义POM文件,将后续各个子模块公用的jar包统一提出来,类似一个抽象父类;
Packaging是pom

<packaging>pom</packaging>
<?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>per.xgt</groupId>
    <artifactId>SpringCloud-REST</artifactId>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>SpringCloud-REST-api</module>
    </modules>

    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--保证每次编译都是java8-->
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
    </properties>

    <dependencyManagement>
        <dependencies>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.9.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.0.4</version>
            </dependency>

            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.31</version>
            </dependency>

            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.0</version>
            </dependency>

            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.3</version>
            </dependency>

            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>

            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

</project>

API公共模块和公共Entity

在父工程下创建api模块SpringCloud-REST-api
在父工程pom文件下出现以下内容

<modules>
        <module>SpringCloud-REST-api</module>
</modules>
API模块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>SpringCloud-REST</artifactId>
        <groupId>per.xgt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringCloud-REST-api</artifactId>

    <!--当前Module需要用到的jar包,按自己需求添加,如果父类已经包含了可以不用谢版本号-->
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

</project>
实体类
package per.xgt.entity;

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

import java.io.Serializable;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 15:57
 * @Description: TODO
 * @Version: 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Dept implements Serializable {

    // 主键
    private Long id;
    // 名称
    private String deptName;
    //来自哪个数据库,因为微服务架构可以一个服务对应一个数据库,同一个信息被存储到不同数据库
    private String db_source;

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

}

部门微服务提供者模块

在父工程下创建部门服务提供者

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>SpringCloud-REST</artifactId>
        <groupId>per.xgt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>SpringCloud-REST-provider-dept-8081</artifactId>

    <dependencies>
        <!--引入自己定义的api通用包,可以使用Dept部门Entity-->
        <dependency>
            <groupId>per.xgt</groupId>
            <artifactId>SpringCloud-REST-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</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-test</artifactId>
        </dependency>
        <!--修改后立即生效,热部署-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>
yml配置
server:
  port: 8081
mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml  # mybatis配置文件所在路径
  type-aliases-package: per.xgt.entity                # 所有Entity别名类所在包
  mapper-locations:
    - classpath:mybatis/mapper/**/*.xml               # mapper映射文件位置
spring:
  application:
    name: SpringCloud-REST-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource      # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver        # mysql驱动包
    url: jdbc:mysql://localhost:3306/mybatis       # 数据库链接url
    username: root
    password: root
    dbcp2:
      min-idle: 5                                     # 数据库连接处最小维持连接数
      initial-size: 5                                 # 初始化连接数
      max-total: 20                                   # 最大连接数
      max-wait-millis: 200                            # 等待连接获取的最大超时时间
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" /><!-- 二级缓存开启 -->
    </settings>

</configuration>
Dao接口
package per.xgt.dao;

import org.apache.ibatis.annotations.Mapper;
import per.xgt.entity.Dept;

import java.util.List;

/**
 * @author gentao9527
 * @version 1.0
 * @date 2022/8/9 16:52
 * @description
 */
@Mapper
public interface DeptDao {

    public boolean addDept(Dept dept);

    public Dept findById(Long id);

    public List<Dept> findAll();

}

对应mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="per.xgt.entity.Dept">
    <select id="findById" resultType="per.xgt.entity.Dept" parameterType="Long">
        select id,dept_name,_db_source from dept where id=#{id};
    </select>
    <select id="findAll" resultType="per.xgt.entity.Dept">
        select id,dept_name,db_source from dept;
    </select>
    <insert id="addDept" parameterType="per.xgt.entity.Dept">
        INSERT INTO dept(dept_name,db_source) VALUES(#{deptName},DATABASE());
    </insert>
</mapper>
Service以及实现类
package per.xgt.service;

import per.xgt.entity.Dept;

import java.util.List;

/**
 * @author gentao9527
 * @version 1.0
 * @date 2022/8/9 17:01
 * @description
 */
public interface DeptService {

    public boolean add(Dept dept);

    public Dept get(Long id);

    public List<Dept> list();

}

package per.xgt.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import per.xgt.dao.DeptDao;
import per.xgt.entity.Dept;
import per.xgt.service.DeptService;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:01
 * @Description: TODO
 * @Version: 1.0
 */
@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    private DeptDao deptDao;

    @Override
    public boolean add(Dept dept) {
        return deptDao.addDept(dept);
    }

    @Override
    public Dept get(Long id) {
        return deptDao.findById(id);
    }

    @Override
    public List<Dept> list() {
        return deptDao.findAll();
    }
}

controller类
package per.xgt.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import per.xgt.entity.Dept;
import per.xgt.service.DeptService;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:04
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class DeptController {

    @Autowired
    private DeptService service;

    @RequestMapping(value = "/dept/add", method = RequestMethod.POST)
    public boolean add(@RequestBody Dept dept)
    {
        return service.add(dept);
    }

    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    public Dept get(@PathVariable("id") Long id)
    {
        return service.get(id);
    }

    @RequestMapping(value = "/dept/list", method = RequestMethod.GET)
    public List<Dept> list()
    {
        return service.list();
    }

}

如果安装的mysql版本8.0+ 需要换一下驱动,url需要后面加上?characterEncoding=utf8等参数

部门微服务消费者模块

父工程创建消费者模块

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>SpringCloud-REST</artifactId>
        <groupId>per.xgt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringCloud-REST-consumer-dept-8091</artifactId>

    <dependencies>
        <!--引入自己定义的api通用包,可以使用Dept部门Entity-->
        <dependency>
            <groupId>per.xgt</groupId>
            <artifactId>SpringCloud-REST-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 修改后立即生效,热部署 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>
配置类
package per.xgt.config;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:36
 * @Description: TODO
 * @Version: 1.0
 */
@Configuration
public class ConfigBean {

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

}

消费者controller
package per.xgt.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import per.xgt.entity.Dept;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:40
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class DeptControllerConsumer {

    private static final String REST_URL_PREFIX = "http://localhost:8081";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/comsumer/dept/add")
    public boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }

    @RequestMapping(value = "/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    }

    @RequestMapping(value = "/consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }

}

启动类启动,同时启动provider8081,访问8091的接口,实现restful访问

Eureka

Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务了,而不需要修改服务调用的配置文件了。功能类似与Dubbo的注册中心,比如Zookeeper。

Eureka采用了CS的设计架构。Eureka Server作为服务注册功能的服务器,它是服务注册中心。

而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来发现系统中的其他微服务,并执行相关的逻辑。

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

Eureka Client是一个java客户端:
用于简化Eureka Server的交互,客户端同时也具备一个内置的。使用轮询负载算法的负载均衡器。在应用启动后,将会相Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接受到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

原则

Netflix在设计Eureka时遵守的就是AP原则;

关系型数据库传统的ACID

A Atomicity :原子性
C Consistency:一致性
I Isolation:独立性
D Durability:持久性

CAP

C Consistency:强一致性
A Availability:高可用性
P Partition tolerance:分区容错性

CAP的3进2
最多只能同时较好的满足两个。

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

分布式系统:分区容错性是必须要实现的,所以只能在一致性和可用性之间选择;

EurekaServer服务注册中心

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>SpringCloud-REST</artifactId>
        <groupId>per.xgt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringCloud-REST-Eureka-8071</artifactId>

    <dependencies>
        <!--eureka-server服务端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
        <!-- 修改后立即生效,热部署 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>
yml配置文件
server:
  port: 8071

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

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

/**
 * @Author: gentao9527
 * @date: 2022/8/9 20:16
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableEurekaServer // EurekaServer服务器端启动了,接受其他微服务注册进来
public class SpringCloud_RES_Eureka_8071_APP {

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

}

启动类启动,访问http://localhost:8071/,为服务中心

服务注册

修改服务提供者微服务:SpringCloud-REST-provider-dept-8081

pom文件新增依赖
<!-- 将微服务provider侧注册进eureka -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
yml配置文件修改添加配置
eureka:
  client: #客户端注册进eureka服务列表内
    service-url:
      defaultZone: http://localhost:8071/eureka # 服务中心地址
启动类
package per.xgt;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:08
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableEurekaClient // 本服务启动后会自动注册进eureka服务中
public class SpringCloud_RES_provider_dept_8081_APP {

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

}

启动服务中心,再启动dept微服务,就能发现服务注册进Eureka服务中心

主机映射名称修改

eureka:
  client: #客户端注册进eureka服务列表内
    service-url:
      defaultZone: http://localhost:8071/eureka # 服务中心地址
  instance:
    instance-id: SpringCloud-REST-dept-provider-8081

访问信息有IP信息提示

eureka:
  client: #客户端注册进eureka服务列表内
    service-url:
      defaultZone: http://localhost:8071/eureka # 服务中心地址
  instance:
    instance-id: SpringCloud-REST-dept-provider-8081
    prefer-ip-address: true # 访问路径可以显示IP

info内容的详细信息

问题:
在注册中心点击服务中心的status下info信息显示ErrorPage

解决:
provider添加依赖:

<!-- actuator监控信息完善 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

父工程添加build构建信息:

<build>
        <finalName>SpringCloud-REST</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <delimiters>
                        <delimit>$</delimit>
                    </delimiters>
                </configuration>
            </plugin>
        </plugins>
    </build>

在provider的配置文件添加配置:

info:
  app.name: SpringCloud-REST-dept-provider-8081
  company.name: www.xgt.com
  build.artifactId: $project.artifactId$
  build.version: $project.version

Eureka自我保护机制

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

某一刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存。

默认情况下,如果在EurekaServer在一定时间内没有接受到某个微服务实例的心跳,EurekaServer将会注销实例(默认90秒)。

但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了,因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题,当EurekaServer节点在短时间内丢失过多客户端时,那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当故障恢复后,该EurekaServer节点会自动退出自我保护机制。

在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该EurekaServer节点就回自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。

综上,自我保护模式是一种应对网络异常的安全保护措施,可以让Eureka集群更加的健壮、稳定。

如果需要禁用,在注册中心用以下参数修改(不推荐)

eureka.server.enable-self-preservation=false

Eureka服务发现

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

修改provider的controller
package per.xgt.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import per.xgt.entity.Dept;
import per.xgt.service.DeptService;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:04
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class DeptController {

    @Autowired
    private DeptService service;

    @Autowired
    private DiscoveryClient client;

    @RequestMapping(value = "/dept/add", method = RequestMethod.POST)
    public boolean add(@RequestBody Dept dept) {
        return service.add(dept);
    }

    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    public Dept get(@PathVariable("id") Long id) {
        return service.get(id);
    }

    @RequestMapping(value = "/dept/list", method = RequestMethod.GET)
    public List<Dept> list() {
        return service.list();
    }

    @RequestMapping(value = "/dept/discovery",method = RequestMethod.GET)
    public Object discovery(){
        List<String> list = client.getServices();
        System.out.println("======" + list);

        List<ServiceInstance> srvList = client.getInstances("SpringCloud-REST-dept");
        for (ServiceInstance element : srvList) {
            System.out.println(element.getServiceId() + ":" + element.getHost() + ":" + element.getPort() + ":" + element.getUri());
        }
        return this.client;
    }

}

启动类配置服务发现
@EnableDiscoveryClient // 服务发现
修改consumer的controller
@RequestMapping(value = "/consumer/dept/discovery")
    public Object discovery(){
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery",Object.class);
    }

Eureka集群配置

再创建两个个Eureka的Module8072 8073,并修改启动类

修改hosts映射文件

WIN路径

C:\Windows\System32\drivers\etc

Linux路径

/etc/hosts

添加以下内容:

127.0.0.1       eureka8071.com
127.0.0.1       eureka8072.com
127.0.0.1       eureka8073.com
EurekaServeryml文件配置
server:
  port: 8071

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

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

eureka:
  instance:
    hostname: eureka8073.com        #eureka服务端的实例名称
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false           #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
      defaultZone: http://eureka8071.com:8071/eureka,http://eureka8072.com:8072/eureka
  server:
    enable-self-preservation: false
将服务注册provider的yaml配置修改注册进三台Eureka
server:
  port: 8081
mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml  # mybatis配置文件所在路径
  type-aliases-package: per.xgt.entity                # 所有Entity别名类所在包
  mapper-locations:
    - classpath:mybatis/mapper/**/*.xml               # mapper映射文件位置
spring:
  application:
    name: SpringCloud-REST-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource      # 当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver       # mysql驱动包
    url: jdbc:mysql://119.3.230.11:3306/mybatis?characterEncoding=utf8       # 数据库链接url
    username: root
    password: XGTflh1314!@#
    dbcp2:
      min-idle: 5                                     # 数据库连接处最小维持连接数
      initial-size: 5                                 # 初始化连接数
      max-total: 20                                   # 最大连接数
      max-wait-millis: 200                            # 等待连接获取的最大超时时间
eureka:
  client: #客户端注册进eureka服务列表内
    service-url:
      #defaultZone: http://localhost:8071/eureka # 服务中心地址
      defaultZone: http://eureka8071.com:8071/eureka,http://eureka8072.com:8072/eureka,http://eureka8073.com:8073/eureka
  instance:
    instance-id: SpringCloud-REST-dept-provider-8081
    prefer-ip-address: true # 访问路径可以显示IP
info:
  app.name: SpringCloud-REST-dept-provider-8081
  company.name: www.xgt.com
  build.artifactId: $project.artifactId$
  build.version: $project.version

Ribbon

是什么?

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

主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起,Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer后面的所有机器,Ribbon会自动帮你基于某种规则去连接这些机器。

负载均衡

在微服务或分布式集群中经常的一种应用。简单说,就是讲用户的请求平摊的分配到多个服务商。常见的负载均衡有软件Nginx、LVS、硬件F5等;
相应的中间件,例如Dubbo和SpringCloud中均给我们提供了负载均衡,切后者可以自定义算法;

客户端添加依赖

在consumer添加相关依赖

<!-- Ribbon相关 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

客户端修改yml文件添加eureka的服务注册地址

server:
  port: 8091

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka8071.com:8071/eureka,http://eureka8072.com:8072/eureka,http://eureka8073.com:8073/eureka,

客户端启动类添加注解

package per.xgt;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:55
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableEurekaClient
public class SpringCloud_RES_comsumer_dept_8091_APP {

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

}

客户端实现负载均衡访问

/**
     * RestTemplate提供了多重便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模块类,是Spring提供的用于访问Rest服务的客户端模板工具类
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

客户端修改Controller访问类

// 通过微服务id名字访问
    private static final String REST_URL_PREFIX = "http://SpringCloud-REST-dept";

访问consumer接口

服务端新建provider并修改yml配置

新建provider8082、8083,并修改对应yml配置文件,连接各自的数据库;
注意:spring: application: name: SpringCloud-REST-dept不能改

instance:  instance-id: SpringCloud-REST-dept-provider-8082

instance:  instance-id: SpringCloud-REST-dept-provider-8083

instance-id不能相同,不然认为是同一个实例

Ribbon组件IRule

根据特定算法来确定要访问的服务

  • RoundRobinRule 轮询,默认
  • RandomRule 随机
  • AvailabilityFilteringRule
    会先过滤掉由多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后堆剩余的服务列表按照轮询的策略进行访问
  • WeightedReponseTimeRule 权重
    根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大,服务启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够后会切换到WeightedReponseTimeRule
  • RetryRule
    先按照RoundRobinRule策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
  • BestAvailableRule
    会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • ZoneAvoidanceRule
    复合判断server所在区域的性能和server的可用性选择服务器;
切换算法
/**
     * 返回指定的负载均衡算法
     * @return
     */
    @Bean
    public IRule getIRule(){
        //return new RoundRobinRule();// 轮询
        return new RandomRule();// 随机
    }
自定义算法
  • 在启动微服务的时候就能去加载自定义Ribbin配置类,从而使配置生效
@RibbonClient(name="XXX",configuration=XXXXX.class)

name:指定哪个微服务使用自定义的负载均衡算法
configuration:哪个类实现了算法

注意:
自定义配置类不能放在@ComponentScan所扫描的当前包以及子包下,否则我们自定义的这个配置类就会被所有Ribbon客户端所共享,也就是说达不到特殊化定制的目的了。

自定义算法规则:

package per.rule;

import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/10 15:45
 * @Description: TODO
 * @Version: 1.0
 */
@Configuration
public class MySelfRule {

    // 5次5次轮询
    @Bean
    public IRule getIRule(){
        return new MyRandomRule();
    }

}

package per.rule;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/10 15:54
 * @Description: TODO
 * @Version: 1.0
 */
public class MyRandomRule extends AbstractLoadBalancerRule {
    // total = 0 // 当total==5以后,我们指针才能往下走,
    // index = 0 // 当前对外提供服务的服务器地址,
    // total需要重新置为零,但是已经达到过一个5次,我们的index = 1
    private int total = 0;            // 总共被调用的次数,目前要求每台被调用5次
    private int currentIndex = 0;    // 当前提供服务的机器索引
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        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;
            }
            if (total < 5) {
                server = upList.get(currentIndex);
                total++;
            } else {
                total = 0;
                currentIndex++;
                if (currentIndex >= upList.size()) {
                    currentIndex = 0;
                }
            }
            if (server == null) {
                /*
                 * The only time this should happen is if the server list were somehow trimmed.
                 * This is a transient condition. Retry after yielding.
                 */
                Thread.yield();
                continue;
            }
            if (server.isAlive()) {
                return (server);
            }
            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }
        return server;
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // TODO Auto-generated method stub
    }

}

Feign

是什么?

是一个声明式的Web服务客户端,使得编写Web服务客户端变得非常容易;

只需要创建一个接口,然后再上面添加注解即可;

能干什么?

在实际开发中,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它,即可完成堆服务提供方的接口绑定,简化了使用SpringCloud Ribbon时,自动封装服务调用客户端的开发量;

使用

创建和8091consumer类似的工程。

修改pom文件添加依赖
<!--Feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
修改api公共模块添加依赖
<!--Feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
api公共模块新增接口
package per.xgt.service;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import per.xgt.entity.Dept;

import java.util.List;

/**
 * @author gentao9527
 * @version 1.0
 * @date 2022/8/10 16:27
 * @description
 */
@FeignClient(value = "SpringCloud-REST-dept")
public interface DeptService {

    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    public Dept get(@PathVariable("id") long id);

    @RequestMapping(value = "/dept/list", method = RequestMethod.GET)
    public List<Dept> list();

    @RequestMapping(value = "/dept/add", method = RequestMethod.POST)
    public boolean add(Dept dept);

}

修改consumer的controller
package per.xgt.controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import per.xgt.entity.Dept;
import per.xgt.service.DeptService;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:40
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class DeptControllerConsumer {

    @Autowired
    private DeptService deptService;

    @RequestMapping(value = "/consumer/dept/add")
    public boolean add(Dept dept) {
        return deptService.add(dept);
    }

    @RequestMapping(value = "/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return deptService.get(id);
    }

    @RequestMapping(value = "/consumer/dept/list")
    public List<Dept> list() {
        return deptService.list();
    }

}

consumer启动了添加注解@EnableFeignClients
package per.xgt;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:55
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"per.xgt"})
public class SpringCloud_RES_comsumer_dept_Feign_APP {

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

}

Hystrix

服务雪崩

多个服务之间调用的时候,在服务调用链上某个服务的响应时间过长或者不可用,对前面的微服务的调用就会越占越多的系统资源,进而引起系统崩溃;

对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的时,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要堆故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

是什么?

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

断路器本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似于熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间等待或抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩;

服务熔断

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

创建Hystrix项目

pom文件

与provider8081依赖一样,新增Hystrix依赖

<!--Hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
controller进行熔断处理
package per.xgt.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import per.xgt.entity.Dept;
import per.xgt.service.DeptService;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:04
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class DeptController {

    @Autowired
    private DeptService service;

    @Autowired
    private DiscoveryClient client;

    @RequestMapping(value = "/dept/add", method = RequestMethod.POST)
    public boolean add(@RequestBody Dept dept) {
        return service.add(dept);
    }

    @RequestMapping(value = "/dept/list", method = RequestMethod.GET)
    public List<Dept> list() {
        return service.list();
    }

    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    @HystrixCommand(fallbackMethod = "hystrixGet")
    public Dept get(@PathVariable("id") Long id) {
        Dept dept = service.get(id);
        if (null == dept){
            throw new NullPointerException("数据库没有");
        }
        return dept;
    }
    public Dept hystrixGet(@PathVariable("id") Long id) {
        Dept dept = new Dept().setId(id).setDeptName("该ID:" + id + "没用对应信息").setDb_source("no data");
        return dept;
    }

}

修改主启动类
package per.xgt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/9 17:08
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableEurekaClient // 本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient // 服务发现
// 对Hystrix熔断机制的支持
@EnableCircuitBreaker
public class SpringCloud_REST_provider_dept_8081_Hystrix_APP {

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

}

服务降级

从整体负荷考虑,当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。

服务降级处理事在客户端,与服务端没有关系,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器。

修改api公共模块工程
根据service新建实现了FlaabackFactoty接口的实现类
package per.xgt.service;

import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import per.xgt.entity.Dept;

import java.util.List;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/10 18:20
 * @Description: TODO
 * @Version: 1.0
 */
@Component
public class DeptServiceFallback implements FallbackFactory<DeptService> {

    @Override
    public DeptService create(Throwable throwable) {
        return new DeptService() {
            @Override
            public Dept get(long id) {
                return new Dept().setId(id).setDeptName("无此数据,服务已降级").setDb_source("no dataBase");
            }

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

            @Override
            public boolean add(Dept dept) {
                return false;
            }
        };
    }

}

在service接口的注解@FeignClient中添加fallbackFactory属性
@FeignClient(value = 
"SpringCloud-REST-dept",fallbackFactory = 
DeptServiceFallback.class)
客户端8092的yml配置文件启用hystrix
feign:
  hystrix:
    enabled: true

HystrixDashboard

服务监控
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。

Hystrix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。SpringCloud也提供了Hystrix Dashboard的整合,堆监控内容转化成可视化界面。

新建consumer8093Module

SpringCloud-REST-consumer-dept-8093-hystrixDashboard

依赖

新增hystrix与hystrixdashboard

<?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>SpringCloud-REST</artifactId>
        <groupId>per.xgt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringCloud-REST-consumer-dept-8093-hystrixDashboard</artifactId>

    <dependencies>
        <!--引入自己定义的api通用包,可以使用Dept部门Entity-->
        <dependency>
            <groupId>per.xgt</groupId>
            <artifactId>SpringCloud-REST-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Ribbon相关 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--Feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        </dependency>
        <!-- 修改后立即生效,热部署 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>
yaml配置
server:
  port: 8093
启动了
package per.xgt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**
 * @Author: gentao9527
 * @date: 2022/8/10 22:09
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableHystrixDashboard
public class SpringCloud_RES_consumer_dept_8093_hystrixDashboard_APP {

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

}

provider增加监控依赖配置
<!-- actuator监控信息完善 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
启动dashboard服务

访问:http://localhost:8093/hystrix

启动provider-hystrix
Cluster via Turbine (default cluster): http://turbine-hostname:port/turbine.streamCluster via Turbine (custom cluster): http://turbine-hostname:port/turbine.stream?cluster=[clusterName]Single Hystrix App: http://hystrix-app:port/hystrix.stream

单个监控访问路径为:
如8081,需要访问路径:

http://localhost:8081/hystrix.stream

页面重复显示:


ping: 

data: {"type":"HystrixCommand","name":"get","group":"DeptController","currentTime":1660141472299,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":0,"rollingCountBadRequests":0,"rollingCountCollapsedRequests":0,"rollingCountEmit":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackEmit":0,"rollingCountFallbackFailure":0,"rollingCountFallbackMissing":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"rollingMaxConcurrentExecutionCount":0,"latencyExecute_mean":0,"latencyExecute":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"latencyTotal_mean":0,"latencyTotal":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"propertyValue_circuitBreakerRequestVolumeThreshold":20,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":50,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":1000,"propertyValue_executionTimeoutInMilliseconds":1000,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":true,"propertyValue_requestLogEnabled":true,"reportingHosts":1,"threadPool":"DeptController"}
图形化监控

将需要查看图形化监控的单个dashboard地址(http://localhost:8081/hystrix.stream)填写在页面中的地址栏

下面参数:
Delay:用来监控服务器上轮询监控信息的延迟时间,默认为2000毫秒,可以通过配置该属性来降低客户端的网络和CPU消耗

Title:该参数对应了头部标题Hystrix Stream之后的内容,默认会使用具体监控实例的URL,可以通过配置该信息来展示更合适的标题

颜色数字:

  • 绿色:成功数
  • 蓝色:熔断数
  • 青色:错误请求数
  • 黄色:超时数
  • 紫色:线程池拒绝数
  • 红色:失败/异常数
  • 灰色:最近10S错误百分比

HOST:服务请求频率
Cluster:集群请求频率

线:两分钟内相对流量变化曲线

实心圆:共有两种含义。它功过颜色的变化代表了实例的健康成都,它的健康从绿色<黄色<橙色<红色递减。它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实例是快速的发现故障实例和高压力实例;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存在这里插入图片描述

Zuul 网关

包含了对请求的路由和过滤的两个主要功能:
路由:负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。
过滤:对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。
Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。

**注意:**Zuul服务最终还是会注册进Eureka

创建Module
SpringCloud-REST-Zuul-gateway-8061
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>SpringCloud-REST</artifactId>
        <groupId>per.xgt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringCloud-REST-Zuul-gateway-8061</artifactId>

    <dependencies>
        <!-- zuul路由网关 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!-- 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>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>per.xgt</groupId>
            <artifactId>SpringCloud-REST-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</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-test</artifactId>
        </dependency>
        <!-- 热部署插件 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>
yaml文件
server:
  port: 8061

spring:
  application:
    name: SpringCloud-REST-Zuul-gateway

eureka:
  client:
    service-url:
      defaultZone: http://eureka8071.com:8071/eureka,http://eureka8072.com:8072/eureka,http://eureka8073.com:8073/eureka
  instance:
    instance-id: gateway8061.com
    prefer-ip-address: true

info:
  app.name: xgt-pringCloud-REST-Zuul-gateway
  company.name: www.xgt.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$
修改hosts文件
127.0.0.1       	gateway8061.com
启动类
package per.xgt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/11 9:43
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableZuulProxy
public class SpringCloud_REST_Zuul_gateway_8061_APP {

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

}
启动微服务
  • 不用路由访问
http://localhost:8081/dept/list
  • 使用路由访问
http://gateway8061.com:8061/SpringCloud-REST-dept/dept/get/1

Zuul路由访问映射

修改yaml文件
zuul:
  ignored-services: "*" # 原真实服务名忽略,多个所有的都忽略可以用 "*"
  routes:
    mydept.serviceId: SpringCloud-REST-dept
    mydept.path: /mydept/**
  prefix: /xgt # 在所有的服务前面需要加上/xgt

访问路径:

http://gateway8061.com:8061/xgt/mydept/dept/get/1

Config 分布式配置中心

概述

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

SpringCloud Config分为服务端和客户端两部分。

  • 服务端:分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口;
  • 客户端:通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于堆环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容;

作用

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

Config与Git通信

gitEE/github上新建一个仓库
SpringCloud-REST-Config

拉取到本地

git clone url
config中新建一个yaml文件
spring:
  profiles:
    active:
      - dev
---
spring:
  profiles: dev
    application:
      name: SpringCloud-REST-Config-dev
---
spring:
  profiles: test
    application:
      name: SpringCloud-REST-Config-test

文件以UTF-8的格式保存

将配置推送至gitEE/github上
git add

git commit -m "add application.yaml"

git push origin master
新建配置中心Module
  • yaml配置文件
server:
  port: 8051

spring:
  application:
    name:  SpringCloud-REST-Config
  cloud:
    config:
      server:
        git:
          uri: url
  • 主启动了注解
package per.xgt;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/12 10:28
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableConfigServer
public class SpringCloud_REST_Config_8051_APP {

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

}

  • hosts文件增加映射
127.0.0.1 		config8051.com
  • 启动微服务8051
    查看能否获取配置
http://config8051.com:8051/application-dev.yml

http://config8051.com:8051/application-test.yml

http://config8051.com:8051/application-xxx.yml

报错

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Cannot clone or checkout repository
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) ~[javax.servlet-api-3.1.0.jar:3.1.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) ~[javax.servlet-api-3.1.0.jar:3.1.0]
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:841) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1650) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:206) ~[websocket-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) ~[spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110) ~[spring-boot-actuator-1.5.9.RELEASE.jar:1.5.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) ~[spring-boot-actuator-1.5.9.RELEASE.jar:1.5.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1637) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) ~[jetty-security-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:190) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473) ~[jetty-servlet-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.Server.handle(Server.java:561) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:334) ~[jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251) [jetty-server-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279) [jetty-io-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:104) [jetty-io-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124) [jetty-io-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247) [jetty-util-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140) [jetty-util-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) [jetty-util-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:243) [jetty-util-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:679) [jetty-util-9.4.7.v20170914.jar:9.4.7.v20170914]
	at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:597) [jetty-util-9.4.7.v20170914.jar:9.4.7.v20170914]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_311]
Caused by: java.lang.IllegalStateException: Cannot clone or checkout repository
	at org.springframework.cloud.config.server.environment.JGitEnvironmentRepository.refresh(JGitEnvironmentRepository.java:217) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.JGitEnvironmentRepository.getLocations(JGitEnvironmentRepository.java:169) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository.getLocations(MultipleJGitEnvironmentRepository.java:146) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.AbstractScmEnvironmentRepository.findOne(AbstractScmEnvironmentRepository.java:42) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository.findOne(MultipleJGitEnvironmentRepository.java:186) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.CompositeEnvironmentRepository.findOne(CompositeEnvironmentRepository.java:45) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.EnvironmentEncryptorEnvironmentRepository.findOne(EnvironmentEncryptorEnvironmentRepository.java:53) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.EnvironmentController.labelled(EnvironmentController.java:106) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.EnvironmentController.labelledYaml(EnvironmentController.java:184) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at org.springframework.cloud.config.server.environment.EnvironmentController.yaml(EnvironmentController.java:174) ~[spring-cloud-config-server-1.3.1.RELEASE.jar:1.3.1.RELEASE]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_311]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_311]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_311]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_311]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	... 57 common frames omitted

原因:

  • 首先检查自己的git仓库权限是否公开,如果是私有的肯定克隆不下来;
  • 如果确认自己的仓库权限没有问题就看下自己的配置文件url有没有加.git后缀;
  • 如果上述都没有问题那就在配置文件上加上你Github或者Gitee的用户名和密码,问题基本都能解决
spring:
  application:
    name:  SpringCloud-REST-Config
  cloud:
    config:
      server:
        git:
          uri: url
          username: xxx
          password: sss
skip-ssl-validation: true
  • 有可能是Windows下安装的git本身的问题,可以尝试在本地 git clone 你的项目,能 clone 下来就需要另找原因,不能 clone 下来请检查你的公私钥是否配置正确!
  • 可能是访问协议的问题,因为https 走的是http+ssl 协议,如果你的证书没有注册的话就会产生安全方面的问题,就会导致你会被拒绝

还是报错:
说我yaml格式不对,case by指出了那一行修改yaml文件

spring:
  profiles:
    active:
      - dev
      -
---
spring:
  profiles: dev
  application:
    name: SpringCloud-REST-Config-dev

---
spring:
  profiles: test
  application:
    name: SpringCloud-REST-Config-test

重新push,启动微服务,访问成功

config客户端通过config中心获取配置

push新的yaml配置文件SpringCloud-Config-client.yml
spring:
  profiles:
    active:
      - dev

---
server:
  port: 8001
spring:
  profiles: dev
  application:
    name: SpringCloud-Config-client
eureka:
  client:
    service-url:
      defaultZone: http://eureka-dev8071.com:8071/eureka

---
server:
  port: 8002
spring:
  profiles: test
  application:
    name: SpringCloud-Config-client
eureka:
  client:
    service-url:
      defaultZone: http://eurekatest8071.com:8071/eureka
新建config client Module

SpringCloud-REST-config-client-8052

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>SpringCloud-REST</artifactId>
        <groupId>per.xgt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringCloud-REST-config-client-8052</artifactId>

    <dependencies>
        <!-- SpringCloud Config客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</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-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</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-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

</project>
bootstrap.yml

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

SpringCloud会创建一个bootstrap Context作为Spring用用Application Context的父上下文。初始化的时候bootstrap Context负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取Environment。bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。bootstrap Context和Application Context有着不同的约定。所以新增了一个bootstrap.yml文件,保证BootStrap Context和Application Context配置的分离。

spring:
  cloud:
    config:
      name: springCloud-Config-client #需要从gitEE上读取的资源名称,注意没有yml后缀名
      profile: test   #本次访问的配置项
      label: master
      uri: http://config8051.com:8051  #本微服务启动后先去找8051号服务,通过SpringCloudConfig获取GitEE的服务地址
修改hosts文件增加映射
127.0.0.1		configclient.com
测试获取
package per.xgt.test;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/12 11:45
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class Test {

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaServers;

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

    @RequestMapping("/config")
    public String getConfig()
    {
        String str = "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
        System.out.println("******str: " + str);
        return "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
    }

}

主启动类
package per.xgt;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/12 11:44
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
public class SpringCloud_REST_Config_Client_8052_APP {

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

}

启动测试client从server获取配置信息

访问:http://config8051.com:8051/application-test.yml

spring:
  application:
    name: SpringCloud-REST-Config-test
  profiles:
    active:
    - dev

访问:http://configclient.com:8002/config

applicationName: SpringCloud-Config-client eurekaServers:http://eurekatest8071.com:8071/eureka port: 8002

config配置与策略切换

新建SpringCloud-config-eureka-client.yml
spring:
  profiles:
    active:
      - dev
---
server:
  port: 8071

spring:
  profiles: dev
  application:
    name: springCloud-config-eureka-client

eureka:
  instance:
    hostname: eureka8071.com #yml文件冒号后面一定要有空格
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://eureka8071.com:8071/eureka

---
server:
  port: 8072

spring:
  profiles: test
  application:
    name: springCloud-config-eureka-client

eureka:
  instance:
    hostname: eureka8072.com #yml文件冒号后面一定要有空格
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://eureka8072.com:8072/eureka
新建SpringCloud-config-dept-client.yml
spring:
  profiles:
    active:
      - dev
---
server:
  port: 8081

spring:
  profiles: dev
  application:
    name: springCloud-config-dept-client
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource      # 当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver       # mysql驱动包
    url: url       # 数据库链接url
    username: root
    password: root
    dbcp2:
      min-idle: 5                                     # 数据库连接处最小维持连接数
      initial-size: 5                                 # 初始化连接数
      max-total: 20                                   # 最大连接数
      max-wait-millis: 200                            # 等待连接获取的最大超时时间
mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  type-aliases-package: per.xgt.entity
  mapper-locations:
    - classpath:mybatis/mapper/**/*.xml

eureka:
  client:
    service-url:
      defaultZone: http://eureka8071.com:8071/eureka
  instance:
    instance-id: dept-8081.com
    prefer-ip-address: true
info:
  app.name: xgt-springCloud-config-dept-client1
  company.name: www.xgt.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$
---
server:
  port: 8082

spring:
  profiles: test
  application:
    name: springCloud-config-dept-client
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource      # 当前数据源操作类型
    driver-class-name: com.mysql.cj.jdbc.Driver       # mysql驱动包
    url: url       # 数据库链接url
    username: root
    password: root
    dbcp2:
      min-idle: 5                                     # 数据库连接处最小维持连接数
      initial-size: 5                                 # 初始化连接数
      max-total: 20                                   # 最大连接数
      max-wait-millis: 200                            # 等待连接获取的最大超时时间
mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  type-aliases-package: per.xgt.entity
  mapper-locations:
    - classpath:mybatis/mapper/**/*.xml

eureka:
  client:
    service-url:
      defaultZone: http://eureka8072.com:8072/eureka
  instance:
    instance-id: dept-8082.com
    prefer-ip-address: true
info:
  app.name: xgt-springCloud-config-dept-client2
  company.name: www.xgt.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$
eureka主启动类
package per.xgt;

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

/**
 * @Author: gentao9527
 * @CreateTime: 2022/8/12 14:01
 * @Description: TODO
 * @Version: 1.0
 */
@SpringBootApplication
@EnableEurekaServer
public class SpringCloud_REST_config_eureka_8071_APP {

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

}

eureka bootstrap.yml文件
spring: 
  cloud: 
    config: 
      name: SpringCloud-REST-config-eureka-client     #需要从github上读取的资源名称,注意没有yml后缀名
      profile: dev 
      label: master 
      uri: http://config8051.com:8051      #SpringCloudConfig获取的服务地址

启动configServer 再 启动eureka client,访问http://eureka8071.com:8071/出现eureka主页表示成功

新建dept-config Module

SpringCloud-REST-config-provider-dept-8081

其他相关启动类与业务类代码与前面8071一致
bootstrap.yml
spring:
  cloud:
    config:
      name: SpringCloud-config-dept-client     #需要从github上读取的资源名称,注意没有yml后缀名
      profile: dev
      label: master
      uri: http://config8051.com:8051      #SpringCloudConfig获取的服务地址

启动配置中心>eureka>provider 访问:http://localhost:8081/dept/list看到数据对应数据库

[{"id":1,"deptName":"开发部","db_source":"mybatis2"},{"id":2,"deptName":"测试部","db_source":"mybatis2"},{"id":3,"deptName":"运维部","db_source":"mybatis2"},{"id":4,"deptName":"营销部","db_source":"mybatis2"}]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值