SpringCloud笔记

仅为学习记录,方便回顾复习,如有侵权请联系删除!

Spring Cloud

目录

文章目录

1、常见面试题

1.1 什么是微服务?

1.2 微服务之间如何独立通讯?

1.3 SpringCloud和Dubbo有哪些区别?

1.4 SpringBoot和SpringCloud,谈谈对它们的理解。

1.5 什么是服务熔断?什么是服务降级?

1.6 微服务的优缺点分别是什么?说一下在项目上遇到的坑?

1.7 你所知道的微服务的技术栈有哪些?列举

1.8 eureka和zookeeper都可以提供服务注册与发现的功能,说说这两个的区别?

2、微服务概述

2.1 什么是微服务

image-20220722102732757

2.2 微服务与微服务架构

微服务

强调的是服务的大小,关注的是某一个点,具体解决某一个问题/提供一个落地的服务实现,可以狭义地理解为IDEA中的一个个微服务工程,或者Module

  1. IDEA 工具里使用Maven开发的一个个独立的小Module,具体是用springboot开发的一个小模块,专业的事交给专业的模块来做,一个模块就做一件事情。
  2. 强调的是一个个的个体,每个个体完成一个具体的任务或者功能。

微服务架构

image-20220722104224992

2.3 微服务的优缺点

优点

image-20220722104322735

缺点

image-20220722104620865

2.4 微服务技术栈有哪些

微服务条目落地技术
服务开发SpringBoot、Spring、SpringMVC
服务配置与管理Netflix公司的Achaius、阿里的Diamond等
服务注册于发现Eureka、Consul、Zookeeper等
服务调用Rest、RPC、gRPC
服务熔断器Hystrix、Envoy等
负载均衡Ribbon、Nginx等
服务接口调用(客户端调用服务的简化工具)Feign等
消息队列Kafka、RabbitMQ、ActiveMQ等
服务配置中心管理SpringCloudConfig、Chef等
服务路由(API网关)Zuul等
服务监控Zabbix、Nagios、Metrics、Specatator等
全链路追踪Zipkin、Brave、Dapper等
服务部署Docker、OpenStack、Kubernetes等
数据流操作开发包SpringCloud Stream(封装与Redis, Rabbit, Kafka等发送接收消息)
事件消息总栈SpringCloud Bus

2.5 为什么要选SpringCloud作为微服务架构

1、选型依据**

  • 整体解决方案和框架成熟度
  • 社区热度
  • 可维护性
  • 学习曲线

2、当前各大IT公司用的微服务架构有哪些?

  • 阿里:dubbo+HFS
  • 京东:JSF
  • 新浪:Motan
  • 当当网 DubboX

3、各服务框架对比

image-20220722105659789 image-20220722105831733

3、SpringCloud入门概述

3.1 SpringCloud是什么?

image-20220722110106023

3.2 SpringCloud和SpringBoot关系

image-20220722110434387

3.3 Dubbo和SpringCloud技术选型

1、分布式+服务治理Dubbo
目前成熟的互联网结构:应用服务化拆分 + 消息中间件

image-20220722111246267

2、Dubbo 和 SpringCloud 对比

image-20220722111339860

结果:

DubboSpring
服务注册中心ZookeeperSpring Cloud Netflix Eureka
服务调用方式RPCREST API
服务监控Dubbo-monitorSpring Boot Admin
断路器不完善Spring Cloud Netflix Hystrix
服务网关Spring Cloud Netflix Zuul
分布式配置Spring Cloud Config
服务跟踪Spring Cloud Sleuth
消息总栈Spring Cloud Bus
数据流Spring Cloud Stream
批量任务Spring Cloud Task

image-20220722111744604

image-20220722112011947

3.4 SpringCloud能干什么

image-20220722112353614

3.5 SpringCloud下载

官网:http://projects.spring.io/spring-cloud/

版本号解释

image-20220722112654439
  1. Spring Cloud 是一个由众多独立子项目组成的大型综合项目,每个子项目有不同的发行节奏,都维护着自己的发布版本号。Spring Cloud 通过一个资源清单BOM (Bill of Materials) 来管理每个版本的子项目清单。为避免与子项目的发布号混淆,所以没有采用版本号的方式,而是通过命名的方式。
  2. 这些版本名称的命名方式采用了伦敦地铁站的名称,同时根据字母的顺序来对应版本时间顺序,比如:最早的Release版本:Angel,第二个Release版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Finchley版本。

参考书:

  • https://springcloud.cc/spring-cloud-netflix.html
  • 中文API文档:https://springcloud.cc/spring-cloud-dalston.html
  • SpringCloud中国社区 http://springcloud.cn/
  • SpringCloud中文网 https://springcloud.cc

4、Rest 学习环境搭建 : 服务提供者

4.1 介绍

image-20220722133227454

4.2 SpringCloud版本选择

image-20220722133316298

image-20220722133345006

4.3 创建父工程 springcloud

新建普通maven工程,不需要根据模板建立。

image-20220722133740214

springcloud的就是整个父工程,src文件夹可以删掉。

下面编辑springcloud工程的pom.xml文件

\springcloud\pom.xml

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

    <groupId>com.chw</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--打包方式 pom-->
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <junit.version>4.13.2</junit.version>
        <lombok.version>1.18.12</lombok.version>
        <slf4j-api.version>1.7.25</slf4j-api.version>
        <logback-core.version>1.2.3</logback-core.version>
        <logback-classic.version>1.2.3</logback-classic.version>
    </properties>

    <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.1.9.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.28</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.2.8</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>
            <!--logback日志-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j-api.version}</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>${logback-core.version}</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${logback-classic.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

创建数据库及表格

image-20220722143047827

创建表格插入数据

image-20220722144230967

image-20220722144209810

创建子模块:springcloud-api

image-20220722145843417

下面new一个普通module命名为springcloud-api,它的父工程是springcloud。

编辑pom.xml文件

\springcloud\springcloud-api\pom.xml

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

    <artifactId>springcloud-api</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <!--当前的module自己需要的依赖,如果父依赖中已经配置了版本,这里就不用写了-->
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>
创建实体类

注意:网络通信要求所有的实体类必须实现序列化。

com/chw/springcloud/pojo/Dept.java

package com.chw.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;

@Data
@NoArgsConstructor
@Accessors(chain = true) //开启链式写法
public class Dept implements Serializable { //Dept 实体类,orm对象关系映射,类表关系映射

    private Long deptno;//主键
    private String dname;
    //这个数据属于哪个数据库。一个服务对应一个数据库,同一个信息可能存在不同的数据库
    private String db_source;

    //构造器只需要一个参数,因为主键自增,db_source是通过函数得到的
    public Dept(String dname) {
        this.dname = dname;
    }

    /*
    链式写法:
        Dept dept = new Dept();
        dept.setDeptno(11).setDname("xiaoming").setDb_source("001")
    */
}

到这里子模块springcloud-api就写完了,这个模块拆分出来的目的就是只管pojo。

创建子模块 : 服务提供者

模块名称:springcloud-provider-dept-8001

把端口号加上,以后项目越来越多时方便管理。

image-20220722162925537

1、编写pom.xml文件

引入依赖
\springcloud\springcloud-provider-dept-8001\pom.xml

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

    <artifactId>springcloud-provider-dept-8001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--需要拿到实体类,所以要配置api模块,引入刚刚写的模块-->
        <dependency>
            <groupId>com.chw</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>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!--日志-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <!--mybaits springboot-->
        <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>
        <!--web相关-->
        <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>
        <!--tomcat,上面的jetty可以换成tomcat-->
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>-->
        <!--热部署工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
</project>
2、写配置文件 *.yml 、*.xml

依赖写完下一步写配置

resources/application.yml

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.chw.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

#spring的配置
spring:
  application:
    name: springcloud-provider-dept  #spring项目起个名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 9527

把mybatis配置补齐

resources/mybatis/mybatis-config.xml

<?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>
3、写接口及映射文件(DAO层)

com/chw/springcloud/mapper/DeptMapper.java

package com.chw.springcloud.mapper;
import com.chw.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;

@Mapper
@Repository
public interface DeptMapper {
    public boolean addDept(Dept dept);
    public Dept queryById(Long id);
    public List<Dept> queryAll();
}

resources/mybatis/mapper/DeptMapper.xml

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

    <!--public boolean addDept(Dept dept);-->
    <insert id="addDept" parameterType="Dept">
        insert into dept (dname, db_source) values (#{dname},DATABASE())
    </insert>

    <!--public Dept queryById(Long id);-->
    <select id="queryById" resultType="Dept" parameterType="Long">
        select * from dept where deptno = #{id}
    </select>

    <!--public List<Dept> queryAll();-->
    <select id="queryAll" resultType="Dept">
        select * from dept
    </select>

</mapper>
4、写service层

com/chw/springcloud/service/DeptService.java

package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import java.util.List;

public interface DeptService {
    public boolean addDept(Dept dept);
    public Dept queryById(Long id);
    public List<Dept> queryAll();
}

com/chw/springcloud/service/DeptServiceImpl.java

package com.chw.springcloud.service;
import com.chw.springcloud.mapper.DeptMapper;
import com.chw.springcloud.pojo.Dept;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class DeptServiceImpl implements DeptService {
    @Resource
    private DeptMapper deptMapper;
    @Override
    public boolean addDept(Dept dept) {
        return deptMapper.addDept(dept);
    }
    @Override
    public Dept queryById(Long id) {
        return deptMapper.queryById(id);
    }
    @Override
    public List<Dept> queryAll() {
        return deptMapper.queryAll();
    }
}
5、写controller层 (提供RESTful服务)

com/chw/springcloud/controller/DeptController.java

package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import com.chw.springcloud.service.DeptService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;

//提供RESTful服务
@RestController
public class DeptController {
    @Resource
    private DeptService deptService;

    @PostMapping("/dept/add")
    public boolean addDept(@RequestBody Dept dept) {
        return deptService.addDept(dept);
    } //如果前端传来的是json值,那么就要通过@RequestBody来将请求体转换为对象
    //由于后面是用消费端转发请求过来调用,转来的请求体是json格式,所以这里加了@RequestBody
    //如果单独启动服务模块单独调用这个控制方法,则不能加@RequestBody

    @GetMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return deptService.queryById(id);
    }
    
    @GetMapping("/dept/list")
    public List<Dept> queryAll() {
        return deptService.queryAll();
    }
}
6、写主启动类

com/chw/springcloud/DeptProvider_8001.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//启动类
@SpringBootApplication
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class, args);
    }
}

启动主程序,用postman测试。

创建子模块:服务消费者

模块名称:springcloud-consumer-dept-80

消费方一般放在80端口,只要用了80端口,写请求的时候就不用带上端口号。

1、写pom.xml文件(导包)

消费者不需要连数据库,只需要实体类和web

springcloud\springcloud-consumer-dept-80\pom.xml

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

    <artifactId>springcloud-consumer-dept-80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--实体类——web-->
        <dependency>
            <groupId>com.chw</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
</project>
2、写配置文件 *.yml

resources/application.yml

server:
  port: 80
3、写消费者相关类
配置类

把 RestTemplate 注册成bean让spring管理

com/chw/springcloud/config/ConfigBean.java

package com.chw.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

//@Configuration  --相当于-- spring applicationContext.xml,原先就是在里面配bean
@Configuration
public class ConfigBean {
    @Bean
    public RestTemplate getRestTemplate() { return new RestTemplate(); }
}
Controller层

com/chw/springcloud/controller/DeptConsumerController.java

package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;

@Controller
public class DeptConsumerController {
    // 理解:消费者模块是不应该有service层的,怎么拿到service层呢?怎么取到url
    // springboot支持RESTful风格。RestTemplate ... 可供我们直接调用,需要把它注册到spring中。
    // RestTempate的重要参数 ( url , 实体 Map保存 , Class<T> responseType返回类型 )
    @Autowired
    private RestTemplate restTemplate; //提供多种便捷访问远程http服务的方法,简单的RESTful服务模板

    //远程服务的地址一般是固定的,所以可以写一个常量
    private static final String REST_URL_PREFIX = "http://localhost:8001";

    //调用远程的服务 http://localhost:8001/dept/get/{id}
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        //服务端的请求方式是get,约束了这里的restTemplate.getForObject。请求方式是post,则restTemplate.postForObject
        return restTemplate.getForObject(REST_URL_PREFIX+"dept/get/"+id,Dept.class);
    }

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

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

下面写主启动类测试

com/chw/springcloud/DeptConsumer_80.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

先启动提供方的启动类(启动完先自测一下本地能不能访问到服务)

再启动消费方的启动类(http://localhost/consumer/dept/list)

5、Eureka服务注册与发现

5.1 什么是Eureka

image-20220723105129549

5.2 原理讲解

image-20220723110730617

image-20220723111130430 image-20220723111154253 image-20220723111204590

image-20220723111221361

下面开始写Eureka的注册中心

创建子模块-Eureka注册中心

命名为springcloud-eureka-7001

第一步:导入依赖

springcloud\springcloud-eureka-7001\pom.xml

<!--导入依赖-->
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--热部署工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>
第二步:配置文件

resources/application.yml

#端口号
server:
  port: 7001

#Eureka配置
eureka:
  instance:
    hostname: localhost   #Eureka服务端的实例名称
  client:
    register-with-eureka: false   #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
    fetch-registry: false  #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
    service-url:  #监控页面
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
第三步:写主启动类,添加注解启动服务

com/chw/springcloud/EurekaServer_7001.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer  //用于标注服务端的启动类,使可以接收别人注册
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class, args);
    }
}

启动后访问http://localhost:7001/就可以进入监控页面

(注意http:// e u r e k a . i n s t a n c e . h o s t n a m e : {eureka.instance.hostname}: eureka.instance.hostname:{server.port}/eureka/,是用于注册时候用的)

image-20220723114131280

如何把提供者注册到Eureka里

第一步:导入依赖

在springcloud-provider-dept-8001这个module里的pom.xml引入Eureka的依赖

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
第二步:配置文件

下一步把服务注册进去,要在yml中配置Eureka

springcloud-provider-dept-8001/src/main/resources/application.yml

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.chw.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

#spring的配置
spring:
  application:
    name: springcloud-provider-dept  #spring项目起个名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 9527

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
第三步:写主启动类,添加注解启动服务

springcloud-provider-dept-8001/src/main/java/com/chw/springcloud/DeptProvider_8001.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//启动类
@SpringBootApplication
@EnableEurekaClient     //添加这个注解之后就会自动把提供者注册到Eureka中了
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class, args);
    }
}

下面进行测试

先启动7001,再启动8001

浏览器访问http://localhost:7001/就可以进入监控页面。

image-20220724060531879

image-20220724060551677

可以看到监控页面中已经出现了提供者的yml里设置的项目名称。

修改Status的标题内容

下面要自定义一下这个Status

image-20220724060817913

springcloud-provider-dept-8001/src/main/resources/application.yml

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: springcloud-provider-dept8001  #修改Eureka上的默认Status信息

修改完之后重新运行项目访问监控中心查看

image-20220724061248258

补充服务提供者的监控信息

下面要去补充这个页面,配置关于服务加载的监控信息

image-20220724061752413

第一步:导入依赖

springcloud-provider-dept-8001/pom.xml

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

第二步:配置文件

springcloud-provider-dept-8001/src/main/resources/application.yml

#info配置
info:
  app.name: Hyrule does not have shield back
  company.name: breath.wild.zelda

启动项目测试,访问监控页面 localhost:7001

image-20220724063005721

image-20220724063031837

服务不可用时 Eureka的自我保护机制

image-20220724063326710

提供者的控制器方法——获取微服务列表的清单

主要应用于团队协作开发中。

控制器方法

springcloud-provider-dept-8001/src/main/java/com/chw/springcloud/controller/DeptController.java

.
.
.
import org.springframework.cloud.client.discovery.DiscoveryClient;
.
.
.
//这个类可以获取一些配置的信息,得到具体的微服务
@Autowired
private DiscoveryClient client;

//从注册进来的微服务里获取一些消息
@GetMapping("/dept/discovery")
public Object discovery() {
    //获取微服务列表的清单
    List<String> services = client.getServices();
    System.out.println("services = " + services);//观察控制台输出

    //得到一个具体的微服务信息,通过具体的微服务id,即spring.application.name
    List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
    for (ServiceInstance instance : instances) {
        System.out.println(
            instance.getHost() + "\t" +
            instance.getPort() + "\t" +
            instance.getUri() + "\t" +
            instance.getServiceId()
        );
    }
    return this.client;
}

启动类添加注解

springcloud-provider-dept-8001/src/main/java/com/chw/springcloud/DeptProvider_8001.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//启动类
@SpringBootApplication
@EnableEurekaClient     //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient  //服务发现
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class, args);
    }
}

重新启动项目,访问http://localhost:8001/dept/discovery查看结果

image-20220724071647193

5.? 集群环境配置(多个注册中心)

创建多个注册中心

创建两个子模块

springcloud-eureka-7002

springcloud-eureka-7003

第一步:导入依赖

把springcloud-eureka-7001的pom.xml中的依赖复制给它们

<!--导入依赖-->
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--热部署工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>
第二步:配置文件

分别配置springcloud-eureka-7002、springcloud-eureka-7003的yml配置文件

springcloud-eureka-7002/src/main/resources/application.yml

server:
  port: 7002

#Eureka配置
eureka:
  instance:
    hostname: localhost #Eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
    fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
    service-url: #监控页面
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

springcloud-eureka-7003/src/main/resources/application.yml

server:
  port: 7003

#Eureka配置
eureka:
  instance:
    hostname: localhost #Eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
    fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
    service-url: #监控页面
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
第三步:写主启动类,添加注解启动服务

分别创建springcloud-eureka-7002、springcloud-eureka-7003的主启动类

springcloud-eureka-7002/src/main/java/com/chw/springcloud/EurekaServer_7002.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

springcloud-eureka-7003/src/main/java/com/chw/springcloud/EurekaServer_7003.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

多个注册中心互相绑定形成集群

image-20220724080349301

为了演示效果,先把C:\Windows\System32\drivers\etc中的hosts文件中添加三个默认地址映射

hosts

127.0.0.1 localhost

#2022-7-24 edit
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
配置文件中defaultZone互相关联

springcloud-eureka-7001/src/main/resources/application.yml

#端口号
server:
  port: 7001

#Eureka配置
eureka:
  instance:
    hostname: localhost   #Eureka服务端的实例名称
  client:
    register-with-eureka: false   #表示是否向eureka中心注册自己,服务器就不用把自己注册进去了。(不做服务提供者)
    fetch-registry: false  #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
    service-url:  #监控页面
      # 如果是单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 如果是集群(关联):
      defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

springcloud-eureka-7002/src/main/resources/application.yml

server:
  port: 7002

#Eureka配置
eureka:
  instance:
    hostname: localhost #Eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
    fetch-registry: false  #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
    service-url: #监控页面
      # 如果是单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 如果是集群(关联):
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/

springcloud-eureka-7003/src/main/resources/application.yml

server:
  port: 7003

#Eureka配置
eureka:
  instance:
    hostname: localhost #Eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否向eureka中心注册,服务器就不用把自己注册进去了,填false。(不做服务提供者)
    fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。(不做消费者)
    service-url: #监控页面
      # 如果是单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 如果是集群(关联):
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/

启动springcloud-eureka-7001、springcloud-eureka-7002、springcloud-eureka-7003的主启动类测试,

分别访问localhost:7001、localhost:7002、localhost:7003。

这里我的浏览器页面并没有显示互相关联的效果

下面将服务提供者注册到三个注册中心

springcloud-provider-dept-8001/src/main/resources/application.yml

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.chw.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

#spring的配置
spring:
  application:
    name: springcloud-provider-dept  #spring项目起个名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 1113

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      #注册到多个服务中心
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept8001  #修改Eureka上的默认Status信息

#info配置
info:
  app.name: Hyrule does not have shield back
  company.name: breath.wild.zelda

5.5 对比Zookeeper

image-20220724094350386

image-20220724094615564

image-20220724094701354

image-20220724095038454

6、Ribbon 负载均衡

第10集:负载均衡及Ribbon

image-20220724100011516

image-20220724100857333

实现客户端(消费端)集成Ribbon

第一步:导入依赖。

springcloud-consumer-dept-80/pom.xml

<!--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.7.RELEASE</version>
</dependency>
第二步:编写配置。

springcloud-consumer-dept-80/src/main/resources/application.yml

server:
  port: 80

#Eureka配置
eureka:
  client:
    register-with-eureka: false  #不做服务提供者
    service-url:  #可以从3个地方取到服务
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
第三步:启动类注解@EnableEurekaClient

springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/DeptConsumer_80.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//Ribbon 和 Eureka 整合后,客户端可以直接调用,不用关心服务端的ip地址和端口号。
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}
第四步:给RestTemplate添加负载均衡注解

springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/config/ConfigBean.java

package com.chw.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

//@Configuration  --相当于-- spring applicationContext.xml,原先就是在里面配bean
@Configuration
public class ConfigBean {
    @Bean
    @LoadBalanced //Ribbon。为RestTemplate配置负载均衡
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
第五步:修改controller中的服务访问地址

springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/controller/DeptConsumerController.java

package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;

@RestController
public class DeptConsumerController {
    @Autowired
    private RestTemplate restTemplate; 

    //private static final String REST_URL_PREFIX = "http://localhost:8001";
    //应用了Ribbon后,这里的地址应该为变量。通过服务名SPRINGCLOUD-PROVIDER-DEPT来访问
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX+"dept/get/"+id,Dept.class);
    }
    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }
    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }
}

启动7001、7002、7003、8001、80。测试访问http://localhost/consumer/dept/list,成功。

image-20220724104034396

总结:Ribbon 和 Eureka 整合后,客户端可以直接调用服务,不用关心服务端的ip地址和端口号。

第11集:使用Ribbon实现负载均衡

准备多个服务提供者

下面要实现多个服务提供者。展示负载均衡的效果。

image-20220724112903600

有个问题,这些服务提供者要注册到哪个注册中心?根据什么原则进行分配注册?

可能原因:每个服务提供者都注册到所有注册中心,多个注册中心是为了保证总有能够正常运行的注册中心。

创建数据库

image-20220724114203148

创建三个提供者。已经有一个8001了,还差8002和8003

image-20220724114515674

8002和8003的依赖

把8001的pom.xml里的依赖全部复制给8002和8003

<dependencies>
    <!--完善监控信息-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--导入Eureka依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--需要拿到实体类,所以要配置api模块,引入刚刚写的模块-->
    <dependency>
        <groupId>com.chw</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>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
    </dependency>
    <!--mybaits springboot-->
    <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>
    <!--web相关-->
    <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>
    <!--热部署工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>
8002和8003的配置文件

springcloud-provider-dept-8002/src/main/resources/application.yml

server:
  port: 8002

#mybatis配置
mybatis:
  type-aliases-package: com.chw.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

#spring的配置
spring:
  application:
    name: springcloud-provider-dept  #spring项目起个名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db02?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 9527

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept8002  #修改Eureka上的默认Status信息

#info配置
info:
  app.name: Hyrule does not have shield back
  company.name: breath.wild.zelda

springcloud-provider-dept-8003/src/main/resources/application.yml

server:
  port: 8003

#mybatis配置
mybatis:
  type-aliases-package: com.chw.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

#spring的配置
spring:
  application:
    name: springcloud-provider-dept  #spring项目起个名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db03?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 9527

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept8003  #修改Eureka上的默认Status信息

#info配置
info:
  app.name: Hyrule does not have shield back
  company.name: breath.wild.zelda

注意:这其中,spring的配置的名称项,要求三个服务提供者都是一致的,这是前提。

spring:
  application:
    name: springcloud-provider-dept  # 8001 8002 8003 三者的名称保持一致
8002和8003的其他文件

resources中的mybatis文件夹以及里面的xml文件。

java中的包和类。

8001、8002、8003三个模块几乎相同,除了数据库不一样之外。

启动测试负载均衡

注册中心:7001、7002、7003

服务提供者:8001、8002、8003

消费端:80

image-20220724121211411

上图能看到3个服务都注册进来了。这就是获取到的服务的列表。

获取服务列表后,通过一种负载均衡算法来决定到底访问谁。

下面浏览器访问测试

第一次访问http://localhost/consumer/dept/list

image-20220724121540747

第二次访问http://localhost/consumer/dept/list

image-20220724121649399

第三次访问http://localhost/consumer/dept/list

image-20220724121712669

看到数据源从db01到db03再到db02,这就是Ribbon默认的轮询机制。

第12集:自定义负载均衡算法

Ribbon对负载均衡的规则设置有个接口 IRule ,有以下实现类:

  • RoundRobinRule : 默认的策略,轮询;
  • AvailabilityFilteringRule : 过滤跳闸的服务,对剩下的进行轮询;
  • RandomRule : 随机;
  • RetryRule : 会先轮询获取服务,如果服务获取失败,则在指定时间内重试;

测试随机算法取代轮询算法

下面试一下把负载均衡算法设置成随机,取代默认的轮询算法。

springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/config/ConfigBean.java

package com.chw.springcloud.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

//@Configuration  --相当于-- spring applicationContext.xml,原先就是在里面配bean
@Configuration
public class ConfigBean {
    @Bean
    @LoadBalanced //Ribbon。为RestTemplate配置负载均衡
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule myRule() {
        return new RandomRule();
    }
}

重新启动测试

注册中心:7001、7002、7003

服务提供者:8001、8002、8003

消费端:80

看看list的获取的结果中,数据源是不是随机取的。

自定义负载均衡算法

先把刚刚定义的随机算法删去,因为不应该在那里面设定自定义的负载均衡算法。

1、重写IRule的实现类

自己重写一个负载均衡算法。把RandomRule复制过来重写,并重命名为ChwRandomRule

springcloud-consumer-dept-80/src/main/java/com/chw/myrule/ChwRandomRule.java

package com.chw.myrule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
//import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class ChwRandomRule extends AbstractLoadBalancerRule {
    public ChwRandomRule(){}
    //现在想让每个服务访问5次,换下一个服务(一共有3个服务)。
    //total=0, 默认为0,若=5,指向下一个服务节点
    //index=0, 如果total=5,index+1
    private int total = 0; //一个服务被调用的次数。
    private int currentIndex = 0; //当前是谁在提供服务。

    //@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 (total < 5) {
                    server = upList.get(currentIndex);
                    total++;
                } else {
                    total = 0;
                    currentIndex++;
                    if (currentIndex > upList.size()) {
                        currentIndex = 0;
                    }
                    server = upList.get(currentIndex); //从可用的服务中,获取指定的服务来操作
                }

                //======================================

                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) {
    }
}
2、写配置类

注意自己写的Ribbon配置组件不能和主启动类同级,会被@ComponentScan扫描到,所以要单独放在另外一个包里。

也就是这里的ChwRule.java要放在com.chw.myrule下,而不是放在com.chw.springcloud下。

image-20220725072438203

springcloud-consumer-dept-80/src/main/java/com/chw/myrule/ChwRule.java

package com.chw.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChwRule {
    @Bean
    public IRule myRule() {
        return new ChwRandomRule();//默认是轮询,现在自定义为了ChwRandomRule
    }
}
3、主启动类添加注解

去主动类添加注解使自定义的Ribbon类生效

springcloud-consumer-dept-80/src/main/java/com/chw/springcloud/DeptConsumer_80.java

package com.chw.springcloud;
import com.chw.myrule.ChwRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

//Ribbon 和 Eureka 整合后,客户端可以直接调用,不用关心服务端的ip地址和端口号。
@SpringBootApplication
@EnableEurekaClient
//在微服务启动时就去加载自定义的Ribbon类
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = ChwRule.class)
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

启动测试即可。

7、Feign 负载均衡

image-20220725084754182

准备工作

新建一个客户端module,命名为 springcloud-consumer-dept-feign

把springcloud-consumer-dept-80的依赖拷贝过来,另外加上Feign的依赖

(在springcloud-consumer-dept-feign模块和springcloud-api模块都加上)

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

在springcloud-consumer-dept-feign模块中创建 包 com.chw.springcloud;

把springcloud-consumer-dept-80的config包和controller包都拷贝过来;

把springcloud-consumer-dept-80的application.yml拷贝过来;

创建主启动类

springcloud-consumer-dept-feign/src/main/java/com/chw/springcloud/FeignDeptConsumer_80.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

image-20220725090237480

创建服务支持接口

在模块springcloud-api中创建包 com.chw.springcloud.service

创建服务接口

springcloud-api/src/main/java/com/chw/springcloud/service/DeptClientService.java

package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;

@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT") //value值写微服务的名字
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")  //这里的映射地址要和服务端的controller方法的映射地址保持一致
    public Dept queryById(@PathVariable("id") Long id);

    @GetMapping("/dept/list") //这里的映射地址要和服务端的controller方法的映射地址保持一致
    public List<Dept> queryAll();

    @PostMapping("/dept/add") //这里的映射地址要和服务端的controller方法的映射地址保持一致
    public Boolean addDept(Dept dept);
}

消费端写服务调用的controller方法

切换至springcloud-consumer-dept-feign模块,写controller方法

springcloud-consumer-dept-feign/src/main/java/com/chw/springcloud/controller/DeptConsumerController.java

package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import com.chw.springcloud.service.DeptClientService;
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 java.util.List;

@RestController
public class DeptConsumerController {
    //把springcloud-api中写的服务支持接口注入进来
    @Autowired
    private DeptClientService service = null;

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

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

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

主启动类添加注解

springcloud-consumer-dept-feign/src/main/java/com/chw/springcloud/FeignDeptConsumer_80.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.chw.springcloud"}) //激活feign及指定扫描范围
public class FeignDeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(FeignDeptConsumer_80.class, args);
    }
}

Feign默认集成Ribbon,底层还是Ribbon,只是通过封装来简化了写法,更符合人们的开发习惯。

启动测试负载均衡

注册中心:7001、7002、7003

服务提供者:8001、8002、8003

消费端:FeignDeptConsumer_80

http://localhost/consumer/dept/list,测试成功,默认是轮询。

8、Hystrix 服务熔断

第14集:Hystrix 服务熔断

Hystrix官网资料:https://github.com/Netflix/Hystrix/wiki

image-20220725103618097

image-20220725104216731

image-20220725104245178

下面演示。

准备工作

新建一个模块 springcloud-provider-dept-hystrix-8001,这个模块将在springcloud-provider-dept-8001的基础上进行改动,整合Hystrix。

把springcloud-provider-dept-8001的依赖、代码、resources都复制给springcloud-provider-dept-hystrix-800,其中主启动类是唯一要改的。

写主启动类

springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/DeptProviderHystrix_8001.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//启动类
@SpringBootApplication
@EnableEurekaClient     //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient  //服务发现
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class, args);
    }
}

实现服务端整合Hystrix熔断机制

现在这个服务提供者是不具有服务熔断功能的,下面要加入服务熔断功能。

步骤还是:导入依赖、编写配置、开启功能

第一步:导入Hystrix依赖

springcloud-provider-dept-hystrix-8001/pom.xml

<!--hystrix依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
第二步:编写配置

修改自定义名称 eureka.instance.instance-id

springcloud-provider-dept-hystrix-8001/src/main/resources/application.yml

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.chw.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

#spring的配置
spring:
  application:
    name: springcloud-provider-dept  #spring项目起个名字
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource #数据源
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: 1113

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
  instance:
    #instance-id: springcloud-provider-dept8001  #修改Eureka上的默认Status信息
    instance-id: springcloud-provider-dept-hystrix-8001 #修改Eureka上的默认Status信息

#info配置
info:
  app.name: Hyrule does not have shield back
  company.name: breath.wild.zelda
第三步:编写一些功能

在controller中应用@HystrixCommand注解实现熔断机制

springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/controller/DeptController.java

package com.chw.springcloud.controller;
import com.chw.springcloud.pojo.Dept;
import com.chw.springcloud.service.DeptService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

//提供RESTful服务
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;

    @GetMapping("/dept/get/{id}")
    @HystrixCommand(fallbackMethod = "hystrixGet")
    public Dept get(@PathVariable("id") Long id) {
        Dept dept = deptService.queryById(id);
        if (dept == null) {
            throw new RuntimeException("id 为 " + id + " 的用户不存在,或信息无法找到");
        }
        return dept;
    }

    //fallback方法
    public Dept hystrixGet(@PathVariable("id") Long id) {
        return new Dept()
                .setDeptno(id)
                .setDname("id 为 " + id + " 的信息不存在,null——@Hystrix")
                .setDb_source("database is not exist");
    }
}
第四步:主启动类添加注解支持熔断

springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/DeptProviderHystrix_8001.java

package com.chw.springcloud;
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 chw1113
 * @create 2022-07-25 10:55
 */
//启动类
@SpringBootApplication
@EnableEurekaClient     //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient  //服务发现
@EnableCircuitBreaker   //开启断路器,使支持熔断
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class, args);
    }
}
启动测试

注册中心:7001、7002、7003

服务提供者:DeptProviderHystrix_8001

消费端:FeignDeptConsumer_80

http://localhost/consumer/dept/get/{id}

改变id测试。

小功能:显示服务提供方的ip

image-20220725114025951

yml中设置 eureka.instance.prefer-ip-address设置为true

springcloud-provider-dept-hystrix-8001/src/main/resources/application.yml

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
  instance:
    #instance-id: springcloud-provider-dept8001  #修改Eureka上的默认Status信息
    instance-id: springcloud-provider-dept-hystrix-8001
    prefer-ip-address: true #设置为true时可显示服务的ip地址

第15集:Hystrix 服务降级

1、编写服务降级接口的实现类

在模块springcloud-api中写一个失败回调接口的实现

springcloud-api/src/main/java/com/chw/springcloud/service/DeptClientServiceFallbackFactory.java

package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;

// 服务降级
@Component //注册到spring中
public class DeptClientServiceFallbackFactory implements FallbackFactory {
    //现在要停掉一个服务,所以返回的就是这个服务类。
    //之前的熔断处理中,崩溃的是查询方法,返回的类型就是查询方法的返回类型。
    @Override
    public DeptClientService create(Throwable throwable) {
        return new DeptClientService() {
            @Override
            public Dept queryById(Long id) {
                return new Dept()
                        .setDname("id:" + id + "没有对应的数据,客户端提供了服务降级的信息,这个服务现在已经关闭")
                        .setDeptno(id)
                        .setDb_source("not exists");
            }

            @Override
            public List<Dept> queryAll() { return null; }
            @Override
            public Boolean addDept(Dept dept) { return null; }
        };
    }
}

2、服务接口添加服务降级注解及属性

服务降级的操作写完之后,要和对应服务接口产生联系,就需要在该服务接口添加注解

springcloud-api/src/main/java/com/chw/springcloud/service/DeptClientService.java

package com.chw.springcloud.service;
import com.chw.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;

@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallbackFactory = DeptClientServiceFallbackFactory.class)
//value值写微服务的名字
//fallbackFactory属性写服务降级操作的类
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")  //这里的映射地址要和服务端的controller方法的映射地址对应
    public Dept queryById(@PathVariable("id") Long id);

    @GetMapping("/dept/list") //这里的映射地址要和服务端的controller方法的映射地址保持一致
    public List<Dept> queryAll();

    @PostMapping("/dept/add") //这里的映射地址要和服务端的controller方法的映射地址保持一致
    public Boolean addDept(Dept dept);
}

3、在客户端的yml配置文件中开启服务降级

切换至模块springcloud-consumer-dept-feign,

springcloud-consumer-dept-feign/src/main/resources/application.yml

server:
  port: 80

#Eureka配置
eureka:
  client:
    register-with-eureka: false  #不做服务提供者
    service-url:  #可以从3个地方取到服务
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

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

注意到,刚才服务熔断的功能,是在服务端做的,而服务降级的功能,是在客户端(消费端)做的。

启动测试

注册中心:7001、7002、7003

服务提供者:springcloud-provider-dept-8001

消费端:FeignDeptConsumer_80

首先正常访问,http://localhost/consumer/dept/get/{id},顺利。

然后关闭FeignDeptConsumer_80,再次访问http://localhost/consumer/dept/get/{id},则被提示服务降级类里设置的信息。

服务熔断和服务降级的对比

服务熔断服务降级
编写位置服务端客户端
是啥某个服务超时或者异常,引起熔断从整体网站负荷考虑,当某个服务关闭后,服务将不再被调用。此时客户端可以通过FallbackFactory来设置缺省值。整体的服务水平下降了。但客户端的请求还是有响应的。

第16集:Hystrix 流监控 Dashboard

新建模块springcloud-consumer-hystrix-dashboard

1、导入依赖

导入消费端的相关依赖

springcloud-provider-dept-hystrix-8001/pom.xml

<dependencies>
    <!--Hystrix依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>    
    <!--Hystrix-dashboard依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--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.7.RELEASE</version>
    </dependency>
    <!--实体类——web-->
    <dependency>
        <groupId>com.chw</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-netflix-eureka-client</artifactId>
        <version>2.1.3.RELEASE</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

2、配置文件-指定监控页面端口号

写一下监控页面的端口号即可

springcloud-consumer-hystrix-dashboard/src/main/resources/application.yml

server:
  port: 9001

3、写主启动类-添加监控注解

建包com.chw.springcloud。创建主启动类

springcloud-consumer-hystrix-dashboard/src/main/java/com/chw/springcloud/DeptConsumerDashboard_9001.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard //开启HystrixDashboard
public class DeptConsumerDashboard_9001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumerDashboard_9001.class, args);
    }
}

既然有监控,那么服务端就必须得有监控相关的依赖。去检查,没有的话要补充

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

三个服务模块8001、8002、8003都检查过,之前已经添加了这个依赖。

启动主动类,访问http://localhost:9001/hystrix

image-20220725151640462

image-20220725151826081


image-20220725151902075

下面进行配置。

注意 Hystrix Dashboard 生效条件

注意:Hystrix Dashboard监控是要监控已经实现了熔断功能的服务模块。比如springcloud-provider-dept-hystrix-8001这个模块里的controller中的get方法添加了@HystrixCommand注解实现了熔断功能。稍后监控也是监控与这个get方法对应的请求。

4、在被监控的服务的主动类里添加servlet的bean

稍后要打开7001和springcloud-provider-dept-hystrix-8001测试,先在hystrix-8001主启动类里注册一个servlet的bean。

springcloud-provider-dept-hystrix-8001/src/main/java/com/chw/springcloud/DeptProviderHystrix_8001.java

package com.chw.springcloud;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

//启动类
@SpringBootApplication
@EnableEurekaClient     //添加这个注解之后就会自动把提供者注册到Eureka中了
@EnableDiscoveryClient  //服务发现
@EnableCircuitBreaker   //开启熔断支持
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class, args);
    }

    //增加一个用于监控的Servlet
    //这是段固定代码,用于配合监控
    @Bean
    public ServletRegistrationBean hystrixMetricsStreamServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
        return servletRegistrationBean;
    }
}
启动测试

启动7001、9001、DeptProviderHystrix_8001

访问http://eureka7001.com:7001/检查到服务已经注册进来。

访问http://localhost:8001/dept/get/1得到信息

访问http://localhost:8001/actuator/hystrix.stream看看监控流存不存在,会看到在不停地ping

image-20220725155140981

访问http://localhost:9001/hystrix,填入监控流地址http://localhost:8001/actuator/hystrix.stream、delay和Title,点击监控

image-20220725153717127 image-20220725155225526

不断刷新http://localhost:8001/dept/get/1,会看到监控页面的变化

image-20220725155418060

监控页面说明

image-20220725155606380

image-20220725155638514

image-20220725155659957

image-20220725155726056

9、Zuul路由网关

第17集:Zuul 路由网关

image-20220725161635921

1、引入依赖

新建模块springcloud-zuul-9527

把springcloud-consumer-hystrix-dashboard模块中的依赖复制过来,再加上zuul的依赖

springcloud-zuul-9527/pom.xml

<dependencies>
    <!--zuul-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--Hystrix依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--Hystrix-dashboard依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>
    <!--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.7.RELEASE</version>
    </dependency>
    <!--实体类——web-->
    <dependency>
        <groupId>com.chw</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-netflix-eureka-client</artifactId>
        <version>2.1.3.RELEASE</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

2、写yml配置文件

springcloud-provider-dept-8001/src/main/resources/application.yml

server:
  port: 9527

#这是一个应用,所以要有一个自己的名字
spring:
  application:
    name: springcloud-zuul

#这个应用要注册进Eureka,配置注册到哪个服务中心?
eureka:
  client:
    service-url:
      #注册到了三个服务中心,就表示可以拿到服务中心里其他的服务了
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
  instance:
    #表示Status栏目下的实例的id
    instance-id: zuul9527.com
    prefer-ip-address: true #显示真实ip

#info配置,点进去Status下面的实例后跳转页面的内容。选配
info:
  app.name: Hyrule does not have shield back
  company.name: breath.wild.zelda

为加强体验效果,

在C:\Windows\System32\drivers\etc的hosts文件里再添加一个默认域名映射

127.0.0.1 www.zuulgateway.com

3、写主启动类-添加注解@EnableZuulProxy

创建包com.chw.springcloud,创建主启动类

springcloud-zuul-9527/src/main/java/com/chw/springcloud/ZuulApplication_9527.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

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

启动测试

启动集群(注册中心):7001、7002、7003

启动服务端:8001

启动网关路由:9527

服务访问方式一:直接访问http://localhost:8001/dept/get/1,顺利。

服务访问方式二:http://www.zuulgateway.com:9527/springcloud-provider-dept/dept/get/1

即 http://{网关地址}:{网关端口}/{微服务名称小写}/请求路径

路由网关避免了真实地址的暴露问题

但是现在微服务的名字是被暴露的,下面进行优化,使它隐藏起来或者换成其他名字。

4、yml中配置zuul隐藏微服务名字

springcloud-provider-dept-8001/src/main/resources/application.yml

server:
  port: 9527

#这是一个应用,所以要有一个自己的名字
spring:
  application:
    name: springcloud-zuul

#这个应用要注册进Eureka,配置注册到哪个服务中心?
eureka:
  client:
    service-url:
      #注册到了三个服务中心,就表示可以拿到服务中心里其他的服务了
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
  instance:
    #表示Status栏目下的实例的id
    instance-id: zuul9527.com
    prefer-ip-address: true #显示真实ip

#info配置,点进去Status下面的实例后跳转页面的内容。选配
info:
  app.name: Hyrule does not have shield back
  company.name: breath.wild.zelda

#zuul配置
zuul:
  routes:
    #替换掉微服务的名字
    mydept.serviceId: springcloud-provider-dept
    mydept.path: /mydept/**

重启9527,再次测试

访问http://www.zuulgateway.com:9527/mydept/dept/get/1,成功。

这样就把真实的微服务的名字隐藏了。

但此时原来的路径还是可以访问到服务http://www.zuulgateway.com:9527/springcloud-provider-dept/dept/get/1,现在我们要使原路径不能访问服务。

5、yml配置忽略原路径访问

在yml中添加配置 zuul.ignored-services

springcloud-provider-dept-8001/src/main/resources/application.yml

zuul:
  routes:
    #替换掉微服务的名字
    mydept.serviceId: springcloud-provider-dept
    mydept.path: /mydept/**
  ignored-services: springcloud-provider-dept # 不能再用这个路径访问服务
  #ignored-services: "*"  表示隐藏全部

重启9527,再次测试

访问http://www.zuulgateway.com:9527/mydept/dept/get/1,成功。

访问原地址http://www.zuulgateway.com:9527/springcloud-provider-dept/dept/get/1,无法访问。

6、路由网关:设置公共的访问前缀

springcloud-provider-dept-8001/src/main/resources/application.yml

#zuul配置
zuul:
  routes:
    #替换掉微服务的名字
    mydept.serviceId: springcloud-provider-dept
    mydept.path: /mydept/**
  ignored-services: springcloud-provider-dept # 不能再用这个路径访问服务
  #ignored-services: "*"  表示隐藏全部真实微服务路径
  prefix: /chw #设置公共访问前缀。
               #效果:http://www.zuulgateway.com:9527/mydept/dept/get/1
               #---->http://www.zuulgateway.com:9527/chw/mydept/dept/get/1

10、SpringCloud Config分布式配置

第18集:Git环境搭建

image-20220726080311319

image-20220726080402538

image-20220726080532194

这里的服务端指的就是配置服务中心,客户端指的就是一个个的微服务。

image-20220726080856324

image-20220726081014989

开始操作

登录码云,新建仓库

image-20220726082226182 image-20220726082337050

找一个文件夹存放即将要克隆的仓库

这里我的目录是D:\developer_tools\Git\git-space\localrepo\config-springcloud

image-20220726082722793 image-20220726084335483

在刚刚克隆下来的文件夹里新建application.yml文件

image-20220726085053519

编辑内容

application.yml

spring:
  profiles:
    active: dev #表示激活谁,写dev就是激活dev
   
---  #单文件中可以通过`---`实现多文件的效果

spring:
  profiles: dev	#表示开发环境,用于赋值给active属性使自身得到激活
  application:
    name: springcloud-config-dev  #给应用自定义名字

---  #单文件中可以通过`---`实现多文件的效果

spring:
  profiles: test #表示测试环境
  application:
    name: springcloud-config-test  #给应用自定义名字

下面要把刚刚的变动提交到gitee上

D:\developer_tools\Git\git-space\localrepo\config-springcloud\springcloud-config目录中右键选择Git Bash Here

第一步:add添加到暂存区

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git add .

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   application.yml
        new file:   application.yml.bak

第二步:commit提交(这一步还是在本地提交)

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git commit -m "first commit"
[master 676ff13] first commit
 2 files changed, 36 insertions(+)
 create mode 100644 application.yml
 create mode 100644 application.yml.bak

第三步:push推送到远程

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 555 bytes | 555.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
To gitee.com:*********/springcloud-config.git
   6f65f50..676ff13  master -> master
image-20220726090814161
image-20220726090907697

第19集:服务端连接Git配置

新建模块:springcloud-config-server-3344

这个服务模块,我们想用它来连接远程仓库。

1、导入依赖

springcloud-config-server-3344/pom.xml

<dependencies>
    <!-- spring-cloud-config-server -->
    <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>
    <!--actuator监控-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

2、编写配置

springcloud-config-server-3344/src/main/resources/application.yml

server:
  port: 3344

spring:
  application:
    name: springcloud-config-server-3344
  #重点:连接远程仓库
  cloud:
    config:
      server:
        git:
          # gitee上springcloud-config仓库点击"克隆/下载"选https地址,复制过来。不是ssh
          uri: https://gitee.com/chenwei_2022/springcloud-config.git

3、写主启动类

建包com.chw.springcloud。

springcloud-config-server-3344/src/main/java/com/chw/springcloud/Config_Server_3344.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer //开启配置服务
public class Config_Server_3344 {
    public static void main(String[] args) {
        SpringApplication.run(Config_Server_3344.class, args);
    }
}

4、启动主动类测试,看能否得到gitee上仓库的文件。

访问http://localhost:3344/application-dev.yml,http://localhost:3344/application-dev.yml。看是否都访问到了配置文件。

这里我访问失败,依赖版本怎么换都不解决问题。

第20集:客户端连接服务端访问远程

下面要写客户端的

克隆到本地的仓库springcloud-config下新建文件config-client.yml

config-client.yml

spring:
  profiles: 
    active: dev
---

server:
  port: 8201

#spring的配置
spring:
  #要测试多环境,先配置一个profiles,指明这是什么环境的配置文件
  profiles: dev
  application:
    name: springcloud-provider-dept  #spring项目起个名字

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

--- # "---"切换文件

server:
  port: 8202

#spring的配置
spring:
  #要测试多环境,先配置一个profiles,指明这是什么环境的配置文件
  profiles: test
  application:
    name: springcloud-provider-dept  #spring项目起个名字

#Eureka的配置,服务注册到哪里
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

保存完push到远程

1、add添加暂存

2、commit提交

3、push到远程

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git add .

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    config-server.yml -> config-client.yml
        renamed:    config-server.yml.bak -> config-client.yml.bak

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git commit -m "3"
[master 0dc8b9a] 3
 2 files changed, 0 insertions(+), 0 deletions(-)
 rename config-server.yml => config-client.yml (100%)
 rename config-server.yml.bak => config-client.yml.bak (100%)

Administrator@CWCD MINGW64 /git-space/localrepo/config-springcloud/springcloud-config (master)
$ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 226 bytes | 226.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Powered by GITEE.COM [GNK-6.3]
To gitee.com:********/springcloud-config.git
   8b43ec4..0dc8b9a  master -> master

刷新gitee仓库的网络页面,发现已经改完了

本地写一个客户端,来一个eureka,新建一个模块springcloud-config-client-3355

1、导入依赖

springcloud-config-client-3355/pom.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.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

2、编写配置

先创建系统级别的配置文件:bootstrap.yml

再创建用户级别的配置文件:application.yml

这里设置系统级别的

springcloud-config-client-3355/src/main/resources/bootstrap.yml

#系统级别的配置
spring:
  cloud:
    config:
      #我们想去远程读取一个config-client.yml的配置文件
      name: config-client #需要从git上读取的资源名称,不需要后缀
      # 指定拿哪个版本的配置
      profile: dev
      # 指定拿哪个版本的分支
      label: master
      #客户端是要连接服务器的,再由服务器连接远程获取配置
      uri: http://localhost:3344

springcloud-config-client-3355/src/main/resources/application.yml

#用户级别的配置
#用户级别的配置
#配置一个名字即可
spring:
  application:
    name: springcloud-config-client-3355

3、写一个controller类用于测试是否能够从远程获取到配置信息

springcloud-config-client-3355/src/main/java/com/chw/springcloud/controller/ConfigClientController.java

package com.chw.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {
    //之前是通过文件去绑定的,现在从远程绑定
    @Value("${Spring.application.name}")
    private String applicationName;

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

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

    @RequestMapping("/config")
    public String getConfig() {
        return "applicationName: " + applicationName +
                "eurekaServer: " + eurekaServer +
                "port: " + port;
    }
}

4、写主启动类

springcloud-config-client-3355/src/main/java/com/chw/springcloud/ConfigClient_3355.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

5、启动测试

把3344和3355都启动。

先检查server和gitee是否连通:http://localhost:3344/master/config-client-dev.yml

注意到,3355模块的配置文件中并没有设置端口号,而是由于bootstrap.yml里

#系统级别的配置
spring:
  cloud:
    config:
      #我们想去远程读取一个config-client.yml的配置文件
      name: config-client #需要从git上读取的资源名称,不需要后缀
      # 指定拿哪个版本的配置
      profile: dev
      # 指定拿哪个版本的分支
      label: master
      #客户端是要连接服务器的,再由服务器连接远程获取配置
      uri: http://localhost:3344

激活了dev,而config-client.yml的dev版本的端口是8201,所以这个客户端的配置现在就已经被设置成了config-client.yml的dev版本了。

访问一下控制器方法的路径,看能不能打印出来配置信息。http://localhost:8201/config

eureka,新建一个模块springcloud-config-client-3355

1、导入依赖

springcloud-config-client-3355/pom.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.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

2、编写配置

先创建系统级别的配置文件:bootstrap.yml

再创建用户级别的配置文件:application.yml

这里设置系统级别的

springcloud-config-client-3355/src/main/resources/bootstrap.yml

#系统级别的配置
spring:
  cloud:
    config:
      #我们想去远程读取一个config-client.yml的配置文件
      name: config-client #需要从git上读取的资源名称,不需要后缀
      # 指定拿哪个版本的配置
      profile: dev
      # 指定拿哪个版本的分支
      label: master
      #客户端是要连接服务器的,再由服务器连接远程获取配置
      uri: http://localhost:3344

springcloud-config-client-3355/src/main/resources/application.yml

#用户级别的配置
#用户级别的配置
#配置一个名字即可
spring:
  application:
    name: springcloud-config-client-3355

3、写一个controller类用于测试是否能够从远程获取到配置信息

springcloud-config-client-3355/src/main/java/com/chw/springcloud/controller/ConfigClientController.java

package com.chw.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {
    //之前是通过文件去绑定的,现在从远程绑定
    @Value("${Spring.application.name}")
    private String applicationName;

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

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

    @RequestMapping("/config")
    public String getConfig() {
        return "applicationName: " + applicationName +
                "eurekaServer: " + eurekaServer +
                "port: " + port;
    }
}

4、写主启动类

springcloud-config-client-3355/src/main/java/com/chw/springcloud/ConfigClient_3355.java

package com.chw.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

5、启动测试

把3344和3355都启动。

先检查server和gitee是否连通:http://localhost:3344/master/config-client-dev.yml

注意到,3355模块的配置文件中并没有设置端口号,而是由于bootstrap.yml里

#系统级别的配置
spring:
  cloud:
    config:
      #我们想去远程读取一个config-client.yml的配置文件
      name: config-client #需要从git上读取的资源名称,不需要后缀
      # 指定拿哪个版本的配置
      profile: dev
      # 指定拿哪个版本的分支
      label: master
      #客户端是要连接服务器的,再由服务器连接远程获取配置
      uri: http://localhost:3344

激活了dev,而config-client.yml的dev版本的端口是8201,所以这个客户端的配置现在就已经被设置成了config-client.yml的dev版本了。

访问一下控制器方法的路径,看能不能打印出来配置信息。http://localhost:8201/config

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值