# springcloud **Repository Path**: xiao_long_ya/springcloud ## Basic Information - **Project Name**: springcloud - **Description**: springcloud代码 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-10-30 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SpringCloud SpringCloud是分布式微服务架构的一站式解决方法,是多种微服务架构落地技术的集合体,俗称微服务全家桶。SpringCloud 和 springCloud Alibaba 目前是最主流的微服务框架组合。 **SpringCloud和SpringBoot版本对应关系查看:https://start.spring.io/actuator/info** # 服务注册与发现 ## Eureka: ## 什么是服务治理: SpringCloud封装了Netflix公司开发的Eureka模块来实现服务治理 在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。 ## 什么是服务注册与发现: Eureka采用了CS的设计结构,Eureka Server服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。这点和zookeeper很相似 在服务注册与发现中,有一个注册中心。当服务器启却时候,会把当前自己服务器的信息比如服务地址適讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务適讯地址然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为便用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址)) ![https://img-blog.csdnimg.cn/img_convert/8da648937c52bff1a3690b6520c3f540.png](https://img-blog.csdnimg.cn/img_convert/8da648937c52bff1a3690b6520c3f540.png) > 官方停更不停用,以后可能用的越来越少。 Eureka 是 Netflix 开发的,一个基于 REST 服务的,服务注册与发现的组件,以实现中间层服务器的负载平衡和故障转移。 Eureka 分为 Eureka Server 和 Eureka Client及服务端和客户端。Eureka Server为注册中心,是服务端,而服务提供者和消费者即为客户端,消费者也可以是服务者,服务者也可以是消费者。同时Eureka Server在启动时默认会注册自己,成为一个服务,所以Eureka Server也是一个客户端,这是搭建Eureka集群的基础。 Eureka Client:一个Java客户端,用于简化与 Eureka Server 的交互(通常就是微服务中的客户端和服务端)。通过注册中心进行访问。是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(roundrobin)负载算氵去的负载均衡器 在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒) Eureka Server:提供服务注册服务,各个微服务节,通过配置启动后,会在Eureka Serverc中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点信息,服务节点的信息可以在界面中直观看到。 服务在Eureka上注册,然后每隔30秒发送心跳来更新它们的租约。如果客户端不能多次续订租约,那么它将在大约90秒内从服务器注册表中剔除。注册信息和更新被复制到集群中的所有eureka节点。来自任何区域的客户端都可以查找注册表信息(每30秒发生一次)来定位它们的服务(可能在任何区域)并进行远程调用 服务提供者向注册中心注册服务,并每隔30秒发送一次心跳,就如同人还活着存在的信号一样,如果Eureka在90秒后还未收到服务提供者发来的心跳时,那么它就会认定该服务已经死亡就会注销这个服务。这里注销并不是立即注销,而是会在60秒以后对在这个之间段内“死亡”的服务集中注销,如果立即注销,势必会对Eureka造成极大的负担。这些时间参数都可以人为配置。 Eureka还有自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,所以不会再接收心跳,也不会删除服务。 客户端消费者会向注册中心拉取服务列表,因为一个服务器的承载量是有限的,所以同一个服务会部署在多个服务器上,每个服务器上的服务都会去注册中心注册服务,他们会有相同的服务名称但有不同的实例id,所以拉取的是服务列表。我们最终通过负载均衡来获取一个服务,这样可以均衡各个服务器上的服务。 ![](https://img-blog.csdnimg.cn/20190816202635152.png) ###Eureka集群 1先启动eureka注册中心 2启动服务提供者payment支付服务 3支付服务启动后会把自身信息化 服务以别名方式注册进eureka 4消费者order服务在要调用接囗时,使用服务别名去注册中心取实际的RPC远程调用地址 5消费者获得调用地址后,底层实际是利用HttpClient技术实现呈调用 6消费者获得服务地址后会存jvm内存中,默认每间隔30s更新一次服务调用地址 >Eureka Server在设计的时候就考虑了高可用设计,在Eureka服务治理设计中,所有节点既是服务的提供方,也是服务的消费方,服务注册中心也不例外。 Eureka Server的高可用实际上就是将自己做为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。 Eureka Server的同步遵循着一个非常简单的原则:只要有一条边将节点连接,就可以进行信息传播与同步。可以采用两两注册的方式实现集群中节点完全对等的效果,实现最高可用性集群,任何一台注册中心故障都不会影响服务的注册与发现。 问题:微服务RPC远程服务调用最核心的是什么: 高可用,试想你的注册中心只有一个。onlyone,它出故障了那就呵呵了,会导致整个为服务环境不可用,所以要搭建Eureka注册中心集群,实现负载均衡+故障容错 Eureka 集群的原理:相互注册,互相守望。每台Eureka服务器都有集群里其他Eureka服务器地址的信息 单机构建集群需要修改host文件区分端口 ###Eureka自我保护机制 某时刻某一个微服务不可用了,Eureka不会立即清理,依旧会对该微服务的信息进行保存。属于CAP里的AP分支 保护模式主要用于一组客户和Eureka Server之间存在网络分区场景下保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中固定信息,也就是不会注销任何微服务。 如果在Eureka Server的首页看到以下这段提示,则说明Eureka讲入了保护模式: > EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE 为什么会产生Eureka自我保护机制? 为了防止Eureka Client可以正常运行但是与Eureka Server网络不通情况下,Eureka Server不会立刻将Eureka Client服务剔除 什么是自我保护模式? 默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时、卡顿、拥挤)时, 微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了—因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过"自我保护模式"来解决这个 问题—当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。 自我保护机制:默认情况下Eureka CIient定时向Eureka Server端发送心跳包。 如畀Eureka在server端在一定时间内(默认90秒)没有收到Eureka Client发送心跳包,便会直接从服务注册列表中剔除该服务,但是在短时间(90秒中)内丢矢了大量的服务实例心跳,这时Eureka Server会开启目我保护机制,不令剔除该服务(该现象可能出现如果网络不通但是Eureka Client出现宕机,此时如果别的注册中心如果一定时间内没有收到心跳会将剔除该服务这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问韙而产生的 在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注册任何服务实例。 它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着 综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。 ###eureka配置项解读: 在注册服务之后,服务提供者会维护一个心跳用来持续高速Eureka Server,“我还在持续提供服务”,否则Eureka Server的剔除任务会将该服务实例从服务列表中排除出去。我们称之为服务续约。 面是服务续约的两个重要属性: (1)eureka.instance.lease-expiration-duration-in-seconds leaseExpirationDurationInSeconds,表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance。 默认为90秒 如果该值太大,则很可能将流量转发过去的时候,该instance已经不存活了。 如果该值设置太小了,则instance则很可能因为临时的网络抖动而被摘除掉。 该值至少应该大于leaseRenewalIntervalInSeconds (2)eureka.instance.lease-renewal-interval-in-seconds leaseRenewalIntervalInSeconds,表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance。除此之外,如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量。 默认30秒 > *eureka.client.registry-fetch-interval-seconds* :表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒 *eureka.server.enable-self-preservation* 是否开启自我保护模式,默认为true。 默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。 Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。 综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。 *eureka.server.eviction-interval-timer-in-ms* eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒 #zookeeper zookeeper是一个分布式协调工具,可以实现注册中心功能 关闭Linux服务器防火墙后动zookeeper服务器 zookeeper服务器取代Eureka服务器,zk作为服务注册中心 ##zookeeper依赖 ```xml org.springframework.cloud spring-cloud-starter-zookeeper-discovery ``` > >我们在zk上注册的node是临时节点,当我们的服务一定时间内没有发送心跳,那么zk就会将这个服务的znode删除了。 没有自我保护机制。重新建立连接后znode-id号也会变 >```shell script ># 通过zookeeper客户端zkCki.sh连接zookeeper ># 查看我们注册的所有服务 >ls /services ># 查看 cloud-provider-service节点 >ls /services/cloud-provider-service >``` **关于 zookeeper 的集群搭建,目前使用较少,而且在 yml 文件中的配置也是类似,以列表形式写入 zookeeper 的多个地址即可,** #Consul consul也是服务注册中心的一个实现,是由go语言写的。官网地址: https://www.consul.io/intro 中文地址: https://www.springcloud.cc/spring-cloud-consul.html Consul是一套开源的分布式服务发现和配置管理系统。 提供了微服务系统中的服务治理,配置中心,控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网络。 **提供的功能:** > 服务发现:提供HTTP和DNS两种发现方式 > 健康监测:支持多种方式,HTTP、TCP、Docker、Shell脚本定制化 KV存储:Key、Value的存储方式 多数据中心:Consul支持多数据中心 可视化Web界面 **CAP理论** > C:Consistency 一致性是指“all nodes see the same data at the same time”,即更新操作成功后,所有节点在同一时间的数据完全一致。 >A:Available 可用性是指“reads and writes always succeed”,即用户访问数据时,系统是否能在正常响应时间返回结果。 >P:Partition tolerance 分区容错性是指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。 >**CAP理论关注粒度是数据,而不是整体系统设计的** CAP 理论认为分布式系统只能兼顾其中的两个特性,即出现 CA、CP、AP 三种情况,如图所示。 CAP架构图 ![](https://img-blog.csdnimg.cn/img_convert/6151d7b348a7cf02a5107bc2821667a6.png) **CAP理论最多只能同时满足两个** CA without P 如果不要求 Partition Tolerance,即不允许分区,则强一致性和可用性是可以保证的。其实分区是始终存在的问题,因此 CA 的分布式系统更多的是允许分区后各子系统依然保持 CA。 CP without A 如果不要求可用性,相当于每个请求都需要在各服务器之间强一致,而分区容错性会导致同步时间无限延长,如此 CP 也是可以保证的。很多传统的数据库分布式事务都属于这种模式。 AP without C 如果要可用性高并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了实现高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。 **三个注册中心**: |组件名 |语言 |CAP |服务健康检查 |对外暴露接口 |SpringCloud集合 |Eureka |java |AP |可配支持 |HTTP |已集成 |Consul |Go |CP |支持 |HTTP/DNS |已集成 |Zookeeper |java |CP |支持 |客户端 |已集成 ![](https://fermhan.oss-cn-qingdao.aliyuncs.com/img/20201008142956.png) ![](https://fermhan.oss-cn-qingdao.aliyuncs.com/img/20201008143000.png) #服务调用 #Ribbon简介 SpringCloud Ribbon是基于NetfIixRibbon实现的一套客户端负载均衡的工具。简单的说,Ribbon是Neix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。 简单的说,就是在配置文件中列出LoadBalancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。 **LB负载均衡(LoadBalance)是什么?** 就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。 常见的负载均衡有软件Nginx,LVS,硬件F5等。 **Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别:** 1.Nginx是服务器负载均衡(集中式LB),客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。 2.Ribbon是本地负载均衡(进程内LB),在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。 **集中式LB** > 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方; **进程内LB** > 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器! Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。 ![](https://fermhan.oss-cn-qingdao.aliyuncs.com/img/20201008152940.png) ****Ribbon目前也进入维护,基本上不准备更新了**** Ribbon在工作时分成两步: 1.第一步先选择Eureka Server,它优先选择在同一个区域内负载较少的server 2.第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。 其中Ribbon提供了多种策略:比如轮询、随相和根据响应时间加权。 > Ribbon 就是 负载均衡 + RestTemplate 调用。实际上不止eureka的jar包有,zookeeper的jar包,还有consul的jar包都包含了他,就是上面使用的服务调用。 >三个注册中心都引入了Ribbon的jar我们不必自己引入 **RedisTemplate说明** > 有两种请求方式:post和get ,还有两种返回类型:object 和 Entity >getForObject():返回对象响应体中数据转化成的对象,基本上可以理解成json >Entity:返回对象是ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等 >返回的entity.getBody()即得到了Object **Ribbon负载均衡核心IRule:根据特定算法从服务列表中选取一个要访问的服务** > **Ribbon 负载均衡规则类型:** >com.netflix.loadbalancer.RoundRobinRule:默认的策略是轮询,及请求次数与注册中心注册的总的服务数量取模 >com.netflix.loadbalancer.RandomRule:随机 >com.netfIix.IoadbaIancer.RetryRuIe:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务 >WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择 >BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 >AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例 >ZoneAvoidanceRule:复合判断server所在区域的性能和server的可用性选择服务器 ![](https://img-blog.csdnimg.cn/img_convert/3fab156f64a839166f24865f6a425390.png) **如何替换Ribbon默认的负载均衡策略** 1.新建一个MyLoadBalanceRule配置类注意不要被@ComponentScan注解扫描到 官方文档明确给出了警告:这个自定义配置类不能放在@ComponentScan 所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。 ```java@Configuration public class MyLoadBalanceRule { /** * @desc 自定义负载均衡规则,默认是轮询规则 * @return */ @Bean public IRule myRule(){ //修改默认的Ribbon负载均衡策略,改为随机, // 要在主启动类上添加注解@RibbonClient(name = "cloud-payment-service",configuration = MyLoadBalanceRule.class) return new RandomRule(); } } ``` 2.在主启动类上加上注解@RibbonClient(name = "cloud-provider-consul-service",configuration = MyLoadBalanceRule.class) #OpenFeign ##什么是Feign Feign是SpringCloud组件中的一个轻量级RESTful的HTTP服务客户端。Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。 Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务 Feign旨在使编写JavaHttp客户端变得更容易 前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中, 由于对服务依赖的调用可能不止一处,往往一个接囗会被多处调用,所以通常都会针对每个微服务自行封装些客户端类来包装这些依赖服务的调用。 所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下, 我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可,即可完成对服务提供方 的接口绑定,简化了使用Springcloud Ribbon时,自动封装服务调用客户端的开发量。 Feign集成了Ribbon利用Ribbon维护了Payment的服务列表信息,并目通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口目以声明式的方法, 优雅而简单的实现了服务调用 ##OpenFeign OpenFeign是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的下的接囗, 并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。 ##OPenFeign超时 ```yaml #设置feign客户端超时时间(OpenFeign默认支持ribbon) ribbon: #指的是建立连接后从服务器读取到可用资源所用的时间(默认是1秒没有返回结果就会报超时异常) ReadTimeout: 5000 #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间 ConnectTimeout: 5000 ``` ##OpenFeign开启日志打印 Feign共了日志打印功能,我们可以诵过配置来调整日志级别,从而了解Feign中Tttp请求的细节。 说白了就是对Feign接口的调用情况进行监控和输出。 日志级别: NONE.默认的,不显示任何日志; BASIC,仅记录请求方法、URL、响应状态码及执行时间; HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息 FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据 yaml配置 ```yaml logging: level: # feign日志以什么级别监控哪个接口 com.springcloud.service.OpenFeignService: debug ``` 配置类 ```java @Configuration public class OpeFeignConfig { @Bean Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } } ``` #服务降级、服务熔断、服务限流 #Hystrix 断路器 [https://github.com/Netflix/Hystrix/wiki/How-To-Use](https://github.com/Netflix/Hystrix/wiki/How-To-Use) ##什么是服务雪崩 多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出"。 如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃, 谓的“雪崩效应”。 对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加, 备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。 ##什么是Hystrix Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下, 不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。 "断路器“本身是一种开关装置,当某个服务单元发生故障之后,涌过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack), 而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。 > Hystrix停止更新,进入维护阶段 ##服务降级 >服务器忙碌或者网络拥堵时,不让客户端等待并立刻返回一个友好提示,fallback。 对方系统不可用了,你需要给我一个兜底的方法,不要耗死。向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法 处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。 **可能导致服务降级的情况** > 程序运行异常, > 超时, > 服务熔 断触发服务降级, > 线程池/信号量打满也会导致服务降级, ## 服务熔断 >类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示,服务的降级->进熔断->恢复调用链路 熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该微服务的调用,快速返回 >错误的响应信息。 >当检查到该结点微服务调用响应正常后,恢复调用链路。 在SpringCloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的情况, 当失败的调用到一定阈值(缺省是5秒内20次调用失败),就会启动熔断机制。熔断机制的注解是@HystrixCommand 熔断打开:请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态 熔断半开:部分请求根据规则调用当前服务,如果请求成功目符合规则,则认为当前服务恢复正常,关闭熔断。 熔断关闭:熔断关闭不会对服务进行熔断,服务正常访问 >涉及到断路器的三个重要参数快照时间窗、请求总数阀值、错误百分比阀值 >1:快照时间窗:断路器确定是否打开需要统计一些请求和错误数据而统计的时间范围就是快照时间窗,默认为最近的10秒。 > @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000") > >2.请求总数阀值:在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次, >即使所有的请求都超时或具他原因失败,断路器都不会打开。 > @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10") > >3.错误百分比阀值:当请求总数在快照时间窗内超过了阀值,发生了10次调用,如果在10次调用中,有6次也就是60%发生了异常,将发生服务熔断,断路器被打开, >需要再等待一个滑动窗口期,断路器会呈现半开状态,如果此时有一个请求进入且成功这时断路器将闭合,否则继续打开 > @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60") > >当断路器打开后将不会再执行主逻辑代码而是直接执行回调方法 ![](https://img2020.cnblogs.com/blog/2016191/202011/2016191-20201107152158012-1824167882.jpg) ## 服务限流 >秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟几个,有序进行 ## 网关GateWay **简介** SpringCloud Gateway是SpringCloud的一个全新项目,基于Spring5.O+Springboot 2.0和ProjectReactor等技术开发的网关,它旨在为微服务架构提供一种 简单有效的统一的API路由管理方式。 SpringCloudGateway作为SpringCloud生态系统中的网关,目标是替代Zuul,在SpringCloud2.0以上版本中,没有对新版本的zuul2.0以上最新高性能版本进行集 成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。 而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而webFlux框架底层则使用了高性能的Reactor模式通信框架Netty。 springCloudGateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。 一方面因为Zuul 1.0已经进入了维护阶段,而且Gateway是SpringCloud团队研发的是亲儿子产品,值得信赖。而且很多功能比zull用起来都简单便捷。 Gateway是基于异步非阻塞模型上进行开发的,性能方面不需要担心。虽然Netflix早就发布了最新的Zuul2.x,但Spring Cloud貌似没有整合计划。而且Netflix相关组件都宣布进入维护期;不知前景如何? 多方面综合考虑Gateway是很理想的网关选择 ![](https://fermhan.oss-cn-qingdao.aliyuncs.com/img/20201013150250.png) SpringCloudGateway与Zuul的区别: 在SpringCloudFinchley正式版之前,SpringCloud推荐的网关是Netflix提供的Zuul: 1、Zuul 1.x是一个基于阻塞I/O的APIGateway 2、Zuul 1.x基于ServIet2.5使用阻塞架构,它不支持任何长连接(如WebSocket),Zuul的设计模式和Nginx较像,每次I/O操作都是从 工作线程中选择一个执行,请求线程阻塞到工作线程完成,但是差别是Nginx用C++实现,Zuul用Java实现,而JVM本身会有第一次加载较慢的情况,使得Zuul的性能相对较差。 3、Zuul 2.x理念更先进想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。Zuul2.x的性能较Zuul1.x有较大提升。在性能方面,根据官方提供的基准测试, SpringCloudGateway的RPS(每秒请求数)是Zuul的1.6倍。 4、SpringCloudGateway建立在SpringFramework5、ProjectReactor和SpringB00t2.之上,使用非阻塞API 5、SpringCloudGateway还支持WebSocket,并且与Spring紧密集成拥有更好的开发体验 Zuul1模型 springcloud中所集成的zuul版本,采用的是tomcat容器,使用的是传统的servlet IO处理模型。 Servlet的生命周期,servlet由servlet container进行生命周期管理 1.container启动时构造servlet对象并调用servlet init()进行初始化, 2.container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service() 3.container关闭时调用servlet destory()销毁servlet ![https://fermhan.oss-cn-qingdao.aliyuncs.com/img/20201013155448.png](https://fermhan.oss-cn-qingdao.aliyuncs.com/img/20201013155448.png) servlete—个简单的网络IO模型,当请求进入servlet container时,servlet container就会为其绑定一个线程在并发不高的场景下这种模型是适用的。但是一旦高并发 (比如抽风用jemeter压),线程数量就会上涨,而线程资源代价是昂贵的(上线文切换,内存消耗大)严重影响请求的处理时间。 在一些简单业务场景下,不希望为每个request分配一个线程,只需要1个或几个线程就能应对极大并发的请求,这种业务场景下servlet模型没有优势 所以Zuul 1.x是基于servlet之上的一个阻塞式处理模型,即spring实现了处理所有request请求的一个servlet(DispatcherServlet)并由该servlet阻塞式处理处理。 所以springcloudzuul无法摆脱servlet模型的弊端。 传统的Web框架比如说:struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的。 但是,在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使 用java8)SpringWebFlux是Spring5.0引入的新的响应式框架区别于SpringMVC,它不需要依赖ServletAPI,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。 **GateWay特性:** > 基于SpringFramework5,ProjectReactor和SpringBoot 2.0进行构建; > > 动态路由:能够匹任何请求属性; > > 可以对路由指定Predicate(断言)和Filter(过滤器)· > > 集成Hystrix的断路器功能; > > 集成SpringCloud服务发现功能; > > 易于编写的Predicate(断言)和Filter(过滤器)· > > 请求限流功能; > > 支持路径重写。 **GateWay三大核心概念** >Route(路由):路由是构建网关的基本模块,它由ID、目标URI、一系列的断言和过滤器组成,如果断言为true则匹配该路由 > >Predicate(断言):参考的是Java8的java.util.function.predicate。开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由 > >Filter(过滤):指的是spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。 **web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。 predicate就是我们的匹配条件; 而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了** ![](https://gitee.com/songjilong/FigureBed/raw/master/img/20200425115208.png) ![](https://img-blog.csdnimg.cn/img_convert/b925eec3db224cb6eee4dea48732e65d.png) **GateWay工作流程** 1.客户端向Spring Cloud Gateway发出请求。然后在Gateway HandlerMapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler 2.Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 3.过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(post)执行业务逻辑。 4.Filter在pre类型的过滤器可以做参数校验,权限校验,流量监听,日志输出,协议转换等, 5.在post类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用 **路由转发+执行过滤器链** **Gateway路由配置有两种方式** 1.yaml的方式 ```yaml spring: application: name: cloud-gateway # cloud整合gateway的配置 cloud: gateway: # 配置路由信息,是个集合List可以配置多个路由 routes: # 路由id,要唯一,建议配合服务名使用 - id: payment_gateway_route1 # 匹配后提供服务的路由地址 #uri+predicates # 要访问这个路径得先经过9527处理 uri: http://127.0.0.1:8001 # predicates断言也是个集合,List predicates,路径相匹配的进行路由 predicates: - Path=/payment/get/** # 同上边一样 - id: payment_gateway_route2 uri: http://127.0.0.1:8001 predicates: - Path=/payment/create ``` 2.配置类的方式 ```java @Configuration public class GateWayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){ RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); routes.route("path_route_custom", r -> { return r.path("/guonei").uri("http://news.baidu.com"); }); return routes.build(); } } ``` **可以通过微服务名称实现动态路由** ## Gateway:Predicate断言 **说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理** > 1. After Route Predicate > 2. Before Route Predicate > 3. Between Route Predicate > 4. Cookie Route Predicate > 5. Header Route Predicate > 6. Host Route Predicate > 7. Method Route Predicate > 8. Path Route Predicate > 9. Query Route Predicate 详细的断言配置 [https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#configuring-route-predicate-factories-and-gateway-filter-factories](https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#configuring-route-predicate-factories-and-gateway-filter-factories) ## Gateway:Filter过滤器 **路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用, Spring Cloud Gateway内置多种路由过滤器,他们都由GatewayFilter的工厂类来产生** 按声明生命周期分类: pre:这种过滤器在请求被路由之前调用。 post:这种过滤器在路由到微服务以后执行。 按种类分: GatewayFilter:单一的过滤器[](https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories) GlobalFilter:全局过滤器[](https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#global-filters) ##自定义全局的过滤器主要实现两个接口GlobalFilter,Ordered 用来做日志记录,统一网关鉴权 ## Config 配置中心 **分布式系统面临的问题** >微服务意味着要将单应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。 >由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。 **Config能干嘛** >SpringCloudConfig为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中 心化的外部配置。可以做到 > >- 集中管理配置文件 > > >- 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release > > >- 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息 > > >- 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置 > > >- 将配置信息以REST接囗的形式暴露 > ![](https://fermhan.oss-cn-qingdao.aliyuncs.com/img/20201013235510.png) **Config怎么玩** >SpringCloudConfig分为服务端和客户端两部分。 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心取和加载配置信息配置服务器默认采用git来存储配置信息, 这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容 ## 消息总线 配置中心当修改配置文件后,服务端能够自动刷新,而客户端需要添加`@RefreshScope`注解 pom添加依赖 ```xml org.springframework.boot spring-boot-starter-actuator ``` yaml配置 ```yaml # 暴露监控端点,客户端可以动态刷新配置中心的配置 management: endpoints: web: exposure: include: "*" ``` 最后想客户端发送一个post请求后,客户端才能发现配置中心的改变, curl -X POST “http://localhost:3345/actuator/refresh” 这种手动刷新的方式虽然不要求必须重启服务,但在服务过多以后,如果每个服务都要手动发送一个post请求就显得有些繁琐 **Spring Cloud Bus配合Spring Cloud Config使用可以实现配置的动态刷新,Bus支持两种消息代理:RabbitMQ和Kafka** ![](http://img.mcxlblog.cn/qiniu_picGospring-cloud-bus.jpg) ### 什么是总线: 在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个公用的消息主题,并让所有的微服务实例都连接上来,由于该主题中消息会被所有实例监听和消费,所以称它为消息总线。在总线上的实例上都可以方便的广播一些需要让其他连接在该主题上的实例都知道的消息。 ### 基本原理 COnfigClienet实例都监听MQ中的同一个Topic(默认是SpringCloudBus),当一个服务刷新数据的时候,它会把这个消息放入到topic中,这样其它同一个topic的服务就能得到通知,然后去更新自身的配置。 给config服务端和客户端都添加依赖 ```xml org.springframework.cloud spring-cloud-starter-bus-amqp ``` ### 利用BUS刷新的两种方式 1. 利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置 ![](http://img.mcxlblog.cn/qiniu_picGospring-config-bus-server.jpg) > 缺点: > > 1. 打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新职责 > 2. 破坏了微服务各节点的对等性 > 3. 有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的修改 2. 利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置(更加推荐) ![](http://img.mcxlblog.cn/qiniu_picGospring-cloud-bus-client.jpg) > **注意:** > > 当引入BUS后需要向config服务器发一个POST请求 > > curl -X POST "http://localhost:3344/actuator/bus-refresh" > > 如果想要顶点刷新通知,即指定某些服务刷新则POST请求如下 > curl -X POST "http://localhost:3344/actuator/bus-refresh/微服务名称:端口号" # SpringCloud Alibaba - Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 - Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 - RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。 - Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。 - Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。 - Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。 - Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。 - Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。 - Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。 ## 1、Nacos [中文官方地址:https://nacos.io/zh-cn/](https://nacos.io/zh-cn/) ### 1.1、Nacos是什么 Nacos:Dynamic Naming and Configuration Service,前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service。Nacos 致力于帮助您发现、配置和管理微服务,其默认集成了Ribbon支持负载均衡。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 简单理解:Nacos = Eureka+Config+Bus #### Nacos做配置中心 ### 1.2、Nacos和其它注册中心的对比 ![](http://img.mcxlblog.cn/qiniu_picGonacos.jpg) > Nacos支持CP模式和AP模式并且可以切换 ```yaml server: port: 80 spring: application: name: nacos-consumer cloud: nacos: discovery: server-addr: localhost:8848 management: endpoints: web: exposure: include: '* ``` ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ``` #### Nacos做注册中心 ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ``` ```yaml server: port: 4433 spring: application: name: nacos-config-server cloud: nacos: config: server-addr: localhost:8848 #配置中心地址 file-extension: yaml #指定yaml格式的配置 prefix: nacos-config-server # 默认是微服务的名称 group: DEFAULT_GROUP # 分组 默认是 DEFAULT_GROUP namespace: public # 默认是名称空间默认是public profiles: active: dev # 以上配置对应配置中心 namespace为public,group为public,dateID 为nacos-config-server-dev.yaml的配置 ``` dateID:`${prefix}-${spring.profiles.active}.${file-extension}` - `prefix` 默认为 `spring.application.name` 的值,也可以通过配置项 `spring.cloud.nacos.config.prefix`来配置。 - `spring.profiles.active` 即为当前环境对应的 profile,详情可以参考 [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)。 **注意:当 `spring.profiles.active` 为空时,对应的连接符 `-` 也将不存在,dataId 的拼接格式变成 `${prefix}.${file-extension}`** - `file-exetension` 为配置内容的数据格式,可以通过配置项 `spring.cloud.nacos.config.file-extension` 来配置。目前只支持 `properties` 和 `yaml` 类型。 通过@RefreshScope注解实现自动刷新 #### Nacos的持久化 Nacos默认自带的是嵌入式数据库derby,可以更改为mysql数据库做持久化 更改config目录下的application.properties文件 ```properties # 为Nacos配置数据库为Mysql ### If use MySQL as datasource: spring.datasource.platform=mysql ### Count of DB: db.num=1 ### Connect URL of DB: db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?useUnicode=true&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user=root db.password=@Root123 ``` 如果有报错需要将mysql驱动复制到plugins/mysql目录下(新建) ### 1.3、Nacos集群 需要一个nginx作为虚拟ip提供负载均衡nginx.conf配置如下: ```nginx upstream cluster{ server 200.8.9.16:8848; server 200.8.9.17:8848; server 200.8.9.18:8848; } server { listen 80; server_name localhost; location / { proxy_pass http://cluster; } } ``` 集群需要三个nacos节点,每个nacos节点的集群配置文件如下: ``` # ip:port ,nacos节点的IP地址和端口号 200.8.9.16:8848 200.8.9.17:8848 200.8.9.18:8848 ``` linux默认是集群模式启动,windows需要修改启动文件 ```cmd if %MODE% == "standalone" ( set "JAVA_OPT=%JAVA_OPT% -Xms512m -Xmx512m -Xmn256m" set "JAVA_OPT=%JAVA_OPT% -Dnacos.standalone=true" if %EMBEDDED_STORAGE% == "embedded" ( set "JAVA_OPT=%JAVA_OPT% -DembeddedStorage=true" ) set "JAVA_OPT=%JAVA_OPT% -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m" set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%BASE_DIR%\logs\java_heapdump.hprof" set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages" ) else ( set "JAVA_OPT=%JAVA_OPT% -Xms512m -Xmx512m -Xmn256m" set "JAVA_OPT=%JAVA_OPT% -Dnacos.standalone=true" ) ``` ## 2、Sentinel **如遇到规则不生效可能是jar包冲突了,把Nacos的fastJsonjar包排除掉** 中文地址[https://github.com/alibaba/Sentinel/wiki/](https://github.com/alibaba/Sentinel/wiki/) **Sentinel: 分布式系统的流量防卫兵**:随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。(Hsytrix加强版) - 流量控制 - 熔断降级 - 系统自适应保护 - 热点参数限流 Sentinel有两部分组成 - 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。 - 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。 1. **流控规则** ![image-20201119215512066](http://img.mcxlblog.cn/qiniu_picGoimage-20201119215512066.png) 说明: - **资源名**:唯一名称,默认请求路径 - **针对来源**:Sentinel可以针对调用者进行限流,填写微服务名,默认是default,不针对任何来源 - **阈值类型/单机阈值**: - QPS(每秒钟请求的数量):当调用该API的QPS到达阈值的时候进行限流 - 线程数:当调用该API的线程数达到阈值的时候进行限流 - **是否集群** - **流控模式**: - 直接:资源达到限流条件时,直接限流 - 关联:当关联的资源达到阈值时,就限流自己 - 链路:只记录指定链路上的流量(指定资源从入口进来的流量,如果达到阈值,就进行限流,API级别的针对来源) - **流控效果** - 快速失败:直接失败抛出异常 - Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长才达到设置的QPS阈值 - 排队等待:匀速排队:让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效 2. **降级规则** ![image-20201120194942949](http://img.mcxlblog.cn/qiniu_picGoimage-20201120194942949.png) - RT平均响应时间(秒级) 如果1s内持续进入5个请求,且对应的平均响应时间都超过了阈值,那么在接下来的时间窗口期内对这个方法的调用都会自动熔断,RT默认最大4900,更大需要通过-Dcsp.sentinel.statistic.max.rt设置生效 异常比列 - 异常比列(秒级) ​ QPS>=5且异常比列超过阈值,触发降级;时间窗口结束后,关闭降级。 - 异常数(分钟级) ​ 最近一分钟异常数(分钟统计)超过阈值,处罚降级熔断;时间窗口结束后,关闭降级,注意,当timeWindow小于60秒结束熔断后仍有可能再次进入熔断状态。 3. **热点规则** ![image-20201120201438962](http://img.mcxlblog.cn/qiniu_picGoimage-20201120201438962.png) ## @SentinelResource注解 > 注意:注解方式埋点不支持private方法。 `@SentinelResource`用于定义资源,并提供可选的异常处理和fallback配置项。`@SentinelResource`注解包含以下属性: - `value`:资源名称,必需项(不能为空) - `entryType`:entry类型,可选项(默认为`EntryType.OUT`) - `blockHandler`/ `blockHandlerClass`:`blockHandler`对应处理`BlockException`的函数名称,可选项.blockHandler函数访问范围需要是`public`,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为`BlockException`.blockHandler函数默认需要和希望使用其他类的函数,则可以指定`blockHandlerClass`为对应的类的`Class`对象,注意对应的函数必需为static函数,否则无法解析。 - `fallback`/ `fallbackClass`:fallback函数名称,可选项,用于在引发异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了`exceptionsToIgnore`里面排除掉的异常类型)进行处理。fallback函数签名和位置要求: - 返回值类型必须与原函数返回值类型一致; - 方法参数列表需要和原函数一致,或者可以额外多一个`Throwable`类型的参数用于接收对应的异常。 - fallback函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定`fallbackClass`为对应的类的`Class`对象,注意对应的函数必需为static函数,否则无法解析。 - `defaultFallback`(自1.6.0版开始):替代的fallback函数名称,可选项,通常用于通用的fallback逻辑(即可以使用很多服务或方法)。替代fallback函数可以针对所有类型的异常(`exceptionsToIgnore`里面排除掉的异常类型)进行处理。若同时配置了fallback和defaultFallback,则只有fallback会实行。defaultFallback函数签名要求: - 返回值类型必须与原函数返回值类型一致; - 方法参数列表需要为空,或者可以额外多一个`Throwable`类型的参数用于接收对应的异常。 - 如果希望使用其他类的函数,则可以指定`fallbackClass`为对应的类的`Class`对象,注意对应的函数必需为静态函数,否则无法解析。 - `exceptionsToIgnore`(自1.6.0版开始):用于指定的异常被排除掉,不会计入异常统计中,也不会进入fallback逻辑中,而是会原样抛出。 > 注:1.6.0之前的版本fallback函数只针对降级异常(`DegradeException`)进行处理,**不能针对业务异常进行处理**。 特别地,若blockHandler和fallback都进行了配置,则被限流降级而抛出`BlockException`时只会进入`blockHandler`处理逻辑。若未配置`blockHandler`,`fallback`和`defaultFallback`,则则被限流降级时可能直接`BlockException` **抛出**(若方法本身未定义抛出BlockException导致被JVM包装一层`UndeclaredThrowableException`)。 **Sentinel整合Ribbon+OpenFeign+fallback** ## Seata **[官网地址http://seata.io/zh-cn/](http://seata.io/zh-cn/)** ### 1、是什么 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 **AT(主要)**、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题,Seata提供了一站式的分布式解决方案。 **TA模式是一个优化版的两阶段提交事务模型** #### TC (Transaction Coordinator) - 事务协调者 seata服务器就是TC 维护全局和分支事务的状态,驱动全局事务提交或回滚。 #### TM (Transaction Manager) - 事务管理器 ``` @GlobalTransactional#谁脑袋上有这个谁就是TM ``` 定义全局事务的范围:开始全局事务、提交或回滚全局事务。 #### RM (Resource Manager) - 资源管理器 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。 #### Transaction ID XID 全局唯一的事务ID **** **AT模式的工作流程** ![](http://img.mcxlblog.cn/qiniu_picGoStataAT.jpg) 1.TM向TC注册全局事务,并生成全局唯一的XID,XID会在微服务调用链路的上下文中传播 2.RM向TC注册分支事务,并将其纳入XID对应的全局事务管辖 3.RM向TC汇报资源准备的状态 4.TM向TC发起针对XID的全局提交或者回滚决议(一阶段结束) 5.TC根据汇总后所有事务参与者的执行状态决定回滚还是提交 6.TC通知所有XID下管辖的全部分支事务完成提交或者回滚(二阶段结束) ### 2、使用Seata [下载seata服务https://github.com/seata/seata/releases](https://github.com/seata/seata/releases) 1. 建表语句 ```sql -- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data #分支事务表 CREATE TABLE IF NOT EXISTS `global_table` ( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_transaction_id` (`transaction_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store BranchSession data #全局事务表 CREATE TABLE IF NOT EXISTS `branch_table` ( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store lock data #全局锁 CREATE TABLE IF NOT EXISTS `lock_table` ( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(96), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log ,这是混gun日志表 CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ``` 在conf模块下,修改file.conf配置文件,主要修改自定义事务组名称+事务日志存储模式为db+数据库连接信息 2. 修改seata file.conf文件 ```conf ## transaction log store, only used in seata-server #这里手动加入service模块 service { #transaction service group mapping #修改,可不改,my_test_tx_group随便起名字。 vgroup_mapping.my_test_tx_group = "default" #only support when registry.type=file, please don't set multiple addresses # 此服务的地址 default.grouplist = "127.0.0.1:8091" #disable seata disableGlobalTransaction = false } store { ## store mode: file、db、redis #这里修改为db mode = "db" ## file store property file { ## store location dir dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions maxBranchSessionSize = 16384 # globe session size , if exceeded throws exceptions maxGlobalSessionSize = 512 # file buffer size , if exceeded allocate new buffer fileWriteBufferCacheSize = 16384 # when recover batch read size sessionReloadReadSize = 100 # async, sync flushDiskMode = async } ## database store property db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" #如果你是mysql8.0以上,则修改这里的驱动,不是则不修改 driverClassName = "com.mysql.cj.jdbc.Driver" url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai" user = "root" password = "root" minConn = 5 maxConn = 30 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } ## redis store property redis { host = "127.0.0.1" port = "6379" password = "" database = "1" minConn = 1 maxConn = 10 queryLimit = 100 } } ``` 修改register.conf ```conf registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa #修改这里为nacos type = "nacos" nacos { application = "seata-server" serverAddr = "127.0.0.1:8848" group = "SEATA_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" } eureka { serviceUrl = "http://localhost:8761/eureka" application = "default" weight = "1" } redis { serverAddr = "localhost:6379" db = 0 password = "" cluster = "default" timeout = 0 } zk { cluster = "default" serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" } consul { cluster = "default" serverAddr = "127.0.0.1:8500" } etcd3 { cluster = "default" serverAddr = "http://localhost:2379" } sofa { serverAddr = "127.0.0.1:9603" application = "default" region = "DEFAULT_ZONE" datacenter = "DefaultDataCenter" cluster = "default" group = "SEATA_GROUP" addressWaitTime = "3000" } file { name = "file.conf" } } config { # file、nacos 、apollo、zk、consul、etcd3 #修改这里为nacos type = "nacos" nacos { serverAddr = "127.0.0.1:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" } consul { serverAddr = "127.0.0.1:8500" } apollo { appId = "seata-server" apolloMeta = "http://192.168.1.204:8801" namespace = "application" } zk { serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" } etcd3 { serverAddr = "http://localhost:2379" } file { name = "file.conf" } } ```