SpringCloud——Eureka Feign Ribbon Hystrix Zuul等要害组件的学习与记录

前言:本篇是对近日学习狂神SpringCloud教程之后的感想和总结,鉴于对SpringCloud体系的理解尚且处于初期阶段,在措辞、了解上难免会有偏颇,还请批评和斧正!

一、概述

SpringCloud是一套微服务解决方案,在spring-cloud-dependencies Hoxton.SR8版本与spring-boot-dependencies 2.3.4.RELEASE版本的配合下,这一套解决方案提供了Eureka注册核心、Ribbon+RestTempleteFeign的负载平衡计划、Hystrix熔断、Hystrix+Feign降级、HystrixDashboard服务监控以及Zuul路由网关。

不过,在spring-cloud-dependencies后续的版本中(如2020.x.x系列版本),上述提供的服务变动较大,尤其是Ribbon进行更新(IRule接口被移除)、Hystrix+Feign的变动使咱们应用自定义的负载平衡算法、进行服务降级时造成了一些艰难,这些局部我还在学习和摸索中,会在根本理解和把握之后再进行记录,因此本篇仍然基于稍早的spring-cloud-dependencies版本进行摘记。

(P.S. 只管Hoxton版本当初仍然处于反对中,不过依照NetFIix在新版本中的改变来看,兴许这一套解决方案被弃用是迟早的事件)

二、我的项目的组成

1.我的项目的集成办法

SpringCloud我的项目由一个总的pom.xml负责管理我的项目中可能会用到的所有依赖,这样治理的作用是不便版本的对立。子模块在应用雷同的依赖时,不用指定依赖的版本,因为这些版本曾经在总的pom.xml中指定好了。

在一个我的项目总的pom.xml中,咱们能够应用<dependencyManagement></dependencyManagement>标签对所有依赖进行治理,此时,在这之中的所有<dependency></dependency>将不会被导入,而是由子模块继承总的pom.xml并应用相干依赖后才会导入到相应的子模块中。

例如,下方展现的是总的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>org.example</groupId>    <artifactId>springclouddemo</artifactId>    <version>1.0-SNAPSHOT</version>    <modules>        <module>springcloud-api</module>        <module>springcloud-provider-dept-8001</module>        <module>springcloud-consumer-dept-80</module>        <module>springcloud-eureka-7001</module>        <module>springcloud-eureka-7002</module>        <module>springcloud-eureka-7003</module>        <module>springcloud-provider-dept-8002</module>        <module>springcloud-provider-dept-8003</module>        <module>springcloud-consumer-dept-feign</module>        <module>springcloud-provider-dept-hystrix-8001</module>        <module>springcloud-consumer-hystrix-dashboard</module>        <module>springcloud-zuul</module>        <module>springcloud-config-server-3344</module>        <module>spingcloud-config-client-3355</module>        <module>springcloud-config-eureka-7001</module>        <module>springcloud-config-dept-8001</module>    </modules>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>        <junit.version>4.12</junit.version>        <druid.version>1.1.10</druid.version>        <lombok.version>1.18.22</lombok.version>    </properties>    <packaging>pom</packaging>    <dependencyManagement>        <dependencies>            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->            <dependency>                <groupId>org.springframework.cloud</groupId>                <artifactId>spring-cloud-dependencies</artifactId>                <version>Hoxton.SR8</version>                <type>pom</type>                <scope>import</scope>            </dependency>            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies -->            <dependency>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-dependencies</artifactId>                <version>2.3.4.RELEASE</version>                <type>pom</type>                <scope>import</scope>            </dependency>            <dependency>                <groupId>mysql</groupId>                <artifactId>mysql-connector-java</artifactId>                <version>8.0.23</version>            </dependency>            <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->            <dependency>                <groupId>com.alibaba</groupId>                <artifactId>druid</artifactId>                <version>${druid.version}</version>            </dependency>            <dependency>                <groupId>org.mybatis.spring.boot</groupId>                <artifactId>mybatis-spring-boot-starter</artifactId>                <version>2.2.1</version>            </dependency>            <!--日志测试-->            <dependency>                <groupId>junit</groupId>                <artifactId>junit</artifactId>                <version>${junit.version}</version>            </dependency>            <dependency>                <groupId>log4j</groupId>                <artifactId>log4j</artifactId>                <version>1.2.12</version>            </dependency>            <dependency>                <groupId>ch.qos.logback</groupId>                <artifactId>logback-core</artifactId>                <version>1.2.7</version>            </dependency>            <dependency>                <groupId>org.projectlombok</groupId>                <artifactId>lombok</artifactId>                <version>${lombok.version}</version>            </dependency>        </dependencies>    </dependencyManagement></project>

下方展现的是某一子模块对父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>springclouddemo</artifactId>        <groupId>org.example</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>springcloud-provider-dept-hystrix-8001</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>            <version>2.2.9.RELEASE</version>        </dependency>        <!--EUREKA提供-->        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>            <version>3.0.5</version>        </dependency><!--        actuator欠缺监控信息-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency><!--用于拿到实体类-->        <dependency>            <groupId>org.example</groupId>            <artifactId>springcloud-api</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>        </dependency>        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->        <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>        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-test -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-test</artifactId>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <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></project>

当然,如果咱们没有在总的pom.xml中申明一些依赖治理,则这些依赖在子模块中导入时还是要写明版本编号的。

2.我的项目的各个局部

一个SpringCloud我的项目由四个局部组成,别离是服务的注册核心、服务的API、服务提供者和服务消费者。

  • 注册核心负责服务提供者的注册和发现工作,不便服务消费者对服务进行应用,同时对提供的服务可能进行无效治理。简略来说,注册核心便是多个集群,当一个集群故障,仍然能够保障其余集群运行,避免服务解体。举个例子,注册核心好比电商服务平台,服务提供者商家在平台上展现商品,服务消费者买家在平台上浏览商品。
  • 服务API中提供服务所须要的实体类(及通常与数据库表绝对应的局部),同时也提供一些通用的配置,比方在Feign的负载平衡应用时,消费者通过调用API的接口实现对服务的获取,这种配置办法防止了在消费者模块中反复配置接口,达到对立应用的目标。
  • 服务提供者是传统我的项目中均存在的局部,例如在SpringBoot中,咱们的我的项目个别蕴含Dao层、Service层、Controller层,从而进行数据库交互、服务逻辑解决和服务接口外显等工作,在SpringCloud中,服务提供者便持续承当这些工作,不同的是,服务提供者可能有多个提供雷同服务的子模块,这是为了不便实现负载平衡所造成的。
  • 服务消费者的目标是对服务提供者url进行荫蔽,外显的是消费者的url和端口,以后端调用服务时,只须要调用消费者便能够失去后盾数据。在消费者端,能够采纳不同的办法实现负载平衡,这将会在后续进行开展。

上面的局部将对SpringCloud提供的几大外围性能进行介绍,并联合我的项目源码进行展现。

三、注册与发现——Eureka

Eureka作为注册核心,提供平台以帮忙服务提供者将服务进行外显。

首先,咱们须要建设一个子项目springcloud-eureka-7001,并在pom.xml中导入依赖

    <dependencies>        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka-server</artifactId>            <version>1.4.4.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-devtools</artifactId>        </dependency>    </dependencies>

注:spring-boot-devtools次要提供热重载服务。

接着,咱们须要对这个子模块进行根本配置,在application中指定端口、eureka服务端实例名称、是否要被注册核心发现、本人是否为注册核心、以及要注册组件拜访的地址

server:  port: 7001#Euraka配置eureka:  instance:#    hostname: localhost #Euraka服务端实例名称    hostname: eureka7001.com #Euraka服务端实例名称 集群状况下  client:    register-with-eureka: false #示意是否向eureka注册核心注册本人    fetch-registry: false #如果为false,则示意本人为注册核心    service-url: #示意与注册核心交互的地址,即监控页面,要注册的组件拜访这个地址#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/      # 如果独自拜访eureka,则在浏览器写 http://localhost:7001/ 即可      defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/

最初,咱们只须要在启动类中表明这个子模块是一个eureka server就能够了,应用注解@EnableEurekaServer

@SpringBootApplication@EnableEurekaServer //EnableEurekaServer服务端启动类,承受他人注册进来public class EurekaServer_7001 {    public static void main(String[] args) {        SpringApplication.run(EurekaServer_7001.class, args);    }}

当然,上文提及过,为了保障注册核心不因为意外解体,咱们能够搭建多个集群,使多个注册核心相互调用,避免注册核心解体导致整个服务不可用。

因而咱们再创立子项目springcloud-eureka-7002、springcloud-eureka-7003,并配置application.yml

7002:

server:  port: 7002#Euraka配置eureka:  instance:    hostname: eureka7002.com #Euraka服务端实例名称  client:    register-with-eureka: false #示意是否向eureka注册核心注册本人    fetch-registry: false #如果为false,则示意本人为注册核心    service-url: #示意与注册核心交互的地址,即监控页面,要注册的组件拜访这个地址#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/      # 如果独自拜访eureka,则在浏览器写 http://localhost:7001/ 即可      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/

7003:

server:  port: 7003#Euraka配置eureka:  instance:    hostname: eureka7003.com #Euraka服务端实例名称  client:    register-with-eureka: false #示意是否向eureka注册核心注册本人    fetch-registry: false #如果为false,则示意本人为注册核心    service-url: #示意与注册核心交互的地址,即监控页面,要注册的组件拜访这个地址#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/      # 如果独自拜访eureka,则在浏览器写 http://localhost:7001/ 即可      defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/

四、负载平衡

在SpringCloud中,负载平衡的意义是从客户端的角度登程的,通过负载平衡的策略抉择一个服务端的Service,从而防止对一个服务的拜访造成较大拥挤,当然,前提是服务提供方提供了几个有同样性能的服务,就像咱们在后面所说的那样。

上面将从Ribbon+RestTemplete和Feign两种不同的办法对负载平衡进行介绍。

五、负载平衡——Ribbon+RestTemplete

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载平衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,能够让咱们轻松地将面向服务的REST模版申请主动转换成客户端负载平衡的服务调用。

RestTemplate 是 Spring 提供的用于拜访 Rest 服务的客户端,RestTemplate 提供了多种便捷拜访近程Http服务的办法,可能大大提高客户端的编写效率。

首先,咱们创立一个名为springcloud-consumer-dept-80的子模块,这一模块将默认应用80端口,在pom.xml中增加依赖,目录组成和代码如下所示

    <dependencies>        <!--ribbon-->        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon --><!--        <dependency>--><!--            <groupId>org.springframework.cloud</groupId>--><!--            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>--><!--            <version>2.2.9.RELEASE</version>--><!--        </dependency>-->        <!--EUREKA提供 其曾经蕴含ribbon,所以不必独自增加了-->        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>            <version>1.4.7.RELEASE</version>        </dependency>        <dependency>            <groupId>org.example</groupId>            <artifactId>springcloud-api</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-devtools</artifactId>        </dependency>    </dependencies>

接着咱们创立一个Config类,用于配置RestTemplate和负载平衡的实现,@LoadBalanced注解实现了负载平衡

@Configurationpublic class ConfigBean {    @Bean    @LoadBalanced //配置负载平衡实现    public RestTemplate getRestTemplate() {        return new RestTemplate();    }}

而后咱们实现Controller层,这里咱们应用一个常量来指定所须要的服务,服务的链接不须要写明url地址,只须要将服务名作为链接的组成部分即可,例如这样:"http://springcloud-provider-dept"

对于每个服务,咱们须要应用RestTemplate中的getForObject或postForObject办法调用服务提供者给出的服务。例如

    @Autowired    private RestTemplate restTemplate;       //ribbon应用时,这里的地址应该是变量,通过服务名来拜访    private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";    @RequestMapping("/consumer/dept/add")    public boolean add(Dept dept) {        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);    }

REST_URL_PREFIX+"/dept/add" 是服务接口名

dept 为传入参数

Boolean.class 为返回值类型

这与public boolean add(Dept dept) 也是绝对应的。

给出残缺的代码:

@RestControllerpublic class DeptConsumerController {    @Autowired    private RestTemplate restTemplate;//    private static final String REST_URL_PREFIX = "http://localhost:8001";    //ribbon应用时,这里的地址应该是变量,通过服务名来拜访    private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";    @RequestMapping("/consumer/dept/add")    public boolean add(Dept dept) {        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);    }    @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/list")    public List<Dept> list() {        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list", List.class);    }}

最初实现主启动类:

// ribbon和Eureka整合之后,客户端能够间接调用,不必关怀IP地址和端口号@SpringBootApplication@EnableEurekaClientpublic class DeptConsumer_80 {    public static void main(String[] args) {        SpringApplication.run(DeptConsumer_80.class, args);    }}

六、负载平衡——Feign

应用Feign实现负载平衡其实与应用Ribbon+RestTemple实现实质是雷同的,因为Feign自身也继承了Ribbon,不同的是,Feign更合乎面向接口编程的思维,通过封装,使咱们在调用时以接口的形式获取服务,而不是通过一个RestTemplate中的那种常量。

首先咱们创立我的项目并导入pom.xml中的依赖:

    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-feign</artifactId>            <version>1.4.7.RELEASE</version>        </dependency>        <!--ribbon 这里其实能够不必加了-->        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-ribbon</artifactId>            <version>1.4.7.RELEASE</version>        </dependency>        <!--EUREKA提供 其曾经蕴含ribbon,所以不必独自增加了-->        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>            <version>1.4.7.RELEASE</version>        </dependency>        <dependency>            <groupId>org.example</groupId>            <artifactId>springcloud-api</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-devtools</artifactId>        </dependency>    </dependencies>

后面讲到过,负载平衡是以客户端依据某种策略抉择服务端的服务接口,因而平衡负载的接口肯定会在客户端实现。因而咱们要在springcloud-api子模块中实现一个接口,这个接口实现一个对立的负载平衡计划,并帮忙客户端调用服务提供者的服务,咱们在springcloud-api子模块中创立service层,并新建一个ClientService接口:

@FeignClient(value = "springcloud-provider-dept")public interface DeptClientService {    @GetMapping("/dept/get/{id}")    Dept queryById(@PathVariable("id") Long id);    @GetMapping("/dept/list")    List<Dept> queryAll();    @PostMapping("/dept/add")    boolean addDept(Dept dept);}

在接口中,@FeignClient(value = "springcloud-provider-dept")注解论述了咱们要拜访的服务名称springcloud-provider-dept,并且默认的提供了负载平衡算法,至此,咱们的负载平衡计划就实现了。

可能某些敌人会纳闷,为什么在Ribbon+RestTemplate的计划中,没有在springcloud-api子模块中创立接口,就我集体的见解来说,其一个起因是Ribbon+RestTemplate计划并非面向接口的,而是应用一个常量作为服务名称间接调用RestTemplate中的办法来搜查服务,并不存在应用一个对立的自定义的公共接口的问题;换一个角度来说,其第二个起因是RestTemplate自身是一个实现类,就如同咱们在springcloud-api子模块中实现的一个公共接口DeptClientService一样,咱们剩下的工作仅仅是调用一下,因而不须要本人独自实现。

而后,咱们回到springcloud-consumer-dept-feign子模块中,创立一个Controller用于调用咱们刚刚写好的接口

@RestControllerpublic class DeptConsumerController {    @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();    }}

最初,在主启动类中引入注解@EnableFeignClients(basePackages = {"com.demo.springcloud"}),使其可能应用Feign并扫描包中的接口

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

七、熔断——Hystrix

熔断的次要作用是:因为某些起因使得服务呈现了过载景象,为避免造成整个系统故障,从而采纳的一种保护措施。

比方咱们申请一个数据库中不存在的数据,如果不加以解决,服务器将在这个中央呈现谬误,而随着访问者的一直增多,所有的访问者都会因为申请这个出错的服务而阻塞在这里,造成服务的解体。而当这一服务解体,与之关联的前序服务也会因为拥塞而解体,进而造成整个服务全面解体,这一景象称之为雪崩。

为了杜绝这一景象,咱们引入熔断的机制,当呈现服务谬误时,咱们尝试应用一个拦挡的伎俩,将谬误的申请返回一个可能使其失常完结的响应,从而避免谬误的产生,防止拥塞。

就我集体的了解来说,这像是一种官网提供的从服务端角度实现的对某一接口的错误处理,咱们仅需实现一个错误处理办法,并应用@HystrixCommand注解,当呈现谬误时,让零碎进行回调即可。

上面是熔断的实现计划:

首先,创立一个我的项目,并导入pom.xml依赖,因为这是在服务提供者的根底上进行的改良,因而依赖与服务提供者是类似的。仅仅多了Hystrix的依赖。

    <dependencies>        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>            <version>2.2.9.RELEASE</version>        </dependency>        <!--EUREKA提供-->        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>            <version>3.0.5</version>        </dependency><!--        actuator欠缺监控信息-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>        </dependency><!--用于拿到实体类-->        <dependency>            <groupId>org.example</groupId>            <artifactId>springcloud-api</artifactId>            <version>1.0-SNAPSHOT</version>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>        </dependency>        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->        <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>        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-test -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-test</artifactId>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <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>

接着,革新Controller层,退出谬误处理函数和注解:

@RestControllerpublic 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);        return dept;    }    //备选计划    public Dept hystrixGet(@PathVariable("id") Long id) {        return new Dept().setDeptno(id)                .setDname("id=>"+id+",不存在该用户,信息无奈找到")                .setDb_source("no database");    }}

最初,在主启动类中退出@EnableCircuitBreaker注解从而反对熔断

@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 registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());        registrationBean.addUrlMappings("/actuator/hystrix.stream");        return registrationBean;    }}

八、降级——Hystrix+Feign

降级的利用场景是,当一个服务拜访过高,而另一个服务简直很少有人拜访时,如果可能敞开另一个服务,则能够为服务器腾出更多的空间来承载那个拜访很高的服务。不过此时,贸然敞开另一个服务会导致应用这个服务的用户忽然呈现谬误,为了避免这个状况产生,应该给这个服务减少一个整体性的错误处理计划。当服务忽然敞开时,返回给用户一个错误信息,而不是让服务器解体。

上面展现实现步骤:

首先,咱们须要一个实现了Hystrix依赖的服务提供方,因而持续应用熔断中的我的项目springcloud-provider-dept-hystrix-8001

接着,因为降级是从客户端角度登程的,因而还须要一个消费者子模块来拜访服务提供方,这里咱们应用实现了Feign依赖的客户端springcloud-consumer-dept-feign

最初,咱们在消费者和提供方两头的桥梁——api子模块中实现一个错误处理计划fallbackfactory,它能够对整个类(即服务)进行错误处理,不过要留神的是,服务提供方的接口上必须要有@HystrixCommand注解,能力起到作用。

@Componentpublic class DeptClientServiceFallBackFactory implements FallbackFactory {    @Override    public DeptClientService create(Throwable cause) {        return new DeptClientService() {            @Override            public Dept queryById(Long id) {                return new Dept()                        .setDeptno(id)                        .setDname("id=>"+id+"没有对应的信息,客户端提供了降级信息,以后服务已敞开")                        .setDb_source("没有数据库对应信息");            }            @Override            public List<Dept> queryAll() {                return null;            }            @Override            public boolean addDept(Dept dept) {                return false;            }        };    }}

这一实现类要实现接口FallbackFactory,同时,应用Feign的那个自定义接口中要批改一下FeignClient注解

@FeignClient(value = "springcloud-provider-dept", fallbackFactory = DeptClientServiceFallBackFactory.class)//@FeignClient(value = "springcloud-provider-dept")public interface DeptClientService {    @GetMapping("/dept/get/{id}")    Dept queryById(@PathVariable("id") Long id);    @GetMapping("/dept/list")    List<Dept> queryAll();    @PostMapping("/dept/add")    boolean addDept(Dept dept);}

此时,当咱们启动服务时,忽然敞开服务提供方的过程,再次从客户端申请便会显示没有对应的信息,客户端提供了降级信息,以后服务已敞开的错误信息了。

熔断与降级的不同:

1.熔断是对某一个接口进行错误处理,降级能够对某一个类进行错误处理

2.熔断从服务提供方的角度登程,降级从消费者角度登程

九、服务监控——HystrixDashBoard

Hystrix会继续地记录所有通过Hystrix发动的申请的执行信息,并以统计报表和图形的模式展现给用户,包含每秒执行多少申请,多少胜利,多少失败等等。

在客户端咱们须要独自创立一个监控子模块,并在主启动类中退出@EnableHystrixDashboard注解。

在服务提供方的pom.xml中退出如下依赖即可

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

在服务提供方的启动类中退出如下代码:

    //减少一个Servlet    @Bean    public ServletRegistrationBean hystrixMetricsStreamServlet() {        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());        registrationBean.addUrlMappings("/actuator/hystrix.stream");        return registrationBean;    }

十、路由网关——Zuul

路由网关次要提供一个对立的零碎入口,拜访服务均从这一入口进行,能够无效屏蔽各个微服务自身的端口信息,晋升安全性。

首先,创立一个zuul的子模块,并在application.yml中退出对zuul的配置信息

zuul:  routes:    mydept.serviceId: springcloud-provider-dept # 本来的项目名称    mydept.path: /mydept/**    # 上述操作用于将微服务名称springcloud-provider-dept暗藏起来,即url上的springcloud-provider-dept将被替换为mydept  ignored-services: springcloud-provider-dept #不再应用这个门路拜访 暗藏全副门路为:“*”  prefix: /demodemo #设置对立的前缀

接着在主启动类中退出@EnableZuulProxy注解即可

@SpringBootApplication@EnableZuulProxy //个别抉择代理public class ZuulApplication_9527 {    public static void main(String[] args) {        SpringApplication.run(ZuulApplication_9527.class, args);    }}

十一、近程yml配置——Config

在我的项目的开发中,每一个子模块都蕴含本人的application.yml配置信息,当一个产品从dev阶段变为test阶段时,咱们必须要批改我的项目中的application.yml。近程的yml配置计划容许不批改我的项目中的yml,而是批改近程仓库中的配置信息,我的项目中的配置文件只须要简略调用近程仓库的配置信息即可,从而达到我的项目与配置解耦的目标,也不便经营、测试人员对我的项目上线前进行环境调试。

首先,咱们须要一个用于获取近程仓库配置文件的子模块,创立springcloud-config-server-3344,并在pom.xml中退出依赖

    <dependencies>        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-config-server</artifactId>            <version>2.0.5.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>    </dependencies>

在application.yml中配置端口和近程仓库连贯信息

server:  port: 3344spring:  application:    name: springcloud-config-server #利用的名称  #连贯近程仓库  cloud:    config:      server:        git:          uri: https://gitee.com/xxx/springcloud-demo.git #https

在主启动类中退出注解@EnableConfigServer

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

接着,咱们在须要近程配置的子模块中引入config依赖

<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-config</artifactId>    <version>2.2.1.RELEASE</version></dependency>

新建一个bootstrap.yml配置文件,这个文件用于调用获取近程仓库配置文件的子模块从而间接调用近程仓库的配置文件

spring:  cloud:    config:      name: config-dept # 须要从git上读取的资源名称,不须要后缀      label: master      profile: dev      uri: http://localhost:3344      # 上述相当于:http://localhost:3344/master/config-dept-dev.yml

批改application.yml,删除所有的配置,仅保留项目名称即可

spring:  application:    name: spring-config-client-3355

因为bootstrap.yml是比application.yml优先级更高的配置文件,因而我的项目肯定会依照近程仓库的在线配置文件来对我的项目进行配置,因而即便在application.yml写上了不同的端口信息,也是不起作用的。

在近程仓库中的yml文件如下所示:

spring:  profiles:    active: dev ---server:  port: 8001management:  endpoints:    web:      exposure:        include: "*"mybatis:  type-aliases-package: com.demo.springcloud.pojo  config-location: classpath:mybatis/mybatis-config.xml  mapper-locations: classpath:mybatis/mapper/*.xmlspring:  profiles: dev  application:    name: springcloud-config-dept #利用的名称  datasource:    type: com.alibaba.druid.pool.DruidDataSource    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://localhost:3306/eis?useUnicode=true&characterEncoding-utf-8    username: root    password: 123456#eureka的配置,服务注册到哪里,提供者要找到注册核心eureka:  client:    service-url:#      defaultZone: http://localhost:7001/eureka/      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/ #集群状况下的提供者公布配置网址  instance:    instance-id: springcloud-provider-dept8001 #批改eureka上的默认形容信息#info配置 轻易写的info:  app.name: demo-yjn  company.name: yangejining.springcloud.com---server:  port: 8001management:  endpoints:    web:      exposure:        include: "*"mybatis:  type-aliases-package: com.demo.springcloud.pojo  config-location: classpath:mybatis/mybatis-config.xml  mapper-locations: classpath:mybatis/mapper/*.xmlspring:  profiles: test  application:    name: springcloud-config-dept #利用的名称  datasource:    type: com.alibaba.druid.pool.DruidDataSource    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://localhost:3306/eis1?useUnicode=true&characterEncoding-utf-8    username: root    password: 123456#eureka的配置,服务注册到哪里,提供者要找到注册核心eureka:  client:    service-url:#      defaultZone: http://localhost:7001/eureka/      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/ #集群状况下的提供者公布配置网址  instance:    instance-id: springcloud-provider-dept8001 #批改eureka上的默认形容信息#info配置 轻易写的info:  app.name: demo-yjn  company.name: yangejining.springcloud.com

附录

参考资料:

https://www.bilibili.com/vide...

restTemplate的介绍和应用_weixin_41261521的博客-CSDN博客_resttemplate

Ribbon详解 - 简书 (jianshu.com)

转载请注明出处#