SpringCloud---负载均衡---Feign服务接口---Hystrix熔断服务---Zuul路由网关
小程序开发定制结合上一篇《》
6. Ribbon负载均衡(小程序开发定制基于客户端)
6.1 Ribbon概述
Ribbon是什么?
Spring Cloud Ribbon是基于Netflix Ribbon小程序开发定制实现的一套小程序开发定制客户端负载均衡的工具。
简单的说,Ribbon是Netflix小程序开发定制发布的开源项目,小程序开发定制主要功能是提供客户端的软件小程序开发定制负载均衡算法,将NetFlix小程序开发定制的中间层服务连接在一起。Ribbon 小程序开发定制的客户端组件提供一系小程序开发定制列完整的配置项如:连接超时、重试等等。简单的说,小程序开发定制就是在配置文件中列出 LoadBalancer(简称LB:负载均衡)小程序开发定制后面所有的机器,Ribbon 小程序开发定制会自动的帮助你基于某种规则(小程序开发定制如简单轮询,小程序开发定制随机连接等等)小程序开发定制去连接这些机器。小程序开发定制我们也很容易使用 Ribbon小程序开发定制实现自定义的负载均衡算法!
Ribbon能干嘛?
LB,小程序开发定制即负载均衡(Load Balance),小程序开发定制在微服务或分布式集群小程序开发定制中经常用的一种应用
小程序开发定制负载均衡简单的说就是小程序开发定制将用户的请求平摊的分小程序开发定制配到多个服务上,小程序开发定制从而达到系统的HA(高可用)。小程序开发定制常见的负载均衡软件有 Nginx,Lvs 等等
Dubbo、SpringCloud小程序开发定制中均给我们提供了负载均衡,SpringCloud小程序开发定制的负载均衡算法可以自小程序开发定制定义负载均衡简单分类:
- 集中式LB
- 小程序开发定制即在服务的消费方和提小程序开发定制供方之间使用独立的LB设施
- 小程序开发定制如之前学习的Nginx,小程序开发定制由该设施负责把访问请小程序开发定制求通过某种策略转发至小程序开发定制服务的提供方!
- 进程式LB
- 将LB小程序开发定制逻辑集成到消费方,小程序开发定制消费方从服务注册中心小程序开发定制获知有哪些地址可用,小程序开发定制然后自己再从这些地址小程序开发定制中选出一个合适的服务器。
- Ribbon小程序开发定制就属于进程内LB,小程序开发定制它只是一个类库,小程序开发定制集成于消费方进程,消费方通过它来获取到服务提供方的地址!
6.2 Ribbon实现负载均衡
- 在springcloud-consumer-dept-80中的
pom.xml
<!--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>2.2.10.RELEASE</version> </dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 修改
application.yml
, 追加Eureka的服务注册地址
# Eureka配置eureka: client: register-with-eureka: false # 不向Eureka注册自己 service-url: defaultZone: http://DESKTOP-QODU2TD:7001/eureka/,http://localhost:7002/eureka/,http://127.0.0.1:7003/eureka/
- 1
- 2
- 3
- 4
- 5
- 6
- 对里面的ConfigBean方法加上注解,在获得Rest时加入Ribbon的配置,开启Ribbon
@Configurationpublic class ConfigBean { // @Configuration --> 相当于 spring 的 application.xml // 配置负载均衡实现RestTemplate // IRule // AvailabilityFilteringRule: 会先过滤掉跳闸,访问故障的服务(提供者),对剩下进行轮询 // RandomRule: 随机 // RetryRule: 重试,会按照轮询获取服务,如果服务获取失败,则会在指定时间内进行重试 @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 主启动类添加,开启Eureka客户端
// Ribbon 和 Eureka 整合以后,客户端可以直接调用访问,不用关心IP地址和端口号@SpringBootApplication@EnableEurekaClientpublic class DeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(DeptConsumer_80.class,args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 修改DeptConsumerController.class客户端访问类,之前的写的地址是写死的,现在需要变化
// Ribbon,我们这里的地址,应该是一个变量,通过服务名来访问 // private static final String REST_URL_PREFIX = "http://localhost:8001"; private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
- 1
- 2
- 3
- 4
6.3 常见负载均衡算法
- 随机:通过随机选择服务进行执行,一般这种方式使用较少
- 轮询:负载均衡默认实现方式,将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端每一台服务器,而不关心服务器实际的连接数和当前的系统负载,请求之后排队处理
- 加权轮询:通过对服务器性能的分析,给高配置,低负载的服务器分配更高的权重,均衡各个服务器的压力,并将请求顺序且按照权重分配到后端
- 加权随机法:与加权轮询法类似,加权随机法也根据后端服务器不同的配置和负载情况,配置不同的权重。不同的是,它是按照权重来随机选取服务器的,而非顺序
- 地址Hash:通过客户端请求的地址的Hash值取模映射进行服务器调度
- 最小连接数:即使请求均衡了,压力不一定均衡,最小连接数法就是根据服务器的情况,比如请求积压数等参数,动态地选取其中当前积压连接数最少的一台服务器来处理当前请求,将请求分配到当前压力最小的服务器上,也就是最小活跃度,尽可能地提高后端服务器的利用率,将负载合理地分流到每台机器
7. 服务接口
7.1 Feign概述
Feign
是一个声明式的Web Service
客户端,它的目的就是让Web Service
调用更加简单。Feign
提供了HTTP
请求的模板,通过编写简单的接口和注解,就可以定义好HTTP
请求的参数、格式、地址等信息
Feign
是⼀个HTTP
请求的轻量级客户端框架。通过 接口 + 注解的方式发起HTTP
请求调用,面向接口编程,而不是像Java
中通过封装HTTP
请求报文的方式直接调用。服务消费方拿到服务提供方的接⼝,然后像调⽤本地接⼝⽅法⼀样去调⽤,实际发出的是远程的请求
在发送 Http Request 请求之前,Feign 通过处理注解的方式替换掉 Request 模板中的参数,生成真正的 Request,并交给 Java Http 客户端去处理。利用这种方式,开发者只需要关注 Feign 注解模板的开发,而不用关注 Http 请求本身,简化了 Http 请求的过程,使得 Http 请求变得简单和容易理解
Spring Cloud 集成了 Ribbon 和 Eureka,可在使用 Feign 时提供负载均衡的http客户端
只需要创建一个接口,然后添加注解即可
调用微服务访问两种方法
- 微服务名字(Eureka注册与服务中获取)
- 接口和注解 【feign】(在我们的spring-api编写接口,在消费者的启动类中添加注解)
Feign能干什么?
- Feign旨在使编写Java Http客户端变得更容易
前面在使用 Ribbon + RestTemplate 时,利用 RestTemplate 对 Http 请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他 来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(类似于以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可。)即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量
Feign集成了Ribbon
- 利用Ribbon维护了springcloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而且简单的实现了服务调用
7.2 Feign使用步骤
1、参考springcloud-consumer-dept-80
2、新建springcloud-consumer-dept-feign-80
- 修改主启动类名称
- 将springcloud-consumer-dept-80的内容都拷贝到 feign项目中
- 修改主启动类的名称为 DeptConsumerFeign80
3、springcloud-consumer-dept-feign-80修改pom.xml
, 添加对Feign的支持
<!--Feign--><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign --><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>3.0.6</version></dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
4、修改springcloud-api工程
- pom.xml添加Feign的支持
- 新建一个service包
- 编写接口 DeptClientService.class,并增加新的注解
package com.vinjcent.springcloud.service;import com.vinjcent.springcloud.pojo.Department;import org.springframework.cloud.openfeign.FeignClient;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;//value为对应调用的服务名称@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")public interface DeptClientService { @GetMapping("/dept/get/{id}") public Department queryById(@PathVariable("id") Long id); @GetMapping("/dept/list") public List<Department> queryByAll(); @PostMapping("/dept/add") public int addDept(Department department);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
5.修改工程springcloud-consumer-dept-feign的controller
修改前
package com.vinjcent.springcloud.controller;import com.vinjcent.springcloud.pojo.Department;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import java.util.List;@RestController@SuppressWarnings("all")public class DeptConsumerController { // 理解: 消费者,不应该有service层 // 支持RestFul RestTemplate 供我们直接调用 // (url,实体:map,Class<T> responseType) @Autowired private RestTemplate restTemplate; // 提供多种便捷访问远程http服务的方法,简单的Restful服务模板 // Ribbon,我们这里的地址,应该是一个变量,通过服务名来访问// private static final String REST_URL_PREFIX = "http://localhost:8001"; private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT"; @RequestMapping("/consumer/dept/list") public List<Department> getAll(){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class); } @RequestMapping("/consumer/dept/add") public int add(Department department){ return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",department,int.class); } @RequestMapping("/consumer/dept/get/{id}") public Department getDept(@PathVariable("id")Long id){ return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Department.class); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
修改后
package com.vinjcent.springcloud.controller;import com.vinjcent.springcloud.pojo.Department;import com.vinjcent.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@SuppressWarnings("all")public class DeptConsumerController { @Autowired private DeptClientService service; @RequestMapping("/consumer/dept/list") public List<Department> getAll(){ return this.service.queryByAll(); } @RequestMapping("/consumer/dept/add") public int add(Department department){ return this.service.addDept(department); } @RequestMapping("/consumer/dept/get/{id}") public Department getDept(@PathVariable("id")Long id){ return this.service.queryById(id); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
6.修改主启动类DeptConsumerFeign,增加一个注解
@EnableFeignClients(basePackages = {“com.vinjcent.springcloud”}),扫描Feign接口
@SpringBootApplication@EnableEurekaClient@EnableFeignClients(basePackages = {"com.vinjcent.springcloud"})public class FeignDeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(FeignDeptConsumer_80.class,args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
测试
访问:
结果:Feign自带负载均衡配置项
小结
-
Feign通过接口的方法调用Rest服务 ( 之前是 Ribbon + RestTemplate )
-
该请求发送给Eureka服务器 (
-
通过Feign直接找到服务接口,由于在进行服务调用的时候融合了Ribbon技术,所以也支持负载均衡作用!
-
Feign其实不是做负载均衡的,负载均衡是ribbon的功能,feign只是集成了Ribbon而已,但是负载均衡的功能还是Feign内置的Ribbon再做,而不是Feign
Feign的作用的替代RestTemplate,性能比较低,但是可以使代码可读性很强
Feign 和 Ribbon 的比较
Ribbon:Ribbon 是一个基于 HTTP 和 TCP 客户端的负载均衡器它可以在客户端配置 ribbonServerList(服务端列表),然后轮询请求以实现均衡负载它在联合 Eureka 使用时ribbonServerList 会被 DiscoveryEnabledNIWSServerList 重写,扩展成从 Eureka 注册中心获取服务端列表同时它也会用 NIWSDiscoveryPing 来取代 IP,它将职责委托给 Eureka 来确定服务端是否已经启动。 使用 HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐
用法
-
RestTemplate注入到容器中,同时使用注解,注解使用默认负载均衡算法(可以使用自定义)
-
用 REST_URL_PREFIX 指定请求地址 , 使用 restTemplate 模拟 HTTP请求。,一般是提供者注册的应用名(也是工程名)
Feign :是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的方式, 只需要创建一个接口,面向接口;然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建HTTP请求。然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写客户端变得非常容易。类似于 mybatis 的 注解
8. 熔断服务
8.1 Hystrix概述
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败!
服务雪崩
如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对该微服务的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的 雪崩效应
什么是Hystrix?
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性
“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩
对此我们应该在可能出现出错或者阻塞的地方使用熔断,通过备用方法顶替出错的地方
有什么用?
- 服务降级
- 服务熔断
- 服务限流
- 接近实时的监控
当一切正常时,请求流可以如下所示
当许多后端系统中有一个潜在阻塞服务时,它可以阻止整个用户请求
随着大容量通信量的增加,单个后端依赖项的潜在性会导致所有服务器上的所有资源在几秒钟内饱和
应用程序中通过网络或客户端库可能导致网络请求的每个点都是潜在故障的来源。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,从而备份队列、线程和其他系统资源,从而导致更多跨系统的级联故障
当使用Hystrix包装每个基础依赖项时,上面的图表中所示的体系结构会发生类似于以下关系图的变化。每个依赖项是相互隔离的,限制在延迟发生时它可以填充的资源中,并包含在回退逻辑中,该逻辑决定在依赖项中发生任何类型的故障时要做出什么样的响应
8.2 服务熔断
什么是服务熔断
熔断机制是对应雪崩效应的一种微服务链路保护机制
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回 错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制
熔断机制的注解是
服务熔断可以解决的问题:
- 当所依赖的对象不稳定时,能够起到快速失败的目的
- 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复
8.3 测试
1.参考springcloud-provider-dept-8001,新建springcloud-provider-dept-hystrix-8001
将之前8001的所有东西拷贝一份
2.修改pom.xml
文件,添加依赖
<!--Hystrix熔断机制--><!-- 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.10.RELEASE</version></dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3.修改yml配置文件,修改eureka实例的id
# Eureka的配置,服务注册到哪里# defaultZone: http://localhost:7001/eureka/eureka: client: service-url: defaultZone: http://DESKTOP-QODU2TD:7001/eureka/,http://localhost:7002/eureka/,http://127.0.0.1:7003/eureka/ instance: instance-id: springcloud-provider-dept-hystrix-8001 # 修改eureka上的默认描述信息 prefer-ip-address: true
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
prefer-ip-address: true不显示localhost,显示我们当前的主机的IP
4.修改Controller
package com.vinjcent.springcloud.controller;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;import com.vinjcent.springcloud.pojo.Department;import com.vinjcent.springcloud.service.DepartmentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.*;import java.util.List;// 提供Restful服务@RestController@SuppressWarnings("all")public class DepartmentController { @Autowired private DepartmentService departmentService; /** * 根据id查询部门信息 * HystrixCommand:一旦调用该方法发生异常后,就执行hystrixGet方法中的代码 * @Param:id */ @GetMapping("/dept/get/{id}") @HystrixCommand(fallbackMethod = "hystrixGet") public Department get(@PathVariable("id") Long id){ Department department = departmentService.queryById(id); if (department == null){ throw new RuntimeException("id ===> "+id+",不存在该用户,或者信息无法找到!"); } return department; } //备选方案 public Department hystrixGet(@PathVariable("id") Long id){ return new Department() .setDeptId(id) .setDeptName("id ===> "+id+",不存在该用户,或者信息无法找到!Hystrix") .setDb_source("No this database in MySQL"); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
5.为主启动类添加熔断注解
// 启动类@SpringBootApplication@EnableEurekaClient //服务启动后自动注册到Eureka中@EnableDiscoveryClient //务发现@EnableHystrix //添加对熔断的支持public class DeptProviderHystrix_8001 { public static void main(String[] args) { SpringApplication.run(DeptProviderHystrix_8001.class,args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
测试
1、启动Eureka集群
2、启动主启动类 DeptProviderHystrix8001
3、启动客户端 springcloud-consumer-dept-80
4、访问
使用熔断后,当访问一个不存在的id时,显示如下
不使用熔断的话
因此,为了避免因某个微服务后台出现异常或错误而导致整个应用或网页报错,使用熔断是必要的
8.4 降级服务
1. 什么是服务降级?
整体资源快不够了,忍痛先将某些服务关闭,等度过难关,再开启回来
服务降级是指当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务
资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。比如当双11活动时,把交易无关的服务统统降级,如查看蚂蚁深林,查看历史订单等等
服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要或不紧急的服务或任务进行服务的延迟使用或暂停使用
如上图,当某一时间内。A的访问量暴增,而B和C的访问量较少,这时候就要B和C关闭一些服务功能,去承担A的部分服务,从而为A分担压力,这叫做服务降级
2. 服务降级需要考虑的问题
1)哪些服务是核心服务,哪些服务是非核心服务
2)哪些服务可以支持降级,那些服务不能支持降级,降级策略是什么
3)除服务降级之外是否存在更复杂的业务放通场景,策略是什么?
3. 自动降级分类
- 1)超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
- 2)失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
- 3)故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
- 4)限流降级:秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)
服务降级处理是在客户端实现完成的,与服务端没有关系
测试用例
- 修改springcloud-api工程,根据已经有的DeptClientService接口新建一个实现了FallbackFactory接口的类
DeptClientServiceFallbackFactory
package com.vinjcent.springcloud.service;import com.vinjcent.springcloud.pojo.Department;import org.springframework.cloud.openfeign.FallbackFactory;import org.springframework.stereotype.Component;import java.util.List;//降级@Componentpublic class DeptClientServiceFallbackFactory implements FallbackFactory { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public Department queryById(Long id) { return new Department() .setDeptId(id) .setDeptName("id ===> "+id+"没有对应的信息,客户端提供了降级的信息,这个服务现在已经被关闭了") .setDb_source("No this database found"); } @Override public List<Department> queryByAll() { return null; } @Override public int addDept(Department department) { return 0; } }; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 修改springcloud-api工程,DeptClientService接口在注解 中添加fallbackFactory属性值
- 因为版本问题,我们同样需要在spring-cloud-consumer-feign导入Hystrix的依赖
<!--Hystrix熔断机制--><!-- 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.10.RELEASE</version></dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 修改工程spring-cloud-consumer-feign中yml开启降级
server: port: 80# 开启降级Feign.hystrixfeign: circuitbreaker: enabled: true# Eureka配置eureka: client: register-with-eureka: false # 不向Eureka注册自己 service-url: defaultZone: http://DESKTOP-QODU2TD:7001/eureka/,http://localhost:7002/eureka/,http://127.0.0.1:7003/eureka/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 在工程spring-cloud-consumer-feign的启动类上加上注解
// Ribbon 和 Eureka 整合以后,客户端可以直接调用访问,不用关心IP地址和端口号@SpringBootApplication@EnableEurekaClient@EnableFeignClients(basePackages = {"com.vinjcent.springcloud"})@EnableHystrix //添加对熔断的支持public class FeignDeptConsumer_80 { public static void main(String[] args) { SpringApplication.run(FeignDeptConsumer_80.class,args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 启动eureka集群7001
- 启动 springcloud-provider-dept-8001
- 启动 springcloud-consumer-dept-feign-80
- 正常访问测试
- 故意关闭微服务启动 springcloud-provider-dept-8001
- 客户端自己调用提示
此时服务端provider已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时
也会获得提示信息而不会挂起耗死服务器
小结
服务熔断 ===> 服务端:一般是某个服务故障或者异常引起,类似现实世界中的 “保险丝” , 当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时!
服务降级 ===> 客户端:所谓降级,一般是从整体负荷考虑,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的FallbackFactory回调,返回一个缺省值。这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强
- 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
- 实现方式不太一样,服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断
熔断,降级,限流:
- 熔断:依赖的下游服务器故障触发熔断,避免引发本地系统崩溃,系统自动执行和恢复
- 降级:服务分优先级,牺牲非核心业务,保证核心服务稳定,从整体符合考虑
- 限流:限制并发的请求访问量,超过阈值则拒绝
8.5 服务监控(hystrixDashboard)
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求,多少成功,多少失败等等
Netflix 通过 hystrix-metrics-event-stream 项目实现了对以上指标的监控 SpringCloud 也提供了 HystrixDashboard 的整合,对监控内容转化成可视化界面!
测试用例
新建工程springcloud-consumer-hystrix-dashboard-9001
- pom.xml 复制之前的80依赖,再添加两个依赖
<!--实体类 + web--><dependencies> <dependency> <groupId>com.vinjcent</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--Eureka客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>3.0.4</version> </dependency> <!--Hystrix熔断机制--> <!-- 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.10.RELEASE</version> </dependency> <!--Hystrix监控--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.10.RELEASE</version> </dependency></dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 主启动类改名 + 新注解
@SpringBootApplication@EnableHystrixDashboardpublic class DeptConsumerDashboard_9001 { public static void main(String[] args) { SpringApplication.run(DeptConsumerDashboard_9001.class,args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 所有的Provider微服务提供类(8001/8002/8003) 都需要监控依赖配置(只有实现了熔断机制的工程才会起作用)
<!--Hystrix熔断机制--><!-- 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.10.RELEASE</version></dependency><!--Hystrix监控--><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.10.RELEASE</version></dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 在 springcloud-provider-dept-hystrix-8001 启动类中增加一个bean,添加监控注解@EnableHystrixDashboard,只有带有HystrixCommand的工程才会被监控到
// 启动类@EnableEurekaClient //服务启动后自动注册到Eureka中@EnableDiscoveryClient //服务发现@EnableHystrix //添加对熔断的支持@EnableHystrixDashboard //要保证提供者都有actuator这个依赖@SpringBootApplicationpublic class DeptProviderHystrix_8001 { public static void main(String[] args) { SpringApplication.run(DeptProviderHystrix_8001.class,args); } //增加一个Servlet @Bean public ServletRegistrationBean servletRegistrationBean(){ ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet()); registrationBean.addUrlMappings("/actuator/hystrix.stream"); return registrationBean; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- springcloud-consumer-hystrix-dashboard-9001中的yml里添加
server: port: 9001# 设置熔断监控允许代理的列表hystrix: dashboard: proxy-stream-allow-list: "localhost"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
测试一
-
启动eureka集群7001
-
启动springcloud-consumer-hystrix-dashboard-9001
-
启动springcloud-provider-dept-hystrix-8001
-
访问
-
访问 【查看1秒一动的数据流】
- 访问http://localhost:9001/hystrix,将上图地址填入
监控显示
参数数据解释
9. Zuul路由网关
Zuul概述
1. 什么是Zuul
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul 和 Eureka 进行整合,将 Zuul 自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得其他微服务的消息,也即以后的访问微服务都是通过 Zuul 跳转后获得
注意:Zuul 服务最终还是会注册进 Eureka
提供:代理 + 路由 + 过滤 三大功能!
2. Zuul能干嘛
- 路由
- 过滤
官方文档:
测试用例
由于版本问题,需要将SpringCloud和SpringBoot版本降低
<!--SpringCloud的依赖--><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies --><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope></dependency><!--SpringBoot--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.4.RELEASE</version> <type>pom</type> <scope>import</scope></dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 新建springcloud-zuul_9527模块,并导入依赖
<!--实体类 + web--><dependencies> <dependency> <groupId>com.vinjcent</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--Eureka客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.2.10.RELEASE</version> </dependency> <!--Hystrix熔断机制--> <!-- 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.10.RELEASE</version> </dependency> <!--Hystrix监控--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.10.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-zuul --> <!--Zuul路由网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> <version>2.2.10.RELEASE</version> </dependency></dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 配置yml
server: port: 9527# 应用名称spring: application: name: spring-zuul# 在eureka服务中心注册eureka: client: service-url: defaultZone: http://DESKTOP-QODU2TD:7001/eureka/,http://localhost:7002/eureka/,http://127.0.0.1:7003/eureka/ instance: instance-id: zuul9527.com prefer-ip-address: trueinfo: app.name: vinjcent-springcloud company.name: blog.vinjcent.com
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 创建主启动类,并增加注解@EnableZuulProxy
@SpringBootApplication@EnableZuulProxypublic class ZuulApplication_9527 { public static void main(String[] args) { SpringApplication.run(ZuulApplication_9527.class,args); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 修改hosts文件
路径:C:\Windows\System32\drivers\etc\hosts
测试
启动
- 一个Eureka集群
- 一个服务提供类:springcloud-provider-dept-hystrix-8001
- zuul路由:springcloud-zuul_9527
访问 :http://localhost:7001/ 可以看到Zuul路由被注册到网关中心了
不用路由访问 :http://localhost:8001/dept/get/1
使用路由 :http://www.vinjcent.com:9527/springcloud-provider-dept/dept/get/1
网关 / 微服务名字 / 具体的服务
路由访问映射规则
问题:
http://www.vinjcent.com:9527/springcloud-provider-dept/dept/get/1
这样去访问的话,就暴露了我们真实微服务的名称!这不是我们需要的!怎么处理呢?
修改:springcloud-zuul-9527 工程的yml文件
server: port: 9527# 应用名称spring: application: name: spring-zuul# 在eureka服务中心注册eureka: client: service-url: defaultZone: http://DESKTOP-QODU2TD:7001/eureka/,http://localhost:7002/eureka/,http://127.0.0.1:7003/eureka/ instance: instance-id: zuul9527.com prefer-ip-address: trueinfo: app.name: vinjcent-springcloud company.name: blog.vinjcent.com# 路由映射zuul: routes: mydept.serviceId: springcloud-provider-dept mydept.path: /mydept/**
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
配置前访问:
http://www.vinjcent.com:9527/springcloud-provider-dept/dept/get/1
配置后访问:
http://www.vinjcent.com:9527/mydept/dept/get/1
问题,现在访问原路径依旧可以访问!这也不是我们所希望的!
原真实服务名忽略
# Zuul路由映射zuul: routes: mydept.serviceId: springcloud-provider-dept mydept.path: /mydept/** ignored-services: springcloud-provider-dept # 不能再使用这个路径访问了
- 1
- 2
- 3
- 4
- 5
- 6
测试:
现在访问http://www.vinjcent.com:9527/springcloud-provider-dept/dept/get/1 就访问不了了
上面的例子中,我们只写了一个服务,那要是有多个需要隐藏,怎么办呢?
# Zuul路由映射zuul: routes: mydept.serviceId: springcloud-provider-dept mydept.path: /mydept/** ignored-services: "*" # 不能再使用这个路径访问了,ignored # 隐藏全部的应用名称
- 1
- 2
- 3
- 4
- 5
- 6
设置统一公共前缀
# Zuul路由映射zuul: routes: mydept.serviceId: springcloud-provider-dept mydept.path: /mydept/** ignored-services: "*" # 不能再使用这个路径访问了,ignored # 隐藏全部的应用名称 prefix: /vinjcent # 设置公共的前缀
- 1
- 2
- 3
- 4
- 5
- 6
- 7
访问:http://www.vinjcent.com:9527/vinjcent/mydept/dept/get/1,加上统一的前缀!否则,就访问不了!