5.Open-Feign实现远程掉用
5.1.Feign简介
- Ribbon存在的问题:不规范、风格不统一、维护性比较差。
- Fegin:SpringCloud提供的伪HTTP客户端(本质还是Http),封装了Http调用流程,更适合面向接口化让java接口注释的方式调用Http请求。
- Nacos支持Fegin可以直接集成实现负载均衡的效果。
5.2.集成Feign实现远端调用
- Fegin让方法调用更加解耦
(1)common项目加入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
(2)主类配置注解@EnableFeignClients
@EnableFeignClients
(3)编写Fegin的service接口
@EnableFeignClient(name="video-service") public interface VideoFeginService{ @GetMapping("/api/v1/video/find_by_id") JsonData findById(@RequestParam("id") int id) }
(4)Post方式调用
@PostMapping("/api/v1/video/save") JsonData save(@RequestBody Video video);
5.3.Ribbon和Feign的选择
Feign默认集成了ribbon 写起来更加思路清晰和方便 采用注解方式进行配置,配置熔断等方式方便
6.分布式架构CAP定理
6.1.CAP定理简介
- CAP定理:指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可同时获得
- 一致性(C):所有节点都可以访问到最新的数据
- 可用性(A):每个请求都是可以得到响应的,不管请求是成功还是失败
- 分区容错性(P):除了全部整体网络故障,其他故障都不能导致整个系统不可用
- CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡
CA: 如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的 CP: 如果不要求A(可用),每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统 AP:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性
6.2.CAP下的注册中心的选择
常见的注册中心:zk、eureka、nacos
Naocs | Eureka | Consul | Zookeeper | |
一致性协议 | CP+CA | AP | CP | CP |
健康检查 | TCP/HTTP/MYSQL/ClientBeat | 心跳 | TCP/HTTP/gRPC/Cmd | Keep Alive |
雪崩保护 | 有 | 有 | 无 | 无 |
访问协议 | HTTP/DNS | HTTP | HTTP/DNS | TCP |
SpringCloud集成 | 支持 | 支持 | 支持 | 支持 |
- Zookeeper:CP设计,保证了一致性,集群搭建的收,某个节点失败,则会进行选举leader,或者半数以上的节点无法用,则无法提供服务,一次可用性无法满足。
- Eureka:AP原则,无主从节点,一个节点挂了,自动切换其他节点可以使用,去中心化。
结论:
- 分布式系统中P,肯定要满足,所以只能在CA中二选一
- 没有最好的选择,最好的选择是根据业务场景来进行架构设计
- 如果要求一致性,则选择zookeeper/Nacos,如金融行业 CP
- 如果要求可用性,则Eureka/Nacos,如电商系统 AP
- CP : 适合支付、交易类,要求数据强一致性,宁可业务不可用,也不能出现脏数据
- AP: 互联网业务,比如信息流架构,不要求数据强一致,更想要服务可用
6.3.BASE理论
什么是base理论
CAP中的一致性和可用性进行一个权衡的结果,核心思想就是:我们无法做强一致,但每个应用都可以根据自身的业务特点,采取响应的方式来是系统达到最终的一直性,来自ebay的架构师提出。
- Basically Available(基本可用)
- 假设系统出现了不可预知的故障,但是还能用,可能会有性能或者功能上的影响。
- Soft state(软状态)
- 允许系统中的数据存在中间状态,并且认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时
- Eventually consistent(最终一致性)
- 系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值
7.流量防卫兵Sentinel
7.1.高并发下微服务存在问题
微服务拆分多个系统,服务之间相互依赖,可能会由于系统负载过高,突发流量或者网络等各种异常情况导致服务不可用。
7.2.面向失败编程
- 限流:
- 漏斗,不管流量有多大,均匀的流入容器,令牌桶算法,漏桶算法。
- 熔断:
- 保险丝,熔断服务,为了防止整个系统故障,包含当前和下游服务:下单服务->商品服务->用户服务->(出现异常)->熔断风险服务。
- 降级:
- 抛弃一些非核心的接口和数据,返回兜底数据。
- 隔离:
- 服务和资源相互隔离,比如网络资源,机器资源,线程资源等,不会因为某个服务的资源不足而抢占其他服务的资源。
- 熔断和降级的相互交集
- 相同点:
- 从可用性和可靠性出发,为例防止系统崩溃,最终都是让用户感知到某些服务是不可用的。
- 不同点:
- 服务熔断一般是下游服务故障导致的,而服务降级一般是从整体系统负荷考虑,由调用方控制。
7.3.Sentinel简介
(1)什么是Sentinel
- 阿里巴巴开源的分布式系统流控工具。
- 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
- 丰富的应用场景:消息削峰填谷、集群流量控制、实时熔断下游不可用等。
- 晚辈的实时监控:Sentinel同时提供监控功能。
- 提供开箱即用与其他开源框架整合,SpringCloud、Dubbo等。
- (2)核心概念
- 资源:是Sentinel中的核心概念之一,可以是java程序中的任何内容,可以是服务或者方法,就是我们想要保护的东西。
- 规则:定义怎样的方式保护资源,主要包括流控规则、熔断降级等。
7.4.微服务项目Sentinel搭建
(1)Sentinel分为两个部分
- 核心库(java客户端):不依赖任何框架/库,能够运行于所有java运行时环境,同时对Dubbo、SpringCloud等框架也有较好的支持。
- 控制台(Dashboard):基于SpringBoot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器
(2)微服务引入Sentinel依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
(3)微服务yml中引入Sentinel
sentinel: transport: dashboard: 127.0.0.1:8080 port: 9999 #dashboard: ip:8080 控制台访问地址 #port: 9999 本地启的端口,随机算哪个不能被占用的,会在应⽤对应的机器上启动⼀个 Http Server,该 Server 会与 Sentinel 控制台做交互, 若被占⽤,则开始+1⼀次扫描
(4)启动sentinel的jar包,控制台访问,默认的账号密码都是sentinel
java -jar dashboard-1.8.0.jar
注意:微服务注册上去后,由于Sentinel是懒加载模式,所以需要访问微服务后才会在控制台出现
7.5.Sentinel流量控制功能
(1)流量控制(flow control)
原理是监控应用流量的QPS或者并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
(2)两种规则
- 基于统计并发线程数的流量控制
并发数控制用于保护业务线程池不被满调用耗尽 Sentinel并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目) 如果超出阈值,新的请求会被立即拒绝,效果类似于信号隔离。
- 基于统计QPS的流量控制
当QPS超过某个阈值的时候,则采取措施进行流量控制
7.6.Sentinel流控规则效果
(1)直接拒绝
- 默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立刻拒绝掉。
(2)Warm up
- 冷启动/预热,如果系统在从之前长期处于空闲的状态,我们希望处理请求的数量是缓步增多,达到预期的时间以后,达到系统处理请求的最大值
(3)匀速排队
- 严格控制请求通过的时间,让每个请求匀速的通过,对应的是漏桶算法,主要用于处理突发性的流量,如消息队列,一下涌进大量消息,我们不希望返回失败,而是空闲时间慢慢处理。
- 注意:
- 匀速排队等待策略是Leaky Bucket算法结合虚拟队列等待机制实现。
- 匀速排队等待策略不止吃QPS>1000的场景
7.7.Sentinel熔断降级规则
(1)熔断降级
- 对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一
- 对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩
- 熔断降级作为保护自身的手段,通常在客户端进行配置
(2)Sentinel熔断降级策略
- 慢比例调用(响应时间):选择以慢比例调用比例作为阈值,需要设置允许的慢调用RT(及最大的响应时间),请求的响应时间大于该值则统计为慢调用。
- 最大RT:设置访问的最大时间限制
- 比例阈值:【0.1-1.0】之间,指所有请求中最大RT请求数的比例
- 熔断时长:超过时间后尝试恢复
- 最小请求数:熔断触发的最小请求数
- 异常比例:当单位统计市场内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断
- 比例阈值:【0.0-1.0】之间,指所有请求中发生异常请求的比例
- 熔断时长:查过比例阈值系统停止提供服务的时长
- 最小请求数:熔断触发的最小请求数
- 异常数:单位统计时长内的异常数目超过阈值之后会自动进行熔断
- 异常数:发生异常的数量
- 熔断时长:超过熔断时间后自动恢复
- 最小请求数:熔断触发的最小请求数
7.8.Sentinel自定义异常
(1)Sentinel⾃定义异常简介
- 限流和熔断返回的数据有问题,微服务交互基本都是json格式,所以需要⾃定义异常信息
(2)AlibabCloud版本升级,不兼容问题
- v2.1.0到v2.2.0后,Sentinel⾥⾯依赖进⾏了改动,且不向下兼容
- 自定义降级返回数据
/*旧版实现UrlBlockHandler并且重写blocked方法*/ @Component public class MyUrlBlockHandler implements UrlBlockHandler { @Override public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException { //降级业务处理 } }
/*旧版实现BlockExceptionHandler并且重写blocked方法*/ @Component public class MyBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception { //降级业务处理 } }
(3)Sentinel自定义异常类
- 异常种类
FlowException //限流异常 DegradeException //降级异常 ParamFlowException //参数限流异常 SystemBlockException //系统负载异常 AuthorityException //授权异常
- 代码编写
@Component public class XdBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { Map<String,Object> backMap = new HashMap<>(); if(e instanceof FlowException){ backMap.put("code",-1); backMap.put("msg","限流异常"); }else if (e instanceof DegradeException){ backMap.put("code",-2); backMap.put("msg","熔断异常"); }else if (e instanceof ParamFlowException){ backMap.put("code",-3); backMap.put("msg","热点异常"); }else if (e instanceof SystemBlockException){ backMap.put("code",-4); backMap.put("msg","系统规则异常"); }else if (e instanceof AuthorityException){ backMap.put("code",-5); backMap.put("msg","认证异常"); } //设置返回json数据 response.setStatus(200); response.setHeader("content-Type","application/json;charset=UTF-8"); response.getWriter().write(JSON.toJSONString(backMap)); } }
(4)案例测试
- 限流异常
- 降级异常
7.9.Sentinel整合Open-Feign
(1)加入依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
(2)开启Feign对Sentinel的支持
feign: sentinel: enabled: true
(3)创建容错类,实现对应的服务接口,记得加注解
@Service public class VideoServiceFallback implements VideoService { @Override public JsonData findVideo(int id) { Video video = new Video(); video.setTitle("熔断降级数据"); return null; } @Override public JsonData save(Video video) { return null; } }
(4)配置feign容错类
@FeignClient(value = "xdclass-video-service", fallback = VideoServiceFallback.class)