共计 25140 个字符,预计需要花费 63 分钟才能阅读完成。
SpringCloud——Eureka Feign Ribbon Hystrix Zuul 等要害组件的学习与记录
前言:本篇是对近日学习狂神 SpringCloud 教程之后的感想和总结,鉴于对 SpringCloud 体系的理解尚且处于初期阶段,在措辞、了解上难免会有偏颇,还请批评和斧正!
一、概述
SpringCloud 是一套微服务解决方案,在 spring-cloud-dependencies Hoxton.SR8 版本与 spring-boot-dependencies 2.3.4.RELEASE 版本的配合下,这一套解决方案提供了 Eureka 注册核心、Ribbon+RestTemplete或 Feign 的负载平衡计划、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 注解实现了负载平衡
@Configuration
public 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) 也是绝对应的。
给出残缺的代码:
@RestController
public 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
@EnableEurekaClient
public 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 用于调用咱们刚刚写好的接口
@RestController
public 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 层,退出谬误处理函数和注解:
@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);
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 注解,能力起到作用。
@Component
public 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: 3344
spring:
application:
name: springcloud-config-server #利用的名称
#连贯近程仓库
cloud:
config:
server:
git:
uri: https://gitee.com/xxx/springcloud-demo.git #https
在主启动类中退出注解 @EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public 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: 8001
management:
endpoints:
web:
exposure:
include: "*"
mybatis:
type-aliases-package: com.demo.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
spring:
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: 8001
management:
endpoints:
web:
exposure:
include: "*"
mybatis:
type-aliases-package: com.demo.springcloud.pojo
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
spring:
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)
转载请注明出处 #