笔记参考来源
尚硅谷 SpringCloud 第二季学习笔记【已完结】_yfstart的博客-CSDN博客_尚硅谷springcloud笔记
SpringCloud
cloud和boot版本要对应才能使用。可上官网查看
一、介绍
服务注册中心
- Eureka(停更)
- Zookeeper
- Consul
- Nacos(推荐)
服务调用
- Ribbon
- LoadBalancer
服务调用2
- OpenFeign
服务降级
- resilience4j(国外推荐)
- sentinel(国内)
服务网关
- gateway
服务配置
- Nacos
服务总线
- Nacos
二、微服务架构编码构建
(一)父工程POM
建完maven项目后:
- editor设置编码UTF-8
- 编译改成java8
- 开启注解功能
1.关于pom.xml
-
删除了<version>${lombok.version}</version> <version>${druid.version}</version> <version>${mybatis.spring.boot.version}</version>
2.Maven工程细节
Maven中的DependencyManagement和Dependencies
- DependencyManagement:
- Maven使用DependencyManagement元素提供一种管理依赖版本号的方式。通常在一个组织或者项目最顶层的父POM中看到dependencyManagement元素
- 使用pom.xml中的dependencyManagement元素能让所有在子项目中引用一个依赖而不用显式的列出版本号
- Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后使用dependencyManagement元素中指定的版本号
好处
- 多个子项目使用同一个依赖时,避免在每个使用的子项目都声明版本号,切换版本号只需在顶层父容器更新。
- 若某个子项目需要另外版本,只需声明version即可。
(二)Rest微服务工程构建
1.支付模块
-
建module √
-
改pom √
-
写yml √
-
主启动类 √
-
业务类 √
-
建表sql √
CREATE TABLE `payment` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `serial` varchar(200) DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
-
entities 实体类 √
-
dao √
-
service √
-
controller √
-
注意小点:
- mysql8.0驱动变了。
- 404 为路径错误,先检查代码controller层有没有错
2.消费者订单模块
- 什么是RestTemplate?
- 使用,有三个参数
- url:REST请求地址
- requestMap:请求参数
- ResponseBean.class:HTTP响应转换被转换成的对象类型
- 使用,有三个参数
3.工程重构
新建一个模块,把公共的实体类 : entities 提取出来,然后在各个模块的 pom 文件导入此模块。
三、SpringCloud Eureka服务注册与发现
(一)Eureka基础知识
1.什么是服务治理
服务与服务之间依赖关系比较复杂,管理复杂。
服务治理管理服务与服务之间依赖关系,实现服务调用、负载均衡、容错等,实现服务与注册。
2.什么是服务注册
Eureka采用CS设计架构,
服务器启动时,会把当前自己服务器的信息,如 服务地址通讯地址等以别名方式注册到注册中心上。
注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。
任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
3.Eureka两组件
- Eureka Server
- 提供服务注册服务
- 各个微服务节点通过配置启动后,会在EurekaServer中进行注册,EurekaServer服务注册表中将会存储所有可用服务节点的信息,服务节点信息可以在界面看到
- 提供服务注册服务
- Eureka Client
- 通过注册中心进行访问
- 客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)。
- 通过注册中心进行访问
(二)单机Eureka构建步骤
1.eurekaServer端服务注册中心
- 建module
- 改pom
- 写yml
- 写主启动类
- 测试
2.8001注册Eureka成为提供者
spring.application.name不能随便改,对应服务注册服务的名字。
自我保护机制?(todo)
关于注册信息是否保留
3.80注册Eureka成为消费者
在yml文件 要给spring.application.name 赋值,才能在服务注册中心注册服务,对应界面上的 application
!!注意!!:yml文件 配置属性的 空格和缩进 不能少!!
(二)集群Eureka构建步骤
1.Eureka集群原理说明
不用集群,容易出现单点故障
核心:高可用,搭建Eureka注册中心集群,实现负载均衡+故障容错
2.EurekaServer集群环境构建步骤
-
建module
-
改pom
-
修改C:\Windows\System32\drivers\etc的hosts文件,添加以下内容
####SpringCloud2022#### 127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com
3.8001发布到2台Eureka集群
改yml文件,将defaultZone修改成如下:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4.80发布到2台Eureka集群
改yml文件,将defaultZone修改成如下:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
5.测试01
先启动EurekaServer,7001/7002f 服务,再启动服务提供者 provide-8001 ,最后启动消费者80。
6.支付服务提供者8001集群环境构建
-
新建cloud-provider-payment8002
-
注入端口,输出端口信息
-
搭建集群后会出现消费者一直调用一个生产者的端口号
-
因为在消费者controller接口的URL写固定了,要改成消费者所在的微服务名称 CLOUD-PAYMENT-SERVICE
-
7.负载均衡
- 上述URL改了之后,注册中心不再暴露出具体端口,而是微服务名称,其里面有多个集群,消费者并不知道要去请求哪一个
- 因此要配置负载均衡
- @LoadBanlanced
- 该注解赋予了RestTemplate负载均衡能力
- 在配置类的RestTemplate Bean上使用
- 该注解赋予了RestTemplate负载均衡能力
- @LoadBanlanced
- Ribbon和Eureka整合后Consumer可以直接调用服务而不再关心地址和端口号,且该服务还有负载均衡功能。
(三)actuator微服务信息完善
问题:
- 微服务名称里面的集群 含有主机信息
- 微服务名称里面的集群 没有IP提示
instance:
prefer-ip-address: true #提示ip信息
instance-id: 8001 #更改服务名称
-
通过url查看状态
(四)服务发现DiIscovery
对于注册进Eureka里面的微服务,可以通过服务发现来获得该服务的信息
-
DiscoveryClient类,该类的实例可以获得注册进Eureka里面的微服务信息
-
在主启动类添加注解 @EnableDiscoveryClient 服务发现
-
DiscoverClient类实例的方法
- getServices():返回List< String>,里面有注册进Eureka里面的微服务名称
- getInstances(“微服务名称”):返回List< ServicesInstance>,返回获得微服务名称里面的集群的信息
- getServiceId :即微服务名称 CLOUD-PAYMENT-SERVICE
- getHost ip地址
- getPort 端口号
- getUri 访问地址 如:http://172.16.9.134:8002
(五)Eureka自我保护
1.故障现象
概述:保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
理解:某端定期向注册中心发送心跳包,但是由于网络延迟心跳包没有发送到注册中心,此时注册中心不会注销其服务,而是尝试保护其服务注册表中的信息。 自我保护机制可以防止 网络延迟 等问题。
2.导致原因
某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息
3.怎么禁止自我保护
配置文件
- 可以设置是否启动
- 可以设置Eureka客户端向服务器端发送心跳的时间间隔
- 可以设置Eureka服务端在收到最后一次心跳时间后等待时间上限
四 Zookeeper服务注册与发现
-
生产者:微服务模块 application.yml 配置服务注册中心的url
-
消费者:
-
临时节点与持久节点
- 临时的,服务不发送心跳,注册中心会移除该服务
可能问题
- 服务器版本与idea zookeeper不一致,要保证一直才能启动成功,注册成功
五 SpringCloud Consul服务注册与发现
(一) Consul简介
1. 是什么
提供了微服务系统中的服务治理、配置中心、控制总线等功能。功能可根据需要单独使用,也可以一起使用以构建全方位的服务网页,Consul提供了一种完整的服务网格解决方案。
2.能干嘛
- 服务发现:提供HTTP和DNS两种发现方式
- 健康监测:支持多种方式,HTTP、TCP、Dockers、Shell脚本定制化监控
- KV存储:Key、Value的存储方式
- 多数据中心:Consul支持多数据中心
- 可视化Web界面
(二) 安装并运行Consul
- 官网安装
https://learn.hashicorp.com/consul/getting-started/install.html
双击consul.exe运行
http://localhost:8500 可以访问Consul首页
(三) 服务提供者
(四) 服务消费者
(五)三个注册中心的异同
1.CAP
- C:Consistency (强一致性)
- A:Availability (可用性)
- P:Partition tolerance (分区容错性)
CAP理论关注粒度是数据,而不是整体系统设计的策略
(二) 经典CAP图
最多只能同时较好的满足两个。
CAP理论核心:一个分布式系统不可能同时很好满足一致性,可用性和分区容错性这三个需求,因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
- CA:单点集群,满足一致性,可用性的系统,通常在可扩展性上不强大
- CP:满足一致性,分区容忍必的系统,通常性能不是特别高
- AP:满足可用性,分区容忍性的系统,通常可能对一致性要求低一些
六 SpringCloud Ribbon负载均衡服务调用
(一) 概述
1.是什么
Spring Cloud Ribbon是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置如连接超时,重试等。简单地说,就是在配置文件中列出Load Banlance简称(LB)后面所有的机器,Ribbon会自动帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。很容易使用Ribbon实现自定义的负载均衡算法。
2.官网资料
https://github.com/Netflix/ribbon/wiki/Getting-Started
目前也进入维护模式
3.能干吗
- LB(负载均衡)
- LB是什么
- 作用:将用户请求平摊分配到多个服务上。目的:实现系统的高可用。Nginx也可实现负载均衡。
- Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别?
- Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。负载均衡由服务端实现
- Ribbon本地负载均衡,在调用服务接口时,会在注册中心上获取注册信息列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术
- Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。负载均衡由服务端实现
(二) Ribbon负载均衡演示
1.架构说明
- Ribbon工作时分为两步
- 先选择EurekaServer,优先选择在同一个区域内负载较少的server
- 再根据用户指定的策略,再从server取到的服务注册列表中选择一个地址;其中Ribbon提供了多种策略:如:轮询、随机和根据响应时间加权。
2.POM
spring-cloud-starter-netflix-eureka-client自带了spring-cloud-starter-ribbon的引用
3.二说RestTemplate的使用
- 官网
- https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html
- getForObject方法/getForEntity方法
- getForObject:
- 返回对象为响应体中数据转化成的对象,基本上可以理解为Json
- getForEntity
- 返回对象是ResponseEntity对象,包含了响应中的一些重要信息,如:响应头、响应状态码、响应体等
- getForObject:
(三) Ribbon核心组件IRule
1.IRule
IRule:根据特定算法中从服务列表中选取一个要访问的服务
- com.netflix.loadbalancer.RoundRobinRule:轮询
- com.netflix.loadbalancer.RandomRule:随机
- com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
- WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
- BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器
2.如何替换负载规则
- 修改cloud-consumer-order80
- 注意配置细节
- 不能放在主启动类所在的包下,因为**@SpringBootApplication包含了@ComponentScan**,它会扫描这个类所在的包及其子包
- 新建package
- MySelfRule规则类
- 上面包下新建MySelfRule规则类
- 主启动类添加@RibbonClient
- 测试
(四) Ribbon负载均衡算法
1.原理
负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始
2.RoundRobinRule源码
CAS、自旋锁
3.手写
- 在8001 controller 8002 controller 加上获取服务端口的方法
- 把80的配置类 @LoadBanlanced 去掉,保证是自己写的负载算法
- 新建包 lib
- 创建接口 LoadBanlance
- 实现类 MyLB
- OrderController
七 SpringCloud OpenFeign服务接口调用
(一) 概述
1.是什么
Feign是一个声明式WebService客户端,让编写WebService客户端更加简单,使用方法是定义一个服务接口然后在上面添加注解。
2.能干嘛
Feign旨使编写Java Http客户端变得更容易,前面在使用Ribbon + RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模板化的调用方法。但在实际开发中,对服务依赖的调用可能不止一处,往往一个接口被多处调用,所以通常会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。
Feign集成了Ribbon,利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
3.Feign和OpenFeign两者区别
Feign:
- Feign是SpringCloud组件中的一个轻量级Restful的HTTP服务客户端
- Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务
- Feign使用方式:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
OpenFeign:
- OpenFeign是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等
- @FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务
(二) OpenFeign使用步骤
接口+注解:微服务调用接口 + @FeignClient
1.新建cloud-consumer-feign-order80
2.pom.xml
3.applicaton.yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4.主启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
5.业务类
-
service
package com.atguigu.springcloud.service; import com.atguigu.springcloud.entities.CommonResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "CLOUD-PAYMENT-SERVICE") public interface PaymentFeignService { //此处地址对应服务方的地址 @GetMapping("/payment/get/{id}") CommonResult getPaymentById(@PathVariable("id") Long id); }
-
controller
package com.atguigu.springcloud.controller; import com.atguigu.springcloud.entities.CommonResult; import com.atguigu.springcloud.entities.Payment; import com.atguigu.springcloud.service.PaymentFeignService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController public class OrderFeignController { @Resource private PaymentFeignService paymentFeignService; @GetMapping("/consumer/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) { return paymentFeignService.getPaymentById(id); } }
6.测试
使用OpenFeign代替了restTemplate。简化开发。
(三) OpenFeign超时控制
1.模拟超时
消费服务 调用 服务,两个不同的服务,会产生超时
超时设置,故意设置超时演示出错情况
-
服务提供方8001、8002故意写暂停程序
//service public String paymentFeignTimeOut(); //serviceimpl @Override public String paymentFeignTimeOut() { System.out.println("*****paymentFeignTimeOut from port: " + serverPort); //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return serverPort; } //controller: @GetMapping(value = "/feign/timeout") public String paymentFeignTimeOut() { return paymentService.paymentFeignTimeOut(); }
-
服务消费方80添加超时方法,PaymentFeignService
//此处 地址 对应 8001 的controller的地址 @GetMapping(value = "/feign/timeout") String paymentFeignTimeOut();
-
服务消费方80添加超时方法,OrderFeignController
//此处地址是客户端访问的地址,调用了service层的方法,service层通过对应的地址调用了服务方对应的方法 @GetMapping(value = "/consumer/payment/feign/timeout") public String paymentFeignTimeOut() { return paymentFeignService.paymentFeignTimeOut(); }
2.分析
为什么自测的8001不报错,而80调用报错了呢?
- OpenFeign默认等待一秒钟,超过后报错
OpenFeign超时控制是什么?
- 默认Feign客户端只等待一秒钟,但是服务端处理需要超过一秒钟,导致Feign客户端不想等待,直接返回报错。为了避免这样情况,需要设置Feign客户端的超时控制。
OpenFeign默认支持Ribbon
YML文件需要开启OenFeign客户端超时控制
#todo 未生效,访问还是超时?
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectTimeout: 5000
3.OpenFeign日志打印功能
Feign提供了日志打印功能,我们可以通过配置调整日志级别,从而了解Feign中Http请求细节。说白就是对Feign接口的调用情况进行监控和输出
日志级别
-
NONE:默认的,不显示任何日志
-
BASIC:仅记录请求方法、URL、响应状态码及执行时间
-
HEADERS:除了BASIC定义的信息外,还有请求和响应的头信息
-
FULL:除了HEADERS中定义的信息外,还有请求和响应的正文及元数据
- 配置日志bean
- YML文件需要开启日志的Feign客户端
八 SpringCloud Hystrix断路器
(一) 概述
1.分布式系统面临的问题
复杂的分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败
服务雪崩:
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和,比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加、备份队列、线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统,所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩
2.Hystrix是什么
是一个用于处理分布式系统延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障后,通过断路器的故障监控(类似熔断保险丝),向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间等待或者抛出调用方无法处理的异常,这样保证了服务调用的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
3.Hystrix能干嘛
服务降级、服务熔断、接近实时的监控…
停更进维了…
官网资料:https://github.com/Netflix/Hystrix/wiki/How-To-Use github 看wiki
(二) Hystrix重要概念
1.服务降级
服务器忙,请稍后再试,不让客户端等待并返回一个友好提示 fallback
什么情况会出现降级?
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
2.服务熔断
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
就是保险丝:服务降级->进而熔断->恢复调用链路
3.服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
(三) hystrix案例
1.构建hystrix-payment8001
-
新建cloud-provider-hystrix-payment8001
pom.xml
-
application.yml
-
主启动类
-
业务类
- service
- controller
-
测试
- 启动eureka7001
- 启动cloud-provider-hystrix-payment8001
2.高并发测试
Jmeter可以进行压测测试,开启Jmeter,发送20000个请求访问paymentInfo_TimeOut服务
看演示结果:两个都在自己转圈圈,为什么会被卡死?
- tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理
3.构建hystrix-order80再压测
cloud-consumer-feign-hystrix-order80
- pom.xml
- application.yml
- 主启动类
- 业务类
- 测试
- 高并发测试
- 2W个线程(Jmeter发两万个请求) 压8001
- 消费端80微服务再去访问正常的OK微服务8001地址
- 消费者80
- 转圈圈等待
- 消费端报超时错误
- 高并发测试
4.故障现象和导致原因
- 8001同一层次的其它接口服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕
- 80此时调用8001,客户端响应缓慢,转圈圈
- 结论:因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
5.如何解决?解决的要求
超时导致服务器变慢(转圈):超时不再等待
出错(宕机或程序运行出错):出错要有兜底
解决:
- 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
- 对方服务(8001)宕机了,调用者(80)不能一直卡死等待,必须有服务降级
- 对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者)自己处理降级
(四) 服务降级
mythink:就是客户端调用的服务 服务端提供不了,防止客户端一直等待或服务器因错误宕机,去调用 一个 fallback 方法,返回报错信息,或者友好提示客户 系统出错或系统繁忙。称为服务降级。
降级配置:@HystrixCommand
8001先从自身找问题:设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要兜底的方法处理,作服务降级fallback
1.8001fallback
-
业务类启用
-
@HystrixCommand报异常后如何处理?
-
一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标好的fallbackMethod调用类中的指定方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ro05kqZD-1652014460556)(G:\a.typoraNotes\DevelopmentFramework\springcloud\img\服务降级处理方法.png)]
-
当前服务不可用了,做服务降级,兜底的方案都是paymentInfo_TimeOutHandler
-
-
主启动类激活
- 添加新注解@EnableCircuitBreaker (circuit breaker -> 断路器)
2.80fallback
80订单微服务,也可以更好的保护自己,依样画葫芦进行客户端降级保护
- 主启动:OrderHystrixMain80
- application.yml
- OrderHystrixController
- 记得把8001的服务降级取消,保证8001正常提供服务
- 说明
- 是否调用兜底fallback取决于 **@HystrixProperty中的value=“4000”,**只要这个value值大于服务端8001的睡眠时间或进来就直接异常,比如说99999。比如说10/0就会走80的fallback方法,与application.yml中配置的ribbon的那个feign超时时间以及hystrix的时间无关
- 服务降级可以用在服务端,也可以用在客户端,一般用在客户端
3.问题和解决方案
-
目前问题
- 每个业务方法对应一个兜底的方法,代码膨胀
- 统一和自定义分开
-
解决方案一 通用的和独享的各自分开,避免了代码膨胀,合理减少代码量。
- 每个方法配置一个? 不行,导致膨胀
- feign
- @DefaultPorperties(defaultFallback = “…”) 在类上面添加,在方法上面添加@HystrixCommand
- 方法上有@HystrixCommand,没有指定方法,则默认调用类上面注解@DefaultPorperties(defaultFallback = “…”) 的方法
- 方法上有@HystrixCommand,有指定方法,调用指定的服务降级方法
- 每个方法配置一个? 不行,导致膨胀
-
解决方案二
和业务逻辑混在一起,会显得混乱
-
服务降级,客户端去调用服务端,碰上服务端宕机或关闭,客户端调用自己的服务降级方法
-
本次案例服务降级处理是在客户端80实现完成的,与服务端8001没有关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
-
未来要应对的异常
- 运行
- 超时
- 宕机
-
再看业务类PaymentController
-
修改feign-hystrix-order80
根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新建一个类(PaymentFallbackServiceImpl)实现该接口,统一为接口里面的方法进行异常处理 todo:实现了解耦?
-
兜底fallback类
- 新建com.atguigu.springcloud.service.fallback.PaymentFallbackServiceImpl
- 改造 PaymentHystrixService:com.atguigu.springcloud.service.PaymentHystrixService,在@FeignClient() 注解加上fallback=xxx.class)
-
测试
- localhost/consumer/payment/hystrix/ok/1
- 能正常访问,把8001服务(服务宕机)停掉后,调用服务降级方法。
-
(五) 服务熔断
1.熔断是什么
熔断机制概述:
熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务出错不可用或者响应时间太长,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误的响应信息,当检测到该节点的微服务调用响应正常后,恢复调用链路
在SpringCloud框架里,熔断机制通过Hystrix实现,Hystrix会监控微服务调用的状况,当失败的调用到一定阈值,缺省是 5 秒内 20 此调用失败,就会启动熔断机制。熔断机制的注解是**@HystrixCommand**
大神论文:https://martinfowler.com/bliki/CircuitBreaker.html todo
总结:降解是思想,熔断是对降解的具体实现,但是降解的实现不止熔断一种
- 调用失败会触发降级,而降级会调用fallback方法
- 但无论如何降级的流程一定会先调用正常方法再调用fallback方法
- 假如单位时间内调用失败次数过多,也就是降级次数过多,则触发熔断
- 熔断以后就会跳过正常方法直接调用fallback方法
- 所谓“熔断后服务不可用”就是因为跳过了正常方法直接执行fallback
2.实操
修改:cloud-provider-hystrix-payment8001
//====服务熔断=====
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),// 是否开启断路器 todo属性在源码对应的类可以找到
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),// 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),// 失败率达到多少后
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
//hutool工具类 maven里导入的 common 依赖
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)