1. Spring Cloud 综述
1.1 Spring Cloud 是什么
[百度百科]Spring Cloud是⼀系列框架的有序集合。它利⽤Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中⼼、消息总线、负载均衡、断路器、数据监控等,都可以⽤ Spring Boot的开发⻛格做到⼀键启动和部署。
Spring Cloud并没有重复制造轮⼦,它只是将⽬前各家公司开发的⽐较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot⻛格进⾏再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了⼀套简单易懂、易部署和易维护的分布式系统开发⼯具包。
Spring Cloud是⼀系列框架的有序集合
(Spring Cloud是⼀个规范)。
开发服务发现注册、配置中⼼、消息总线、负载均衡、断路器、数据监控等。利⽤Spring Boot的开发便利性简化了微服务架构的开发(⾃动装配)。
这⾥,我们需要注意,Spring Cloud其实是⼀套规范,是⼀套⽤于构建微服务架构的规范,⽽不是⼀个可以拿来即⽤的框架(所谓规范就是应该有哪些功能组件,然后组件之间怎么配合,共同完成什么事情)。在这个规范之下第三⽅的Netflix公司开发了⼀些组件、Spring官⽅开发了⼀些框架/组件,包括第三⽅的阿⾥巴巴开发了⼀套框架/组件集合Spring Cloud Alibaba,这些才是Spring Cloud规范的实现。
Netflix搞了⼀套 简称SCN。
Spring Cloud 吸收了Netflix公司的产品基础之上⾃⼰也搞了⼏个组件。
阿⾥巴巴在之前的基础上搞出了⼀堆微服务组件,Spring Cloud Alibaba(SCA)
1.2 Spring Cloud 解决什么问题
Spring Cloud 规范及实现意图要解决的问题其实就是
微服务架构实施过程中存在的⼀些问题,⽐如微服务架构中的服务注册发现问题、⽹络问题(⽐如熔断场景)、统⼀认证安全授权问题、负载均衡问题、链路追踪等问题
。
1.3 Spring Cloud 架构
如前所述,Spring Cloud是⼀个微服务相关规范,这个规范意图为搭建微服务架构提供⼀站式服务,采⽤组件(框架)化机制定义⼀系列组件,各类组件针对性的处理微服务中的特定问题,这些组件共同来构成Spring Cloud微服务技术栈。
1.3.1 Spring Cloud 核⼼组件
Spring Cloud ⽣态圈中的组件,按照发展可以分为第⼀代 Spring Cloud组件和第⼆代 Spring Cloud组件。
第⼀代 Spring Cloud (Netflix,SCN) | 第⼆代 Spring Cloud(主要就是Spring Cloud Alibaba,SCA) | |
---|---|---|
注册中⼼ | Netflix Eureka | 阿⾥巴巴 Nacos |
客户端负载均衡 | Netflix Ribbon | 阿⾥巴巴 Dubbo LB、Spring Cloud Loadbalancer |
熔断器 | Netflix Hystrix | 阿⾥巴巴 Sentinel |
⽹关 | Netflix Zuul:性能⼀般,未来将退出Spring Cloud ⽣态圈 | 官⽅ Spring Cloud Gateway |
配置中⼼ | 官⽅ Spring Cloud Config | 阿⾥巴巴 Nacos、携程 Apollo |
服务调⽤ | Netflix Feign | 阿⾥巴巴 Dubbo RPC |
消息驱动 | 官⽅ Spring Cloud Stream | |
链路追踪 | 官⽅ Spring Cloud Sleuth/Zipkin | |
阿⾥巴巴 seata 分布式事务⽅案 |
1.3.2 Spring Cloud 体系结构(组件协同⼯作机制)
Spring Cloud中的各组件协同⼯作,才能够⽀持⼀个完整的微服务架构。⽐如:
- 注册中⼼负责服务的注册与发现,很好将各服务连接起来。
- API⽹关负责转发所有外来的请求。
- 断路器负责监控服务之间的调⽤情况,连续多次失败进⾏熔断保护。
- 配置中⼼提供了统⼀的配置信息管理服务,可以实时的通知各个服务获取最新的配置信息。
1.4 Spring Cloud 与 Dubbo 对⽐
Dubbo是阿⾥巴巴公司开源的⼀个⾼性能优秀的服务框架,基于RPC调⽤,对于⽬前使⽤率较⾼的Spring Cloud Netflix来说,它是基于HTTP的,所以效率上没有Dubbo⾼,但问题在于Dubbo体系的组件不全,不能够提供⼀站式解决⽅案,⽐如服务注册与发现需要借助于Zookeeper等实现,⽽Spring Cloud Netflix则是真正的提供了⼀站式服务化解决⽅案,且有Spring⼤家族背景。
前些年,Dubbo使⽤率⾼于SpringCloud,但⽬前Spring Cloud在服务化/微服务解决⽅案中已经有了⾮常好的发展趋势。
1.5 Spring Cloud 与 Spring Boot 的关系
Spring Cloud 只是利⽤了Spring Boot 的特点,让我们能够快速的实现微服务组件开发,否则不使⽤Spring Boot的话,我们在使⽤Spring Cloud时,每⼀个组件的相关Jar包都需要我们⾃⼰导⼊配置以及需要开发⼈员考虑兼容性等各种情况。所以Spring Boot是我们快速把Spring Cloud微服务技术应⽤起来的⼀种⽅式。
2. 第⼀代 Spring Cloud 核⼼组件详解
由于⽹关组件Zuul性能⼀般,未来将退出Spring Cloud ⽣态圈,所以我们直接讲解GateWay,我们把GateWay划分到第⼀代Spring Cloud 核⼼组件这⼀部分了。
各组件整体结构如下:
从形式上来说,Feign⼀个顶三,Feign = RestTemplate + Ribbon + Hystrix
2.1 Eureka服务注册中⼼
2.1.1 关于服务注册中⼼
注意:服务注册中⼼本质上是为了解耦服务提供者和服务消费者。
对于任何⼀个微服务,原则上都应存在或者⽀持多个提供者(⽐如简历微服务部署多个实例),这是由微服务的分布式属性决定的。
更进⼀步,为了⽀持弹性扩缩容特性,⼀个微服务的提供者的数量和分布往往是动态变化的,也是⽆法预先确定的。因此,原本在单体应⽤阶段常⽤的静态LB机制就不再适⽤了,需要引⼊额外的组件来管理微服务提供者的注册与发现,⽽这个组件就是服务注册中⼼。
-
服务注册中⼼⼀般原理
分布式微服务架构中,服务注册中⼼⽤于存储服务提供者地址信息、服务发布相关的属性信息,消费者通过主动查询和被动通知的⽅式获取服务提供者的地址信息,⽽不再需要通过硬编码⽅式得到提供者的地址信息。消费者只需要知道当前系统发布了那些服务,⽽不需要知道服务具体存在于什么位置,这就是透明化路由。- 服务提供者启动
- 服务提供者将相关服务信息主动注册到注册中⼼
- 服务消费者获取服务注册信息:
pull模式:服务消费者可以主动拉取可⽤的服务提供者清单;
push模式:服务消费者订阅服务(当服务提供者有变化时,注册中⼼也会主动推送更新后的服务清单给消费者。 - 服务消费者直接调⽤服务提供者。
另外,注册中⼼也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除。 -
主流服务中⼼对⽐
- Zookeeper
Zookeeper它是⼀个分布式服务框架,是Apache Hadoop 的⼀个⼦项⽬,它主要是⽤来解决分布式应 ⽤中经常遇到的⼀些数据管理问题,如:统⼀命名服务、状态同步服务、集群管理、分布式应⽤配置项的管理等。
简单来说zookeeper本质=存储+监听通知。
znode节点
Zookeeper ⽤来做服务注册中⼼,主要是因为它具有节点变更通知功能,只要客户端监听相关服务节点,服务节点的所有变更,都能及时的通知到监听客户端,这样作为调⽤⽅只要使⽤ Zookeeper 的客户端就能实现服务节点的订阅和变更通知功能了,⾮常⽅便。另外,Zookeeper 可⽤性也可以,因为只要半数以上的选举节点存活,整个集群就是可⽤的。(集群最少3台) - Eureka
由Netflix开源,并被Pivatal集成到SpringCloud体系中,它是基于 RestfulAPI⻛格开发的服务注册与发现组件。 - Consul
Consul是由HashiCorp基于Go语⾔开发的⽀持多数据中⼼分布式⾼可⽤的服务发布和注册服务软件, 采⽤Raft算法保证服务的⼀致性,且⽀持健康检查。 - Nacos
Nacos是⼀个更易于构建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是 注册中⼼ + 配置中⼼的组合,帮助我们解决微服务开发必会涉及到的服务注册 与发现,服务配置,服务管理等问题。
Nacos 是Spring Cloud Alibaba 核⼼组件之⼀,负责服务注册与发现,还有配置。
组件名 语⾔ CAP 对外暴露接⼝ Eureka Java AP(⾃我保护机制,保证可⽤) HTTP Consul Go CP HTTP/DNS Zookeeper Java CP 客户端 Nacos Java ⽀持AP/CP切换 HTTP P:分区容错性(⼀定的要满⾜的)
C:数据⼀致性
A:⾼可⽤
CAP不可能同时满⾜三个,要么是AP,要么是CP - Zookeeper
2.1.2 服务注册中⼼组件 Eureka
- Eureka 基础架构
- Eureka 交互流程及原理
Eureka 包含两个组件:Eureka Server 和 Eureka Client,Eureka Client是⼀个Java客户端,⽤于简化与Eureka Server的交互;Eureka Server提供服务发现的能⼒,各个微服务启动时,会通过Eureka Client向Eureka Server 进⾏注册⾃⼰的信息(例如⽹络信息),Eureka Server会存储该服务的信息。- 图中us-east-1c、us-east-1d,us-east-1e代表不同的区也就是不同的机房;
- 图中每⼀个Eureka Server都是⼀个集群;
- 图中Application Service作为服务提供者向Eureka Server中注册服务,Eureka Server接受到注册事件会在集群和分区中进⾏数据同步,Application Client作为消费端(服务消费者)可以从Eureka Server中获取到服务注册信息,进⾏服务调⽤;
- 微服务启动后,会周期性地向Eureka Server发送⼼跳(默认周期为30秒)以续约⾃⼰的信息;
- Eureka Server在⼀定时间内没有接收到某个微服务节点的⼼跳,Eureka Server将会注销该微服务节点(默认90秒);
- 每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的⽅式完成服务注册列表的同步;
- Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使⽤缓存中的信息找到服务提供者。
Eureka通过⼼跳检测、健康检查和客户端缓存等机制,提⾼系统的灵活性、可伸缩性和可⽤性。
2.1.3 Eureka应⽤及⾼可⽤集群
1)单实例Eureka Server—>访问管理界⾯—>Eureka Server集群
2)服务提供者(简历微服务注册到集群)
3)服务消费者(⾃动投递微服务注册到集群/从Eureka Server集群获取服务信息)
4)完成调⽤
2.1.4 Eureka细节详解
-
Eureka元数据详解
Eureka的元数据有两种:标准元数据和⾃定义元数据。
标准元数据
:主机名、IP地址、端⼝号等信息,这些信息都会被发布在服务注册表中,⽤于服务之间的调⽤。
⾃定义元数据
:可以使⽤eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这 些元数据可以在远程客户端中访问。类似于:
instance: prefer-ip-address: true metadata-map: # ⾃定义元数据(kv⾃定义) cluster: cl1 region: rn1
-
Eureka客户端详解
服务提供者(也是Eureka客户端)要向EurekaServer注册服务,并完成服务续约等⼯作。服务注册详解(服务提供者)
- 当我们导⼊了eureka-client依赖坐标,配置Eureka服务注册中⼼地址
- 服务在启动时会向注册中⼼发起注册请求,携带服务元数据信息
- Eureka注册中⼼会把服务的信息保存在Map中。
服务续约详解(服务提供者)
服务每隔30秒会向注册中⼼续约(⼼跳)⼀次(也称为报活),如果没有续约,租约在90秒后到期,然后服务会被失效。
每隔30秒的续约操作我们称之为⼼跳检测。
往往不需要我们调整这两个配置:#向Eureka服务中⼼集群注册服务 eureka: instance: # 租约续约间隔时间,默认30秒 lease-renewal-interval-in-seconds: 30 # 租约到期,服务时效时间,默认值90秒,服务超过90秒没有发⽣⼼跳,EurekaServer会将服务从列表移除 lease-expiration-duration-in-seconds: 90
获取服务列表详解(服务消费者)
每隔30秒服务会从注册中⼼中拉取⼀份服务列表,这个时间可以通过配置修改。往往不需要我们调整#向Eureka服务中⼼集群注册服务 eureka: client: # 每隔多久拉取⼀次服务列表 registry-fetch-interval-seconds: 30
- 服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地
- 每隔30秒,会重新获取并更新数据
- 每隔30秒的时间可以通过配置eureka.client.registry-fetch-interval-seconds修改
-
Eureka服务端详解
服务下线
- 当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。
- 服务中⼼接受到请求后,将该服务置为下线状态。
失效剔除
Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进⾏检查,如果发现实例在在⼀定时间(此值由客户端设置的eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到⼼跳,则会注销此实例。⾃我保护
服务提供者 —> 注册中⼼
定期的续约(服务提供者和注册中⼼通信),假如服务提供者和注册中⼼之间的⽹络有点问题,不代表服务提供者不可⽤,不代表服务消费者⽆法访问服务提供者。如果在15分钟内超过85%的客户端节点都没有正常的⼼跳,那么Eureka就认为客户端与注册中⼼出现了⽹络故障,Eureka Server⾃动进⼊⾃我保护机制。
为什么会有⾃我保护机制?
默认情况下,如果Eureka Server在⼀定时间内(默认90秒)没有接收到某个微服务实例的⼼跳,Eureka Server将会移除该实例。但是当⽹络分区故障发⽣时,微服务与Eureka Server之间⽆法正常通信,⽽微服务本身是正常运⾏的,此时不应该移除这个微服务,所以引⼊了⾃我保护机制。
服务中⼼⻚⾯会显示如下提示信息:
当处于⾃我保护模式时:- 不会剔除任何服务实例(可能是服务提供者和EurekaServer之间⽹络问题),保证了⼤多数服务依然可⽤
- Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可⽤,当⽹络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
- 在Eureka Server⼯程中通过eureka.server.enable-self-preservation配置可⽤关停⾃我保护,默认值是打开。
eureka: server: enable-self-preservation: false # 关闭⾃我保护模式(缺省为打开)
经验:建议⽣产环境打开⾃我保护机制
2.2 Ribbon负载均衡
2.2.1 关于负载均衡
负载均衡⼀般分为服务器端负载均衡和客户端负载均衡。
所谓服务器端负载均衡,⽐如Nginx、F5这些,请求到达服务器之后由这些负载均衡器根据⼀定的算法将请求路由到⽬标服务器处理。
所谓客户端负载均衡,⽐如我们要说的Ribbon,服务消费者客户端会有⼀个服务器地址列表,调⽤⽅在请求前通过⼀定的负载均衡算法选择⼀个服务器进⾏访问,负载均衡算法的执⾏是在请求客户端进⾏。
Ribbon是Netflix发布的负载均衡器。Eureka⼀般配合Ribbon进⾏使⽤,Ribbon利⽤从Eureka中读取到服务信息,在调⽤服务提供者提供的服务时,会根据⼀定的算法进⾏负载。
2.2.2 Ribbon⾼级应⽤
不需要引⼊额外的Jar坐标,因为在服务消费者中我们引⼊过eureka-client,它会引⼊Ribbon相关Jar。
代码中使⽤如下,在RestTemplate上添加对应注解即可:
@Bean
// Ribbon负载均衡
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
2.2.3 Ribbon负载均衡策略
Ribbon内置了多种负载均衡策略,内部负责负载均衡的顶级接⼝为com.netflix.loadbalancer.IRule ,类树如下:
负载均衡策略 | 描述 |
---|---|
RoundRobinRule:轮询策略 | 默认超过10次获取到的server都不可⽤,会返回⼀个空的server |
RandomRule:随机策略 | 如果随机到的server为null或者不可⽤的话,会while不停的循环选取 |
RetryRule:重试策略 | ⼀定时限内循环重试。默认继承RoundRobinRule,也⽀持⾃定义注⼊,RetryRule会在每次选取之后,对选举的server进⾏判断,是否为null,是否alive,并且在500ms内会不停的选取判断。⽽RoundRobinRule失效的策略是超过10次,RandomRule是没有失效时间的概念,只要serverList没都挂。 |
BestAvailableRule:最⼩连接数策略 | 遍历serverList,选取出可⽤的且连接数最⼩的⼀个server。该算法⾥⾯有⼀个LoadBalancerStats的成员变量,会存储所有server的运⾏状况和连接数。如果选取到的server为null,那么会调⽤RoundRobinRule重新选取。1(1) 2(1)3(1) |
AvailabilityFilteringRule:可⽤过滤策略 | 扩展了轮询策略,会先通过默认的轮询选取⼀个server,再去判断该server是否超时可⽤,当前连接数是否超限,都成功再返回。 |
ZoneAvoidanceRule:区域权衡策略(默认策略) | 扩展了轮询策略,继承了2个过滤器:ZoneAvoidancePredicate和AvailabilityPredicate,除了过滤超时和链接数过多的server,还会过滤掉不符合要求的zone区域⾥⾯的所有节点,AWS --ZONE 在⼀个区域/机房内的服务实例中轮询 |
修改负载均衡策略:
# 针对的被调⽤⽅微服务名称,不加就是全局⽣效
work-service-resume:
ribbon:
NFLoadBalancerRuleClassName:
com.netflix.loadbalancer.RandomRule #负载策略调整
2.2.4 Ribbon核⼼原理剖析
Ribbon⼯作原理:
重点:Ribbon给restTemplate添加了⼀个拦截器
思考:Ribbon在做什么:
当我们访问http://work-service-resume/resume/openstate/的时候,ribbon应该根据服务名work-service-resume获取到该服务的实例列表并按照⼀定的负载均衡策略从实例列表中获取⼀个实例Server,并最终通过RestTemplate进⾏请求访问。
Ribbon细节结构图(涉及到底层的⼀些组件/类的描述):
1)获取服务实例列表
2)从列表中选择⼀个server
图中核⼼是负载均衡管理器LoadBalancer(总的协调者,相当于⼤脑,为了做事情,协调四肢),围绕它周围的多有IRule、IPing等。
- IRule:是在选择实例的时候的负载均衡策略对象;
- IPing:是⽤来向服务发起⼼跳检测的,通过⼼跳检测来判断该服务是否可⽤;
- ServerListFilter:根据⼀些规则过滤传⼊的服务实例列表;
- ServerListUpdater:定义了⼀系列的对服务列表的更新操作。
2.3 Hystrix熔断器
属于⼀种容错机制。
2.3.1 微服务中的雪崩效应
什么是微服务中的雪崩效应呢?
微服务中,⼀个请求可能需要多个微服务接⼝才能实现,会形成复杂的调⽤链路。
扇⼊:代表着该微服务被调⽤的次数,扇⼊⼤,说明该模块复⽤性好
扇出:该微服务调⽤其他微服务的个数,扇出⼤,说明业务逻辑复杂
扇⼊⼤是⼀个好事,扇出⼤不⼀定是好事
在微服务架构中,⼀个应⽤可能会有多个微服务组成,微服务之间的数据交互通过远程过程调⽤完成。这就带来⼀个问题,假设微服务A调⽤微服务B和微服务C,微服务B和微服务C⼜调⽤其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调⽤响应时间过⻓或者不可⽤,对微服务A的调⽤就会占⽤越来越多的系统资源,进⽽引起系统崩溃,所谓的“雪崩效应”。
如图中所示,最下游简历微服务响应时间过⻓,⼤量请求阻塞,⼤量线程不会释放,会导致服务器资源耗尽,最终导致上游服务甚⾄整个系统瘫痪。
2.3.2 雪崩效应解决⽅案
从可⽤性可靠性着想,为防⽌系统的整体缓慢甚⾄崩溃,采⽤的技术⼿段;
下⾯,我们介绍三种技术⼿段应对微服务中的雪崩效应,这三种⼿段都是从系统可⽤性、可靠性⻆度出发,尽量防⽌系统整体缓慢甚⾄瘫痪。
-
服务熔断
熔断机制是应对雪崩效应的⼀种微服务链路保护机制。
我们在各种场景下都会接触到熔断这两个字。⾼压电路中,如果某个地⽅的电压过⾼,熔断器就会熔断,对电路进⾏保护。股票交易中,如果股票指数过⾼,也会采⽤熔断机制,暂停股票的交易。
同样,在微服务架构中,熔断机制也是起着类似的作⽤。当扇出链路的某个微服务不可⽤或者响应时间太⻓时,熔断该节点微服务的调⽤,进⾏服务的降级,快速返回错误的响应信息。当检测到该节点微服务调⽤响应正常后,恢复调⽤链路。注意
1)服务熔断重点在“断”,切断对下游服务的调⽤。
2)服务熔断和服务降级往往是⼀起使⽤的,Hystrix就是这样。
-
服务降级
通俗讲就是整体资源不够⽤了,先将⼀些不关紧的服务停掉(调⽤我的时候,给你返回⼀个预留的值,也叫做兜底数据),待渡过难关⾼峰过去,再把那些服务打开。
服务降级⼀般是从整体考虑,就是当某个服务熔断之后,服务器将不再被调⽤,此刻客户端可以⾃⼰准备⼀个本地的fallback回调,返回⼀个缺省值,这样做,虽然服务⽔平下降,但好⽍可⽤,⽐直接挂掉要强。
-
服务限流
服务降级是当服务出问题或者影响到核⼼流程的性能时,暂时将服务屏蔽掉,待⾼峰或者问题解决后再打开;但是有些场景并不能⽤服务降级来解决,⽐如秒杀业务这样的核⼼功能,这个时候可以结合服务限流来限制这些场景的并发/请求量。限流措施也很多,⽐如:
限制总并发数
(⽐如数据库连接池、线程池)限制瞬时并发数
(如nginx限制瞬时并发连接数)限制时间窗⼝内的平均速率
(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)限制远程接⼝调⽤速率、限制MQ的消费速率等
2.3.3 Hystrix简介
[来⾃官⽹]Hystrix(豪猪----->刺),宣⾔“defend your app”是由Netflix开源的⼀个延迟和容错库,⽤于隔离访问远程系统、服务或者第三⽅库,防⽌级联失败,从⽽提升系统的可⽤性与容错性。
Hystrix主要通过以下⼏点实现延迟和容错:
包裹请求
:使⽤HystrixCommand包裹对依赖的调⽤逻辑。 ⾃动投递微服务⽅法(@HystrixCommand 添加Hystrix控制) ——调⽤简历微服务跳闸机制
:当某服务的错误率超过⼀定的阈值时,Hystrix可以跳闸,停⽌请求该服务⼀段时间。资源隔离
:Hystrix为每个依赖都维护了⼀个⼩型的线程池(舱壁模式)(或者信号量)。如果该线程池已满, 发往该依赖的请求就被⽴即拒绝,⽽不是排队等待,从⽽加速失败判定。监控
:Hystrix可以近乎实时地监控运⾏指标和配置的变化,例如成功、失败、超时、以及被拒绝 的请求等。回退机制
:当请求失败、超时、被拒绝,或当断路器打开时,执⾏回退逻辑。回退逻辑由开发⼈员 ⾃⾏提供,例如返回⼀个缺省值。⾃我修复
:断路器打开⼀段时间后,会⾃动进⼊“半开”状态。
2.3.4 Hystrix熔断应⽤
⽬的:简历微服务⻓时间没有响应,服务消费者—>⾃动投递微服务快速失败给⽤户提示。
只需要在服务调用端添加熔断策略
,服务提供方不需要做任何修改。
- 服务消费者⼯程(⾃动投递微服务)中引⼊Hystrix依赖坐标(也可以添加在⽗⼯程中)
- 服务消费者⼯程(⾃动投递微服务)的启动类中添加熔断器开启注解@EnableCircuitBreaker
- 定义服务降级处理⽅法,并在业务⽅法上使⽤@HystrixCommand的fallbackMethod属性关联到服务降级处理⽅法。
注意:
降级(兜底)⽅法
必须和被降级⽅法
相同的⽅法签名(相同参数列表、相同返回值) - 可以在类上使⽤@DefaultProperties注解统⼀指定整个类中共⽤的降级(兜底)⽅法
2.3.5 Hystrix舱壁模式(线程池隔离策略)
问题:
如果不进⾏任何设置,所有熔断⽅法使⽤⼀个Hystrix线程池(10个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可⽤导致的,⽽是我们的线程机制导致的,如果⽅法A的请求把10个线程都⽤了,⽅法2请求处理的时候压根都没法去访问B,因为没有线程可⽤,并不是B服务不可⽤。
为了避免问题服务请求过多导致正常服务⽆法访问,Hystrix不是采⽤增加线程数,⽽是单独的为每⼀个控制⽅法创建⼀个线程池
的⽅式,这种模式叫做"舱壁模式
",也是线程隔离的⼿段。
2.3.6 Hystrix⼯作流程与⾼级应⽤
- 当调⽤出现问题时,开启⼀个时间窗(10s)
- 在这个时间窗内,统计调⽤次数是否达到最⼩请求数?
如果没有达到,则重置统计信息,回到第1步。
如果达到了,则统计失败的请求数占所有请求数的百分⽐,是否达到阈值?
如果达到,则跳闸(不再请求对应服务)。
如果没有达到,则重置统计信息,回到第1步。- 如果跳闸,则会开启⼀个活动窗⼝(默认5s),每隔5s,Hystrix会让⼀个请求通过,到达那个问题服务,看 是否调⽤成功,如果成功,重置断路器回到第1步,如果失败,回到第3步。
-
跳闸逻辑设置:
/** * 8秒钟内,请求次数达到2个,并且失败率在50%以上,就跳闸 * 跳闸后活动窗⼝设置为3s */ @HystrixCommand( commandProperties = { @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "8000"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "2"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "3000") } )
-
我们上述通过注解进⾏的配置也可以配置在配置⽂件中:
# 配置熔断策略: hystrix: command: default: circuitBreaker: # 强制打开熔断器,如果该属性设置为true,强制断路器进⼊打开状态,将会拒绝所有的请求。 默认false关闭的 forceOpen: false # 触发熔断错误⽐例阈值,默认值50% errorThresholdPercentage: 50 # 熔断后休眠时⻓,默认值5秒 sleepWindowInMilliseconds: 3000 # 熔断触发最⼩请求次数,默认值是20 requestVolumeThreshold: 2 execution: isolation: thread: # 熔断超时设置,默认为1秒 timeoutInMilliseconds: 2000
-
基于springboot的健康检查观察跳闸状态(⾃动投递微服务暴露健康检查细节)
# springboot中暴露健康检查等断点接⼝ management: endpoints: web: exposure: include: "*" # 暴露健康接⼝的细节 endpoint: health: show-details: always
访问健康检查接⼝:http://localhost:8090/actuator/health
hystrix正常⼯作状态:
跳闸状态:
活动窗⼝内⾃我修复:
2.3.7 Hystrix Dashboard断路监控仪表盘
正常状态是UP,跳闸是⼀种状态CIRCUIT_OPEN,可以通过/health查看,前提是⼯程中需要引⼊SpringBoot的actuator(健康监控),它提供了很多监控所需的接⼝,可以对应⽤系统进⾏配置查看、相关功能统计等。
已经统⼀添加在⽗⼯程中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
如果我们想看到Hystrix相关数据,⽐如有多少请求、多少成功、多少失败、多少降级等,那么引⼊SpringBoot健康监控之后,访问/actuator/hystrix.stream接⼝可以获取到监控的⽂字信息,但是不直观,所以Hystrix官⽅还提供了基于图形化的DashBoard(仪表板)监控平 台。Hystrix仪表板可以显示每个断路器(被@HystrixCommand注解的⽅法)的状态。
-
新建⼀个监控服务⼯程,导⼊依赖
<!--hystrix 仪表盘--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrixdashboard</artifactId> </dependency>
-
启动类添加@EnableHystrixDashboard激活仪表盘
@SpringBootApplication @EnableHystrixDashboard // 开启hystrix dashboard public class HystrixDashboardApplication9000 { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } }
-
配置 application.yml
server: port: 9000 Spring: application: name: lagou-cloud-hystrix-dashboard eureka: client: serviceUrl: # eureka server的路径 defaultZone: http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeureka serverb:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表 instance: #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip) prefer-ip-address: true #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress instance-id: ${ spring.cloud.client.ipaddress}:${ spring.application.name}:${ server.port}:@project.version@
-
在被监测的微服务中注册监控servlet(⾃动投递微服务,监控数据就是来⾃于这个微服务)
@Bean public ServletRegistrationBean getServlet(){ HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/actuator/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; }
被监控微服务发布之后,可以直接访问监控servlet,但是得到的数据并不直观,后期可以结合仪表盘更友好的展示。
-
访问测试http://localhost:9000/hystrix
输入监控的微服务端点地址,展示监控的详细数据,比如监控服务消费者http://locahost:8090/actuator/hystrix.stream百分⽐:
- 10s内错误请求百分⽐
实⼼圆:
- ⼤⼩:代表请求流量的⼤⼩,流量越⼤球越⼤
- 颜⾊:代表请求处理的健康状态,从绿⾊到红⾊递减,绿⾊代表健康,红⾊就代表很不健康
曲线波动图:
- 记录了2分钟内该⽅法上流量的变化波动图,判断流量上升或者下降的趋势
2.4 Feign远程调⽤组件
服务消费者调⽤服务提供者的时候使⽤RestTemplate技术:
存在不便之处
1)拼接url
2)restTmplate.getForObJect
这两处代码都⽐较模板化,能不能不让我们来写这种模板化的东⻄?
另外来说,拼接url⾮常的low,拼接字符串,拼接参数,很low还容易出错。
2.4.1 Feign简介
Feign
是Netflix开发的⼀个轻量级RESTful的HTTP服务客户端(⽤它来发起请求,远程调⽤的),是以Java接⼝注解的⽅式调⽤Http请求
,⽽不⽤像Java中通过封装HTTP请求报⽂的⽅式直接调⽤,Feign被⼴泛应⽤在Spring Cloud 的解决⽅案中。
类似于Dubbo,服务消费者拿到服务提供者的接⼝,然后像调⽤本地接⼝⽅法⼀样去调⽤,实际发出的是远程的请求。
- Feign可帮助我们更加便捷,优雅的调⽤HTTP API:不需要我们去拼接url,然后调⽤restTemplate的api。在SpringCloud中,使⽤Feign⾮常简单,创建⼀个接⼝(在消费者–服务调⽤⽅这⼀端),并在接⼝上添加⼀些注解,代码就完成了
- SpringCloud对Feign进⾏了增强,使Feign⽀持了SpringMVC注解(OpenFeign)
本质:封装了Http调⽤流程,更符合⾯向接⼝化的编程习惯,类似于Dubbo的服务调⽤。
Dubbo的调⽤⽅式其实就是很好的⾯向接⼝编程。
2.4.2 Feign配置应⽤
在服务调⽤者⼯程
(消费)创建接⼝(添加注解)
(效果)Feign = RestTemplate+Ribbon+Hystrix
-
服务消费者⼯程(⾃动投递微服务)中引⼊Feign依赖(或者⽗类⼯程)。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
服务消费者⼯程(⾃动投递微服务)启动类使⽤注解@EnableFeignClients添加Feign⽀持。
@SpringBootApplication @EnableDiscoveryClient // 开启服务发现 @EnableFeignClients // 开启Feign public class AutodeliverFeignApplication8092 { public static void main(String[] args) { SpringApplication.run(AutodeliverFeignApplication8092.class,args); } }
注意:此时去掉Hystrix熔断的⽀持注解@EnableCircuitBreaker即可包括引⼊的依赖,因为Feign会⾃动引⼊。
-
创建Feign接⼝。
// name:调⽤的服务名称,和服务提供者yml⽂件中spring.application.name保持⼀致 @FeignClient(name="work-service-resume") public interface ResumeFeignClient { //调⽤的请求路径 @RequestMapping(value = "/resume/openstate/{userId}", method=RequestMethod.GET) public Integer findResumeOpenState(@PathVariable(value ="userId") Long userId); }
注意:
- 1)@FeignClient注解的name属性⽤于指定要调⽤的服务提供者名称,和服务提供者yml⽂件中spring.application.name保持⼀致。
- 2)接⼝中的接⼝⽅法,就好⽐是远程服务提供者Controller中的Hander⽅法(只不过如同本地调⽤了),那么在进⾏参数绑定的时,可以使⽤@PathVariable、@RequestParam、@RequestHeader等,这也是OpenFeign对SpringMVC注解的⽀持,但是需要注意value必须设置,否则会抛出异常。
-
使⽤接⼝中⽅法完成远程调⽤(注⼊接⼝即可,实际注⼊的是接⼝的实现)。
@Autowired private ResumeFeignClient resumeFeignClient; @Test public void testFeignClient(){ Integer resumeOpenState = resumeFeignClient.findResumeOpenState(1545132l); System.out.println("=======>>>resumeOpenState:" + resumeOpenState); }
2.4.3 Feign对负载均衡的⽀持
Feign 本身已经集成了Ribbon依赖和⾃动配置,因此我们不需要额外引⼊依赖,可以通过 ribbon.xx 来进⾏全局配置,也可以通过服务名.ribbon.xx 来对指定服务进⾏细节配置配置(参考之前,此处略)。
Feign默认的请求处理超时时⻓1s,有时候我们的业务确实执⾏的需要⼀定时间,那么这个时候,我们就需要调整请求处理超时时⻓,Feign⾃⼰有超时设置,如果配置Ribbon的超时,则会以Ribbon的为准。
Ribbon设置:
#针对的被调⽤⽅微服务名称,不加就是全局⽣效
lagou-service-resume:
ribbon:
#请求连接超时时间
#ConnectTimeout: 2000
#请求处理超时时间
#ReadTimeout: 5000
#对所有操作都进⾏重试
OkToRetryOnAllOperations: true
####根据如上配置,当访问到故障请求的时候,它会再尝试访问⼀次当前实例(次数由MaxAutoRetries配置),
####如果不⾏,就换⼀个实例进⾏访问,如果还不⾏,再换⼀次实例访问(更换次数由MaxAutoRetriesNextServer配置),
####如果依然不⾏,返回失败信息。
MaxAutoRetries: 0 #对当前选中实例重试次数,不包括第⼀次调⽤
MaxAutoRetriesNextServer: 0 #切换实例的重试次数
NFLoadBalancerRuleClassName:
com.netflix.loadbalancer.RoundRobinRule #负载策略调整
2.4.4 Feign对熔断器的⽀持
-
在Feign客户端⼯程配置⽂件(application.yml)中开启Feign对熔断器的⽀持
# 开启Feign的熔断功能 feign: hystrix: enabled: true
Feign的超时时⻓设置那其实就上⾯Ribbon的超时时⻓设置
Hystrix超时设置(就按照之前Hystrix设置的⽅式就OK了)注意:
-
1)开启Hystrix之后,Feign中的⽅法都会被进⾏⼀个管理了,⼀旦出现问题就进⼊对应的回退逻辑处理。
-
2)针对超时这⼀点,当前有两个超时时间设置(Feign/hystrix),熔断的时候是根据这两个时间的最⼩值来进⾏的,即处理时⻓超过最短的那个超时时间了就熔断进⼊回退降级逻辑。
hystrix: command: default: execution: isolation: thread: ######### Hystrix的超时时⻓设置 timeoutInMilliseconds: 15000
-
-
⾃定义FallBack处理类(需要实现FeignClient接⼝)