提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 1.微服务理论
- 1.1 微服务的特点
- 1.2 微服务与微服务架构
- 1.3 微服务的优点
- 2.分布式集群概念
- 2.1 分布式集群概念
- 2.1.1 什么是分布式
- 2.1.2 什么是集群
- 2.1.3 分布式集群的关系
- 2.2 架构演进过程
- 2.2.1 单体架构
- 2.2.2 垂直架构
- 2.2.3 SOA架构
- 2.3.4 分布式架构
- 2.3 RPC是什么
- 2.3.1 RPC 框架原理
- 2.3.2 服务之间的交互可以用两种方式
- 2.4 分布式思想相关概念
- 2.4.1 高并发
- 2.4.2 高可用
- 2.4.3 注册中心
- 2.4.4 负载均衡
- 2.4.5 服务雪崩
- 2.4.6 服务熔断
- 2.5.7 服务限流
- 2.5.8 API网关
- 2.5.9 服务跟踪
- 2.5.10 弹性云
- 3.Spring Cloud概述及环境准备
- 3.1 Spring Cloud概述
- 3.1 环境准备
- 3.1.1 数据库环境准备
- 3.1.2 项目准备
- 4.Eureka服务注册与发现
- 4.1 Eureka概述
- 4.1.1 Eureka简介
- 4.1.2 什么是服务注册
- 4.1.3 Eureka的组件
- 4.2 Eureka Server服务代码实现
- 4.2.1 在springcloudparent项目中构建cloud-eureka-server7001模块,并导入依赖。
- 4.2.2 编写yml配置文件,配置Eureka配置信息
- 4.2.3 编写启动类EurekaServerMain7001.java
- 4.2.4 启动服务测试
- 4.3 将服务的提供者cloud-provider-payment8001注册到服务中心中
- 4.3.1 导入依赖
- 4.3.2 编写yml配置文件,配置Eureka配置信息
- 4.3.3 在启动类上添加`@EnableEurekaClient`注解
- 4.3.4 启动服务进行测试
- 4.4 将服务的消费者cloud-consumer-order8000注册到服务中心中
- 4.4.1 导入依赖
- 4.4.2 编写yml配置文件,配置Eureka配置信息
- 4.4.3 在启动类上添加`@EnableEurekaClient`注解
- 4.3.4 启动服务进行测试
- 4.5 总服务测试
- 5.Ribbon负载均衡服务调用
- 5.1 概述
- 5.1.1 简介
- 5.1.2 可以做什么
- 5.2 Ribbon负载均衡演示
- 5.2.1 架构示例图
- 5.2.2 默认负载均衡策略代码演示
- 5.3 负载均衡的策略
- 5.3.1 IRule:根据特定算法从服务列表中选取一个要访问的服务
- 5.3.1 将上述案例的默认策略改为RandomRule随机策略-编码方式
- 5.3.2 配置轮询策略-配置yml方式
- 5.4 负载均衡算法-轮询策略
- 5.5 小结
- 6.OpenFeign 声明式服务调用
- 6.1 概述
- 6.1.1 什么是Feign
- 6.1.2 Feign作用
- 6.2 OpenFeign开发步骤
- 6.2.1 新建项目cloud-consumer-feign-order8000
- 6.2.2配置YML文件
- 6.2.3 编写启动类
- 6.2.4 编写微服务调用PaymentFeignService接口并新增注解@FeignClient
- 6.2.5 编写OrderController,通过微服务调用接口访问远程服务
- 6.2.6 启动服务测试
- 6.2.7 小结
- 6.3 OpenFeign的超时设置
- 6.3.1 超时演示
- 6.3.2 什么是超时
- 6.3.3 yml中配置OpenFeign的超时控制
- 6.4 OpenFeign的日志打印
- 6.4.1 概述
- 6.4.2 配置日志bean
- 6.2.3 在yml配置文件中需要配置开启日志的Feign客户端
- 6.2.4 重启服务器访问测试
- 7.Hystrix断路器
- 7.1 雪崩的概述
- 7.2 Hystrix简介
- 7.2.1 服务的降级(Fallback)
- 7.2.2 服务熔断Breaker
- 7.2.3 服务限流
- 7.3 高并发测试工具-Jmeter
- 7.4 Hystrix案例
- 7.4.1 构建一个新的服务提供者模块
- 7.4.2 构建一个新的服务消费者模块
- 7.5 服务的降级
- 7.5.1 服务提供端降级处理:cloud-provider-hystrix-payment8001
- 7.5.2 服务的消费端设置降级:cloud-consumer-feign-hystrix-order8000
- 7.5.2 定义统一的降级方法
- 7.5.2 实现服务降级业务解耦
- 7.6 服务的熔断
- 7.6.1 概述
- 7.6.2 代码实现-cloud-provider-hystrix-payment8001项目
- 7.6.3 服务熔断原理
- 7.6 服务的限流
- 7.7 服务监控hystrixDashboard
- 7.7.1 服务监控的概述
- 7.7.2 仪表盘9001
- 7.7.3 断路器的演示
- 8.Gateway新一代网关
- 8.1 概述简介
- 8.1.1 Gateway概述
- 8.1.2 网关能干啥
- 8.1.3 微服务架构中网关在哪里
- 8.2 网关的核心概念
- 8.3 Gateway工作流程
- 8.4 入门案例
- 8.4.1 创建新的模块cloud-gateway-gateway9527
- 8.4.2 配置yml文件
- 8.4.3 添加启动类
- 8.4.4 在yml中配置路由映射
- 8.4.5 启动程序测试
- 8.5 通过微服务名实现动态路由
- 8.5.1 配置yml文件
- 8.5.2 启动程序测试
- 8.6 Predicate的使用
- 8.6.1 是什么
- 8.6.2 常用的Route Predicate
- 8.7 Filter的使用
- 8.7.1 概述
- 8.7.2 局部过滤器
- 8.7.3 全局过滤器
- 8.7.4 GatewayFilter(31种之多)
- 8.7.5 GlobalFilter
- 8.7.6 常用的GatewayFilter
- 8.8 自定义过滤器
- 8.8.1 自定义一个类LoginFilter实现GlobalFilter,Ordered接口
- 8.8.2 重启网关测试
- 9.SpringCloud Sleuth分布式链路请求跟踪
- 9.1 概述
- 9.2 搭建链路监控步骤
- 9.2.1 zipkin
- 9.2.2 服务提供者cloud-provider-payment8001
- 9.2.3 服务消费者(调用方)cloud-consumer-order8000
- 9.2.4 重启项目测试
- 10 SpringCloud Alibaba入门简介
- 10.1 为啥会有SpringCloud Alibaba
- 10.2 SpringCloud Alibaba带来了什么?
- 10.2.1 概述
- 10.2.2 作用
- 10.2.3 依赖
- 10.2.4 如何用
- 11.SpringCloud Alibaba Nacos服务注册和配置中心
- 11.1 Nacos简介
- 11.2 各种注册中心比较
- 11.3 安装并运行Nacos
- 11.3.1 下载Nacos
- 11.3.2 安装并运行Nacos
- 11.3.4 运行后在浏览器访问
- 11.3.5 主页面如下
- 11.4 Nacos作为服务注册中心演示
- 11.4.1 官方网站
- 11.4.2 基于Nacos的服务提供者
- 11.4.3 基于Nacos的服务消费者
- 11.4.4 服务注册中心对比
- 11.5 Nacos作为服务配置中心演示
- 11.5.1 Nacos作为配置中心-基础配置
- 11.5.2. Nacos作为配置中心-分类配置
- 11.6 Nacos集群和持久化配置(重要)
- 11.6.1 官网说明
- 11.6.2 Nacos持久化配置解释
- 11.4.3. Linux版Nacos+MySQL生产环境配置
- 12 SpringCloud Alibaba Sentinel实现熔断与限流
- 12.1 Sentiel介绍
- 12.2 安装Sentinel控制台
- 12.2.1 Sentinel两个核心组件
- 12.2.2 安装步骤
- 12.3 初始化演示工程
- 12.3.1 启动Nacos8848成功
- 12.3.2 案例
- 12.4 流控规则
- 12.4.1 基本概述
- 12.4.2 流量模式
- 12.4.3 流控效果
- 12.5 熔断规则
- 12.5.1 基本概述
- 12.5.2 熔断策略实战
- 12.6 热点规则
- 12.6.1 基本概述
- 12.6.2 承上启下复习
- 12.6.3 代码实现
- 12.6.4 参数例外项
- 12.6.5 其他
- 12.7 系统规则
- 12.7.1 基本概述
- 12.7.2 系统规则
- 12.7.3 配置全局QPS
- 12.8 @SentinelResource
- 12.8.1 基本概述
- 12.8.2 流控规则+自定义处理
- 12.8.3. 上面兜底方法面临的问题
- 12.8.4. 集中自定义处理
- 12.8.5. 更多注解属性说明
- 12.9 熔断框架比较
- 12.10 规则持久化
- 12.10.1 基本概述
- 12.10.2 代码实现
前言
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
提示:以下是本篇文章正文内容,下面案例可供参考
1.微服务理论
微服务(Microservice Architecture)是最近几年流行的一种架构思想。简单来说,微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
1.1 微服务的特点
应用(服务)组件化
每一个应用都是一个单独的服务,所有的应用都可以被单独的替代或升级,单独进行部署。
服务无状态化
这个是可以实现微服务快速扩展的基本条件。
标准而轻量级的通信协议
Restful,把所有的应用通过相同的协议集成在一个平台当中。
去中心化
包含数据去中心化,技术栈去中心化。
1)技术去中心化:既可以使用java,也可以使用php,go。只要使用支持标准通信协议就可以接入微服务平台。
2)数据去中心化:每个服务可以使用自己独立的数据源,可以使用mysql,redis等等。
故障处理设计和实时监控
微服务发展到后面会越来越复杂,服务变多,环境复杂,所以当出现问题需要有相应的系统容错机制和完善的实时的日志监控。
1.2 微服务与微服务架构
微服务:强调的是服务的大小,他关注的是某一个点。
微服务架构:他是一种风格,一种思想,需要从整体上对软件系统进行通盘的考虑。
1.3 微服务的优点
- 简单灵活,可以独立部署,对其他服务不会影响。
- 松耦合,高内聚,易扩展。服务于服务之间是松耦合的。
- 专注,专业,可靠的小团队。微服务一个很大的特点就是可以和敏捷开发有个好的整合,因为微服务每个模块都可以很小,正好适合小团队。
- 语言,工具无关系。只要任何语言合适就可以使用。
- 能够实现复杂的项目的持续集成和持续交付。不像大型项目,发布一次可能几十分钟,牵涉的东西也很多,而微服务项目比较小,启动也很快
2.分布式集群概念
2.1 分布式集群概念
2.1.1 什么是分布式
分布式系统一定是由多个节点组成的系统。其中,节点指的是计算机服务器,而且这些节点一般不是孤立的,而是互通的。这些连通的节点上部署了我们的节点,并且相互的操作会有协同。分布式系统对于用户而言,他们面对的就是一个服务器,提供用户需要的服务而已,而实际上这些服务是通过背后的众多服务器组成的一个分布式系统,因此分布式系统看起来像是一个超级计算机一样。简单来说:很多"人”一起,干不样的事。这些不一样的事, 合起来是一件大事。
2.1.2 什么是集群
集群是指在几个服务器上部署相同的应用程序来分担客户端的请求。
它是同一个系统部署在不同的服务器上,比如一个登陆系统部署在不同的服务器上。好比 多个人一起做同样的事。集群主要的使用场景是为了分担请求的压力。但是,当压力进一步增大的时候,可能在需要存储的部分,比如mysql无法面对大量的“写压力”。因为在mysql做成集群之后,主要的写压力还是在master的机器上,其他slave机器无法分担写压力,这时,就引出了“分布式”。简单来说:很多“人”一起,干一样的事。
2.1.3 分布式集群的关系
上面说了:分布式是一群人做不同的事情,所有的事务合起来是一个整体。集群是一群人做相同的事情。所有分布式系统中每一个节点服务都可以搭建集群,但是集群不一定是分布式架构。
2.2 架构演进过程
2.2.1 单体架构
一个war包或jar包的项目中,包含了应用所有功能的应用程序,我们通常称之为单体应用。单体架构中,全部功能集中在一个项目内(All in one),这是一种比较传统的架构风格。
优势:
架构简单,前期开发成本低、开发周期短,适合小型项目(OA、CRM、ERP 企业内部应用)。
缺点
① 复杂性高
② 技术债务逐渐上升
③ 部署速度逐渐变慢
④ 扩展能力受限,无法按需伸缩
⑤ 阻碍技术创新
2.2.2 垂直架构
垂直架构是指将单体架构中的多个模块拆分为多个独立的项目。形成多个独立的单体架构。垂直MVC项目主要有表现层,业务逻辑层,数据访问层组成的MVC架构,整个项目打包放在一个tomcat里面。适合于 访问量小,用户数不多的业务。
优点
技术栈可扩展(不同的系统可以用不同的编程语言编写)。
缺点
① 垂直架构中每一个系统还是单体架构。不利于开发、扩展、维护,项目的部署效率很低,代码全量编译和部署一次发布需要很长时间,更重要的是 如果某个功能出错有问题,所有的功能都需要再重新打包编译,部署效率极低。
② 相互无法访问,导致多个项目中可能要编写重复的功能代码,加载重复的数据。
③ 团队协作难度高,如多人使用 git 很可能在同一个功能上,多人同时进行了修改,作为一个大而全的项目,可能个人只是需要开发其中一个小的模块的需求,却需要导入整个项目全量的代码。
2.2.3 SOA架构
SOA 全称为 Service-Oriented Architecture,即面向服务的架构。它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署、组合和使用。一个服务通常以独立的形式存在于操作系统进程中。站在功能的角度,把业务逻辑抽象成可复用的服务,通过服务的编排实现业务的快速再生,目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用。
优点
① 可重用性高
② 维护成本低
③ 可扩展性强
缺点
① 抽取服务的粒度大
② 技术难度较高,不容易解决bug
2.3.4 分布式架构
其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
优点
① 服务拆分粒度更细,遵循单一职责原则,有利于提高开发效率。
② 可采用Http协议进行服务间的调用
③ 可以针对不同服务制定对应的优化方案,适用于互联网时代,产品迭代周期更短。
缺点
① 对开发团队技术要求高
2.3 RPC是什么
RPC(Remote Procedure Call)— 远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。
2.3.1 RPC 框架原理
RPC 架构主要包括三部分:
- 服务注册中心(Registry),负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。
- 服务提供者(Server),提供服务接口定义与服务实现类。
- 服务消费者(Client),通过远程代理对象调用远程服务。
服务提供者启动后主动向服务注册中心(Registry)注册机器IP、端口以及提供的服务列表;
服务消费者启动时向服务注册中心(Registry)获取服务提供方地址列表。服务注册中心(Registry)可实现负载均衡和故障切换。
RPC 调用过程
(1) 客户端(client)以本地调用方式调用服务;
(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);
(3) 客户端通过 sockets 将消息发送到服务端;
(4) 服务端存根(server stub)收到消息后进行解码(将消息对象反序列化);
(5) 服务端存根(server stub)根据解码结果调用本地的服务;
(6) 本地服务执行并将结果返回给服务端存根(server stub);
(7) 服务端存根(server stub)将返回结果打包成消息(将结果消息对象序列化);
(8) 服务端(server)通过 sockets 将消息发送到客户端;
(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);
(10) 客户端(client)得到最终结果。
RPC 就是要把 2、3、4、7、8、9 这些步骤都封装起来。
2.3.2 服务之间的交互可以用两种方式
1.RPC调用: Netty(Socket)+自定义序列化
2.RestAPI: 严格来说,SpringCloud是使用Rest方式进行服务之间交互的,不属于RPC。HTTP+JSON
2.4 分布式思想相关概念
2.4.1 高并发
1.通过设计保证系统可以并行处理很多请求。应对大量流量与请求
- Tomcat最多支持并发多少用户?
- Tomcat 默认配置的最大请求数是 150,也就是说同时支持 150 个并发,当然了,也可以将其改大。
- 当某个应用拥有 250 个以上并发的时候,应考虑应用服务器的集群。
- 具体能承载多少并发,需要看硬件的配置,CPU 越多性能越高,分配给 JVM 的内存越多性能也就越高,但也会加重 GC 的负担。
- 操作系统对于进程中的线程数有一定的限制:
- Windows 每个进程中的线程数不允许超过 2000
- Linux 每个进程中的线程数不允许超过 1000
- 另外,在 Java 中每开启一个线程需要耗用 1MB 的 JVM 内存空间用于作为线程栈之用。
- Tomcat 默认的 HTTP 实现是采用阻塞式的 Socket 通信,每个请求都需要创建一个线程处理。这种模式下的并发量受到线程数的限制,但对于 Tomcat 来说几乎没有 BUG 存在了。
- Tomcat 还可以配置 NIO 方式的 Socket 通信,在性能上高于阻塞式的,每个请求也不需要创建一个线程进行处理,并发能力比前者高。但没有阻塞式的成熟。
- 这个并发能力还与应用的逻辑密切相关,如果逻辑很复杂需要大量的计算,那并发能力势必会下降。如果每个请求都含有很多的数据库操作,那么对于数据库的性能也是非常高的。
- 对于单台数据库服务器来说,允许客户端的连接数量是有限制的。
- 并发能力问题涉及整个系统架构和业务逻辑。
- 系统环境不同,Tomcat版本不同、JDK版本不同、以及修改的设定参数不同。并发量的差异还是满大的。
- maxThreads=“1000” 最大并发数 ,默认值为200
- minSpareThreads=“100”//初始化时创建的线程数,默认值为10
- acceptCount=“700”// 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,默认值为100
2.高并发衡量指标
- 响应时间(RT)
- 请求做出响应的时间,即一个http请求返回所用的时间
- 吞吐量
- 系统在单位时间内处理请求的数量
- QPS(Query/Request Per Second)、 TPS(Transaction Per Second)
- 每秒查询(请求)数、每秒事务数
- 专业的测试工具:Load Runner
- Apache ab
- Apache JMeter
- 并发用户数
- 承载的正常使用系统功能的用户的数量
2.4.2 高可用
- 高可用: 通常是指通过合理的设计下,减少服务不能提供的时间。是分布式中一个很重要的考虑因素。
- 如何保证高可用:
- 服务的集群部署
- 数据库主从+双机热备
- 主-备方式(Active-Standby方式)
- 主-备方式即指的是一台服务器处于某种业务的激活状态(即Active状态),另一台服务器处于该业务的备用状态(即Standby状态)。
- 双主机方式(Active-Active方式)
- 双主机方式即指两种不同业务分别在两台服务器上互为主备状态(即Active-Standby和Standby-Active状态)
2.4.3 注册中心
- 保存服务地址等信息的地方,方便其它调用者实时的获取服务信息。
- 服务注册:服务的提供者,将服务的地址信息注册到注册中心。
- 服务发现:服务的消费者,从注册中心发现并保存服务提供者的地址信息。
2.4.4 负载均衡
- 是高并发、高可用系统必不可少的关键组件,目标是 尽力将网络流量平均分发到多个服务器上,以提高系统整体的响应速度和可用性。简单来说,就是根据某种策略将将请求发送给空闲的服务器。
- 策略
- 轮询(Round Robin)
- 加权轮询(Weighted Round Robin)
- 随机Random
- 哈希Hash
- 最小连接数LC
- 最短响应时间LRT
2.4.5 服务雪崩
雪崩效应 : 在微服务之间进行服务调用是由于某一个服务故障,导致级联服务故障的现象,称为雪崩效应。雪崩效应描述的是提供方不可用,导致消费方不可用并将不可用逐渐放大的过程。
2.4.6 服务熔断
服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。
2.5.7 服务限流
服务限流就是为了保护后端服务和数据不被大流量击垮而设计,当发生流量浪涌时,让实例能在最大有效负荷运行,而不会超过负载导致崩溃。限流就是限制某个服务每秒的调用本服务的频率。
2.5.8 API网关
API网关要做很多工作,它作为一个系统的后端总入口,承载着所有服务的组合路由转换等工作,除此之外,我们一般也会把安全,限流,缓存,日志,监控,重试,熔断等放到 API 网关来做。
2.5.9 服务跟踪
当部署的服务越来越多的时候,需要跟踪一个请求从一个服务到下一个微服务的传播过程。追踪服务的调用链,记录整个系统执行请求过程。如:请求响应时间,判断链中的哪些服务属于慢服务(可能存在问题,需要改善)。
2.5.10 弹性云
所谓的弹性,指的是云端的可用资源能够随着用户的需求而灵活变化、自由升降,在业务高峰或低谷期,能匹配适量的资源。
弹性云服务器,简单地说,是指运行在云计算环境中的虚拟服务器,支持用户自由调度计算资源,不受传统硬件设备的限制。
Elastic Compute Service(ECS)弹性计算服务
动态扩容,压榨服务器闲时能力
例如:双11,618,高峰时多配置些服务器,平时减少多余的服务器配置(用于其他服务应用),避免资源浪费
3.Spring Cloud概述及环境准备
3.1 Spring Cloud概述
- Spring Cloud 是一系列框架的有序集合。
- Spring Cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来。
- 通过 Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
- 它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、 断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
- Spring Cloud项目官方网址:https://spring.io/projects/spring-cloud
- Spring Cloud 版本命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本:Angel,第二个Release版本:Brixton,然后是Camden、Dalston、Edgware,Finchley,Greenwich,Hoxton。
- Spring Cloud 与 Dubbo 对比
- spring cloud微服务架构
- spring cloud组件图
3.1 环境准备
3.1.1 数据库环境准备
CREATE DATABASE IF NOT EXISTS cloud2022 DEFAULT CHARACTER SET utf8 ;
USE cloud2022 ;
DROP TABLE IF EXISTS payment ;
CREATE TABLE payment (
id BIGINT (20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
SERIAL VARCHAR (300) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE = INNODB AUTO_INCREMENT = 33 DEFAULT CHARSET = utf8 ;
INSERT INTO payment (id, SERIAL) VALUES(31, '商品01'),(32, '商品02') ;
3.1.2 项目准备
初始化项目已准备好直接导入即可
4.Eureka服务注册与发现
4.1 Eureka概述
4.1.1 Eureka简介
- Eureka 是 Netflix 公司开源的一个服务注册与发现的组件 。
- Eureka 和其他 Netflix 公司的服务组件(例如负载均衡、熔断器、网关等) 一起,被 Spring Cloud 社区整合为Spring-Cloud-Netflix 模块。
- Eureka 包含两个组件:Eureka Server (注册中心) 和 Eureka Client (服务提供者、服务消费者)。
4.1.2 什么是服务注册
- Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。
- 而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员可以通过Eureka Server来监控系统中各个微服务是否正常运行。
- 在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息,比如:服务通讯地址等以别名方式注册到注册中心上。
- 另一方(消费者服务),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后,再实现本地RPC远程调用。
- RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。
- 在任何RPC远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))。
4.1.3 Eureka的组件
1.Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
2.Eureka Client通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会在Eureka Server发送心跳(默认周期30秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移出(默认90秒)
4.2 Eureka Server服务代码实现
4.2.1 在springcloudparent项目中构建cloud-eureka-server7001模块,并导入依赖。
导入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
4.2.2 编写yml配置文件,配置Eureka配置信息
server:
port: 7001
spring:
application:
name: cloud-eureka-server
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetchRegistry: false
service-url:
defaultZone: http://localhost:7001/eureka
4.2.3 编写启动类EurekaServerMain7001.java
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServerMain7001.class,args);
}
}
4.2.4 启动服务测试
访问:http://localhost:7001/,看到如下结果证明访问成功!!!
4.3 将服务的提供者cloud-provider-payment8001注册到服务中心中
4.3.1 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
4.3.2 编写yml配置文件,配置Eureka配置信息
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
4.3.3 在启动类上添加@EnableEurekaClient
注解
@SpringBootApplication
@MapperScan("com.tlc.mapper")
@EnableEurekaClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
4.3.4 启动服务进行测试
在浏览其中访问:http://localhost:7001/,看到如下结果证明配置成功!!!
4.4 将服务的消费者cloud-consumer-order8000注册到服务中心中
4.4.1 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
4.4.2 编写yml配置文件,配置Eureka配置信息
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
4.4.3 在启动类上添加@EnableEurekaClient
注解
@SpringBootApplication
@EnableEurekaClient
public class OrderMain8000 {
public static void main(String[] args) {
SpringApplication.run(OrderMain8000.class,args);
}
}
4.3.4 启动服务进行测试
在浏览其中访问:http://localhost:7001/,看到如下结果证明配置成功!!!
4.5 总服务测试
- 先要启动EurekaServer,7001服务
- 再要启动服务提供者8001服务和服务消费者80服务
- eureka服务器
- 测试查询:http://localhost/consumer/payment/get/31
5.Ribbon负载均衡服务调用
5.1 概述
5.1.1 简介
- Ribbon是 Netflix 提供的一个基于HTTP和TCP的客户端负载均衡工具。
- Ribbon主要有两个功能:
- 简化远程调用
- 负载均衡
- 服务端负载均衡
- 负载均衡算法在服务端
- 由负载均衡器维护服务地址列表
- 客户端负载均衡
- 负载均衡算法在客户端
- 客户端维护服务地址列表
5.1.2 可以做什么
1. LB(负载均衡)
- 简单的说就是将用户的请求平均分配到多个服务器上,从而达到系统的HA(高可用)。
- 常见的负载均衡有软件Nginx,LVS,硬件F5等。
- Ribbon的本地负载均衡客户端 VS Nginx服务端负载均衡区别:
- Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后,由nginx实现转发请求。即负载均衡是由服务器端完成的。
- Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用。
- 集中式LB
- 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如Nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
- 进程内LB
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
- Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
2. 总结
Ribbon=负载均衡+RestTemplate调用
5.2 Ribbon负载均衡演示
5.2.1 架构示例图
Ribbon在工作时分成两步:
第一步,先选择EurekaServer,它优先选择在同一个区域内负载较少的server。
第二步,再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略。比如:轮询、随机和根据响应时间加权。
总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
5.2.2 默认负载均衡策略代码演示
1.创建多个cloud-provider-payment8001服务
①启动PaymentMain8001启动类
②在idea中拷贝2个PaymentMain800服务,PaymentMain8002、PaymentMain8003
③启动PaymentMain8002、PaymentMain8003服务
④重新编写cloud-provider-payment8001服务中的PaymentController类
@RestController
public class PaymentController {
@Autowired
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/get/{id}")
public CommonResult<Payment> getById(@PathVariable Long id){
Payment payment = paymentService.getById(id);
return new CommonResult<Payment>(200,"查询成功----端口"+serverPort,payment);
}
}
③重新启动PaymentMain8001、PaymentMain8002、PaymentMain8003服务
2.在cloud-consumer-order8000服务中添加Ribbon负载均衡
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
注意:这个不需要手动引用,Eureka客户端自带Ribbon
3.改写cloud-consumer-order8000服务中OrderController请求方式,通过Eureka服务拿到地址
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/get/{id}")
public CommonResult<Payment> getById(@PathVariable long id){
return restTemplate.getForObject("http://CLOUD-PAYMENT-SERVICE:8001/payment/get/" + id , CommonResult.class);
}
}
4.在config.ApplicationContextConfig配置类中的getRestTemple方法上添加 @LoadBalanced注解
@SpringBootConfiguration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //启用RestTemplate负载均衡的能力,才能通过服务名远程调用
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5.在启动类上添加@RibbonClient注解
@SpringBootApplication
@EnableEurekaClient
public class OrderMain8000 {
public static void main(String[] args) {
SpringApplication.run(OrderMain8000.class,args);
}
}
6.直接启动cloud-consumer-order8000服务测试默认的负载均衡策略
在ribbon中,默认的负载均衡策略是轮询
访问:http://localhost:8000/order/get/31
第一次查询结果:
第二次查询的结果
第三次的结果
以后查询的都依次顺序循环。轮询的顺序与你启动服务的顺序有关。
5.3 负载均衡的策略
5.3.1 IRule:根据特定算法从服务列表中选取一个要访问的服务
配置负载均衡策略的核心组件IRule
结构体系
策略介绍:
- com.netflix.loadbalancer.RoundRobinRule 轮询,默认策略。
- com.netflix.loadbalancer.RandomRule 随机
- com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
- WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
- BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
5.3.1 将上述案例的默认策略改为RandomRule随机策略-编码方式
1.在启动类的上一级目录中创建配置规则包rule,在该包下创建策略
@Component
public class MyRule {
/**
* @Description: 存储一个随机的负载均衡的对象,到Spring容器中
* 注意:不能被SpringBoot启动类扫描到,
* 一旦被扫描到,该对象会应用到全局(对所有服务的调用都使用随机的访问方式)
* @Author: TCL
* @Date: 2022/11/25
* @Time: 20:32
*/
@Bean
public IRule randomRule(){
return new RandomRule();
}
}
2.在配启动类上配置@RibbonClient,让其扫描到该类将IRule添加到IOC容器中
@SpringBootApplication
@RibbonClient(value = "CLOUD-PAYMENT-SERVICE", configuration = MyRule.class)
public class OrderMain8000 {
public static void main(String[] args) {
SpringApplication.run(OrderMain8000.class,args);
}
}
3.重启服务测试
访问:http://localhost:8000/order/get/31
每次访问结果都是随机的。
5.3.2 配置轮询策略-配置yml方式
在yml中配置方式如下:
user-service: # 生产者服务名称
ribbon:
NFloadBalancerRuleClassName: XxxRule # 负载均衡策略
5.4 负载均衡算法-轮询策略
5.5 小结
- ribbon是做负载均衡的,与nginx类似,两者的区别如下:
- Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后,由nginx实现转发请求。即负载均衡是由服务器端完成的。
- Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用
- 部署在客户端上部署ribbon的步骤
- ①导入依赖(如果导入了Eureka依赖,会默认传递一个ribbon依赖)
- ②编写配置类,在配置类中加载一个RestTemple到Spring容器中。在加载方法上添加
@Bean、@LoadLoadBalanced
注解 - ③配置轮询策略:在启动类的上一级目录中创建规则包rule,在该包下创建规则类,并将规则添加到Spring容器中。
- ④在启动类中添加@RibbonClient注解,加载规则配置类。
- ⑤在controller中通过restTemplate.getForObject发起请求,访问服务的提供者。请求格式
http://应用服务名称:端口号/资源访问路径
6.OpenFeign 声明式服务调用
6.1 概述
6.1.1 什么是Feign
- Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可
- SpringCloud对Feign进行了封装,使其支持了SpringMVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
6.1.2 Feign作用
1.能做什么
- Feign旨在使用编写Java Http客户端变得更容易。
- 前面在使用Ribbon+RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。
- 但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务端额调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。
- 在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是DAO接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
2.Feign集成了 Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
3.Feign和OpenFeign两者区别
4.实现图解
实现思想:微服务调用接口+@FeignClient
6.2 OpenFeign开发步骤
6.2.1 新建项目cloud-consumer-feign-order8000
新项目中的内容与cloud-consumer-order8000一样,并且openFeign也是自带ribbon
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tlc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
6.2.2配置YML文件
server:
port: 8000
spring:
application:
name: cloud-consumer-feign-order8000
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
6.2.3 编写启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderFeignMain8000 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain8000.class,args);
}
}
6.2.4 编写微服务调用PaymentFeignService接口并新增注解@FeignClient
在service包下创建接口类
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //声明远程调用的服务名称
public interface PaymentFeignService {
/**
* @Description: 远程调用方法
* @Author: TCL
* @Date: 2022/11/25
* @Time: 21:31
*/
@GetMapping("/payment/get/{id}")
public CommonResult<Payment> getById(@PathVariable("id") Long id);
}
6.2.5 编写OrderController,通过微服务调用接口访问远程服务
@RestController
public class OrderController {
// 调用远程微服务接口
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping("/order/get/{id}")
public CommonResult<Payment> getById(@PathVariable long id){
return paymentFeignService.getById(id);
}
}
6.2.6 启动服务测试
访问:http://localhost:8000/order/get/31
发现测试成功。
6.2.7 小结
6.3 OpenFeign的超时设置
6.3.1 超时演示
1.在服务提供方cloud-provider-payment8001故意写暂停程序
/**
* @Description: 暂停程序
* @Author: TCL
* @Date: 2022/11/25
* @Time: 21:55
*/
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout(){
try { TimeUnit.SECONDS.sleep(3); }catch (Exception e) {e.printStackTrace();} //单位秒
return "端口:"+serverPort;
}
2.在消费端cloud-consumer-feign-order8000的服务调用接口PaymentFeignService中编写远程访问方法
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();
3.在消费端cloud-consumer-feign-order8000的控制方法中调用超时方法
@GetMapping("/consumer/payment/feign/timeout")
public String paymentFeignTimeout() {
return paymentFeignService.paymentFeignTimeout();
}
4.启动服务测试
http://localhost:8000/order/payment/feign/timeout
错误页面,OpenFeign默认等待一秒钟,超过后报错
6.3.2 什么是超时
- 默认Feign客户端只等待一秒钟,但是,服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接报错。
- 为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制,也即Ribbon的超时时间,因为Feign集成了Ribbon进行负载均衡。
6.3.3 yml中配置OpenFeign的超时控制
#设置Feign客户端超时时间(openfeign默认支持ribbon)
ribbon:
ConnectTimeout: 3000 #连接等待的最大时间,默认1000毫秒
ReadTimeout: 3000 #请求处理等待的最大时间,默认1000毫秒
MaxAutoRetries: 1 #同一台实例最大重试次数,不包括首次调用
MaxAutoRetriesNextServer: 1 #切换服务的最大次数,不包括第一个服务
OkToRetryOnAllOperations: false #是否所有请求都重试,false-仅get请求重试
#hystrix的超时时间
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 9000
一般情况下 都是 ribbon 的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制)
因为ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制,源码如下
要开启Feign的重试机制如下:(Feign默认重试五次 源码中有)
@Bean
Retryer feignRetryer() {
return new Retryer.Default();
}
- 根据上面的参数计算重试的次数:
(MaxAutoRetries+1)*(MaxAutoRetriesNextServer+1)
一共产生4次调用。 - 如果在重试期间,时间超过了hystrix的超时时间,便会立即执行熔断,fallback。所以要根据上面配置的参数计算hystrix的超时时间,使得在重试期间不能达到hystrix的超时时间,不然重试机制就会没有意义。
- hystrix超时时间的计算:
(1 + MaxAutoRetries+MaxAutoRetriesNextServer) * ReadTimeout
即按照以上的配置 hystrix的超时时间应该配置为(1+1+1)*3=9秒
- 当ribbon超时后且hystrix没有超时,便会采取重试机制。
当OkToRetryOnAllOperations设置为false时,只会对get请求进行重试。如果设置为true,便会对所有的请求进行重试
,如果是put或post等写操作,如果服务器接口没做幂等性,会产生不好的结果,所以OkToRetryOnAllOperations慎用。如果不配置ribbon的重试次数,默认会重试一次
注意:
默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试 非GET方式请求,只有连接异常时,才会进行重试
配置超时后,再次测试,发现发送请求后并不是立即报错,而是等待设置的超时时间过后报错。
6.4 OpenFeign的日志打印
6.4.1 概述
-
日志打印:
- Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。说白了就是对Feign接口的调用情况进行监控和输出。
-
日志级别
- NONE:默认的,不显示任何日志
- BASIC:仅记录请求方法、RUL、响应状态码及执行时间
- HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
FULL
:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
6.4.2 配置日志bean
在配置config报下创建FeignConfig,将日志级别加载到spring容器中
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
6.2.3 在yml配置文件中需要配置开启日志的Feign客户端
#开启OpenFeign日志打印功能
logging:
level:
com.tlc.service.PaymentFeignService: debug
6.2.4 重启服务器访问测试
访问:http://localhost:8000/order/get/31
7.Hystrix断路器
7.1 雪崩的概述
复杂分布式体系结构中的应用程序有数十个依赖关系,每一个依赖关系在某些时候将不可避免的失败。
服务雪崩
多个微服务之间调用的时候,假如微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的"扇出"。
如果扇出的链路上某个微服务的调用响应的时间过长或者不可用,对微服A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的"雪崩效应"。
对于高流量的应用来说,单一的后端依赖可能会导致所有的服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。
也可以这样说:当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)
7.2 Hystrix简介
- Hystrix是一个用于处理分布式系统的
延迟
和容错
的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常
等。 - Hystrix能够保证在一个依赖出问题的情况下,
不会导致整体服务失败,避免级联故障
,以提高分布式系统的弹性。 断路器
本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(Fallback),而不是长时间的等待或者抛出调用方无法处理的异常
,这样就保证了服务调用方的线程不会被长时间、不必要地占用
,从而避免了故障在分布式系统中的蔓延,乃至雪崩。- Hystrix作用:
- 服务降级
- 服务熔断
- 服务限流
- 服务隔离
- 接近实时的监控
7.2.1 服务的降级(Fallback)
- 服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示
- 哪些情况会触发降级
- 程序运行异常
- 超时自动降级
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
- 人工降级
7.2.2 服务熔断Breaker
- 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
- 就是保险丝
- 服务的降级->进而熔断->恢复调用链路
7.2.3 服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行Flowlimit
7.3 高并发测试工具-Jmeter
下载地址:https://archive.apache.org/dist/jmeter/binaries/
添加一个并发请求的方式:
7.4 Hystrix案例
7.4.1 构建一个新的服务提供者模块
1.构建模块cloud-provider-hystrix-payment8001,并导入相关依赖
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.tlc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.配置yml文件
server:
port: 8001
spring:
application:
name: cloud-hystrix-payment-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
3.编写启动类
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
4.编写service接口以及实现类serviceImpl
public interface PaymentService {
/**
* @Description: 执行成功
* @Author: TCL
* @Date: 2022/11/25
* @Time: 23:10
*/
public String paymentInfo_OK(Integer id);
/**
* @Description: 超时方法
* @Author: TCL
* @Date: 2022/11/25
* @Time: 23:10
*/
public String payment_Timeout(Integer id);
}
@Service
public class PaymentServiceImpl implements PaymentService {
//成功
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"哈哈哈" ;
}
//失败
public String payment_Timeout(Integer id){
int timeNumber = 3;
try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
return "线程池:"+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
}
}
5.编写controller类
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_OK(id);
log.info("*******result:"+result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_Timeout(@PathVariable("id") Integer id){
String result = paymentService.payment_Timeout(id);
log.info("*******result:"+result);
return result;
}
}
6.启动服务测试
- 启动eureka7001
- 启动cloud-provider-hystrix-payment8001
- 访问
- 访问: http://localhost:8001/payment/hystrix/ok/31
- 每次调用耗费3秒钟: http://localhost:8001/payment/hystrix/timeout/31
- 上述module均OK
以上述为根基平台,从正确->错误->降级熔断->恢复
7.通过Jmeter进行压力测试
-
压测的过程中再来访问一下微服务
http://localhost:8001/payment/hystrix/ok/31
http://localhost:8001/payment/hystrix/timeout/31 -
演示结果
- 两个都在自己转圈圈
- 为什么会被卡死
- tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
-
压力测试结论
- 上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死
7.4.2 构建一个新的服务消费者模块
1.搭建cloud-consumer-feign-hystrix-order8000模块,并导入依赖
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.tlc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.编写yml配置文件
server:
port: 8001
spring:
application:
name: cloud-consumer-hystrix-order
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
4.编写启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderHystrixMain8000 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain8000.class,args);
}
}
5.启动服务测试
访问:http://localhost:8000/consumer/payment/hystrix/ok/32
6.高并发测试
- 2W个线程压8001
- http://localhost:8000/consumer/payment/hystrix/timeout/32
- 消费者80,呜呜呜
- 要么转圈圈等待
- 要么消费端报超时错误
报错:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sat Nov 26 08:15:59 CST 2022
There was an unexpected error (type=Internal Server Error, status=500).
Read timed out executing GET http://CLOUD-HYSTRIX-PAYMENT-SERVICE/payment/hystrix/timeout/32
feign.RetryableException: Read timed out executing GET http://CLOUD-HYSTRIX-PAYMENT-SERVICE/payment/hystrix/timeout/32
at feign.FeignException.errorExecuting(FeignException.java:249)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:129)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100)
at com.sun.proxy.$Proxy107.paymentInfo_Timeout(Unknown Source)
at com.tlc.controller.OrderHystrixController.paymentInfo_Timeout(OrderHystrixController.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
7.5 服务的降级
服务的降级可以在消费端配置,也可以在服务提供端配置。首先看在服务端配置值服务降级,这里会使用@HystrixCommand
配置降级。该注解作用到方法上。表示该方法因为某种原因,该方法不能正常返回数据,降级使用备用的方法,返回数据。
7.5.1 服务提供端降级处理:cloud-provider-hystrix-payment8001
在服务提供端使用服务降级的场景:
- 超时
- 程序出现异常
①在service层中,设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback。
@Service
public class PaymentServiceImpl implements PaymentService {
//成功
public String paymentInfo_OK(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id: " + id + "\t" + "哈哈哈";
}
/**
* @Description: 设置超时时间,超过时间自动降级处理,走降级方法。
* @Author: TCL
* @Date: 2022/11/27
* @Time: 8:06
*/
@HystrixCommand(
// 降级方法调用
fallbackMethod = "payment_TimeoutHandler" ,
commandProperties = {
// 设置超时时间,超时5s自动走降级方法
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
}
)
public String payment_Timeout(Integer id) {
// int timeNumber = 3;
int timeNumber = 10; // 模拟超时时间
// int i = 1/0; // 模拟异常
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (Exception e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id: " + id + "\t" + "呜呜呜" + " 耗时(秒)" + timeNumber;
}
//兜底方法,上面方法出问题,我来处理,返回一个出错信息
public String payment_TimeoutHandler(Integer id) {
return "服务提供端,线程池:"+Thread.currentThread().getName()+" payment_TimeoutHandler,系统繁忙,请稍后再试\t o(╥﹏╥)o ";
}
}
一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
②在启动类上添加@EnableHystrix
注解
@SpringBootApplication
@EnableHystrix
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
③启动服务,测试程序
访问:http://localhost:8001/payment/hystrix/timeout/31
测试超时和算数异常,都会走兜底方法——服务降级
7.5.2 服务的消费端设置降级:cloud-consumer-feign-hystrix-order8000
订单微服务,也可以更好的保护自己,自己也依样画葫芦进行客户端降级保护。注意:服务降级可以在服务提供者侧,也可以在服务消费者侧。更多是在服务消费者侧。在消费端中使用服务降级的场景:
- 程序出现异常
- 远程调用出现错误
- 程序连接超时
1.在yml中配置,在远程调用出错时启用Hystrix
feign:
hystrix:
enabled: true #远程调用出错时,启用hystrix
2.在启动类上添加@EnableHystrix
注解
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain8000 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain8000.class,args);
}
}
3.在业务中编写服务降级OrderHystrixController
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
log.info("*******result:"+result);
return result;
}
@HystrixCommand(
fallbackMethod = "payment_TimeoutHandler",
commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")//超过1.5秒就降级自己
}
)
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_Timeout(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级
int timeout = 3000; // 模拟超时,导致降级
if (timeout != 0){
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String result = paymentHystrixService.paymentInfo_Timeout(id);
log.info("*******result:"+result);
return result;
}
//兜底方法,上面方法出问题,我来处理,返回一个出错信息
public String payment_TimeoutHandler(Integer id) {
return "服务的消费端8000,对方支付系统繁忙请10秒后再试。或自己运行出错,请检查自己。";
}
}
4.启动服务进行测试
访问:http://localhost:8000/consumer/payment/hystrix/timeout/31
出现的问题
- 每个业务方法对应一个兜底的方法,代码膨胀,代码耦合
- 统一通用处理和自定义独立处理的分开
7.5.2 定义统一的降级方法
如果没有特殊的处理方法,就使用默认的降级方法,通过@DefaultProperties(defaultFallback = “”)+@DefaultProperties(defaultFallback = “”)设置统一跳转到统一处理结果页面,通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量。
1.代码实现
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
log.info("*******result:"+result);
return result;
}
@HystrixCommand(
fallbackMethod = "payment_TimeoutHandler",
commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")//超过1.5秒就降级自己
}
)
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_Timeout(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级
int timeout = 3000; // 模拟超时,导致降级
if (timeout != 0){
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String result = paymentHystrixService.paymentInfo_Timeout(id);
log.info("*******result:"+result);
return result;
}
//兜底方法,上面方法出问题,我来处理,返回一个出错信息
public String payment_TimeoutHandler(Integer id) {
return "服务的消费端8000,对方支付系统繁忙请10秒后再试。或自己运行出错,请检查自己。";
}
@GetMapping("/consumer/payment/hystrix/timeout02/{id}")
public String paymentInfo_Timeout02(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级
int timeout = 3000; // 模拟超时,导致降级
if (timeout != 0){
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String result = paymentHystrixService.paymentInfo_Timeout(id);
log.info("*******result:"+result);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout03/{id}")
public String paymentInfo_Timeout03(@PathVariable("id") Integer id){
// int age = 1/0; // 模拟异常,导致降级
int timeout = 3000; // 模拟超时,导致降级
if (timeout != 0){
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String result = paymentHystrixService.paymentInfo_Timeout(id);
log.info("*******result:"+result);
return result;
}
//下面是全局fallback方法
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试,(┬_┬)";
}
}
2.重启服务器,测试程序
访问:http://localhost:8000/consumer/payment/hystrix/timeout02/31
http://localhost:8000/consumer/payment/hystrix/timeout03/31
测试结果都是:
3.出现的问题
服务的降级方法和业务逻辑混一起,会出现耦合。服务降级,客户端去调用服务端,碰上服务端宕机或关闭。可能导致整个服务宕机。解决方式只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。
7.5.2 实现服务降级业务解耦
未来我们要面对的异常
- 运行
- 超时
- 宕机
再看我们的业务类PaymentController
修改cloud-consumer-feign-hystrix-order8000
根据cloud-consumer-feign-hystrix-order8000已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理PaymentFallbackService类实PaymentFeignClientService接口.
1.定义PaymentFallbackService类实PaymentFeignClientService
@Component
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
}
@Override
public String paymentInfo_Timeout(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
}
}
2.配置yml,添加容错机制
feign:
hystrix:
enabled: true #远程调用出错时,启用hystrix
3.编写PaymentFeignClientService接口
@FeignClient(value = "CLOUD-HYSTRIX-PAYMENT-SERVICE",fallback = PaymentFallbackService.class)
@Component
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_Timeout(@PathVariable("id") Integer id);
}
4.测试程序
7.6 服务的熔断
7.6.1 概述
1.断路器
- 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
- 就是保险丝
- 服务的降级->进而熔断->恢复调用链路
2.熔断机制概述
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在SpringCloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状态,当失败的调用到一定阈值,缺省是10秒内20次调用并有50%的失败情况,就会启动熔断机制。熔断机制的注解是@HystrixCommand
7.6.2 代码实现-cloud-provider-hystrix-payment8001项目
1.找到修改PaymentServiceImpl,添加服务熔断机制
//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //当在配置时间窗口内达到此数量,打开断路,默认20个
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //断路多久以后开始尝试是否恢复,默认5s
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
})
public String paymentCircuitBreaker(Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();//hutool.cn工具包
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
2.修改PaymentController,添加方法
//===服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("*******result:"+result);
return result;
}
3.启动服务进行测试
自测cloud-provider-hystrix-payment8001
正确: http://localhost:8001/payment/circuit/31
错误: http://localhost:8001/payment/circuit/-31
一次正确一次错误trytry
重点测试
多次错误(狂点),然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路
7.6.3 服务熔断原理
1.大神总结
2.熔断器的类型
熔断打开:
请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入熔断状态。熔断关闭:
熔断关闭不会对服务进行熔断熔断半开:
部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常。关闭熔断 :
当服务一直处于正常状态 能正常调用
3.官网断路器流程图
①官网步骤
②断路器在什么情况下开始起作用
//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"), //当快照时间窗(默认10秒)内达到此数量才有资格打开断路,默认20个
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000"), //断路多久以后开始尝试是否恢复,默认5s
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
})
涉及到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值。
配置属性参考:https://github.com/Netflix/Hystrix/wiki/Configuration
快照时间窗 :
断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。请求总数阈值:
在快照时间窗内,必须满足请求总数阈值才有资格熔断。默认20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开错误百分比阈值:
当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果在这30次调用,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。
③断路器开启或者关闭的条件
- 当满足一定阀值的时候(默认10秒内超过20个请求次数)
- 当失败率达到一定的时候(默认10秒内超过50%请求失败)
- 到达以上阀值,断路器将会开启
- 当开启的时候,所有请求都不会进行转发
- 一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5
④断路器打开之后
- 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallbak。通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果。
- 原来的主逻辑要如何恢复呢?
对于这一个问题,hystrix也为我们实现了自动恢复功能。
当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。
⑤All配置
7.6 服务的限流
因为Hystrix停止维护了,所以后面用阿里的Sentinel来实现服务限流
7.7 服务监控hystrixDashboard
7.7.1 服务监控的概述
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard)
,Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stram项目实现了对以上指示的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
7.7.2 仪表盘9001
1.新建一个模块:cloud-consumer-hystrix-dashboard9001,并导入依赖
<dependencies>
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.配置yml文件
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "*"
3.创建启动类,添加新的注解@EnableHystrixDashboard
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
4.所有的服务提供者添加新的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
5.启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001
访问:http://localhost:9001/hystrix
7.7.3 断路器的演示
1. 修改cloud-provider-hystrix-payment8001,添加一个配置类
@Configuration
public class HystrixConfig {
/**
*此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
*只要在自己的项目里配置上下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
2.测试程序
9001监控8001
http://localhost:8001/hystrix.stream
测试地址
http://localhost:8001/payment/circuit/31
http://localhost:8001/payment/circuit/-31
上述测试通过
ok
先访问正确地址,再访问错误地址,再正确地址,会发现图示断路器都是慢慢放开的
监控结果,成功
监控结果,失败
如何看
7色
1圈
实心圆:共有两种含义。它通过颜色的变化代表了实例的健康程度,它的健康度从绿色<黄色<橙色<红色递减。
该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大。所以通过该实心圆的展示,就可以在大量的实体中快速的发现故障实例和高压力实例。
1线
曲线:用来记录2分钟内流量的相对变化,可以通过它来观察到流量的上升和下降趋势。
整体图说明
整体图说明二
监控多种复杂的服务提提供端
8.Gateway新一代网关
8.1 概述简介
https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关https://github.com/Netflix/zuul/wiki
但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关代替Zull,那就是SpringCloud Geteway;
总之一句话:Geteway是原Zuul1.x版的替代
8.1.1 Gateway概述
- Gateway是在spring生态系统之上构建的API网关服务,基于Spring5,SpringBoot2和Project Reactor等技术。
- Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等
- SpringCloud Gateway是SpringCloud的一个全新项目,基于Spring5.X+SpringBoot2.X和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。
- 为了提升网关的性能,SpringCloud Gatway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通讯框架Netty。
- SpringCloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/指标、和限流。
- 总之Spring Cloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架
8.1.2 网关能干啥
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
- 。。。。。。
8.1.3 微服务架构中网关在哪里
8.2 网关的核心概念
Route(路由):
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
Predicate(断言):
参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
Filter(过滤):
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
Web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
Predicate就是我们的匹配条件: 而Filter,就是可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。
8.3 Gateway工作流程
https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求匹配的路由,将其发送到Gateway Web Handler.
Handler再通过指定的过滤器链来将请求发送给我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
Filter在"pre"类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量控制等有着非常重要的作用
网关的核心逻辑是:路由转发+执行过滤器链
8.4 入门案例
8.4.1 创建新的模块cloud-gateway-gateway9527
创建新的模块cloud-gateway-gateway9527并导入相关依赖
<dependencies>
<!--新增gateway,不需要引入web和actuator模块-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.tlc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
8.4.2 配置yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
8.4.3 添加启动类
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class,args);
}
}
8.4.4 在yml中配置路由映射
cloud:
gateway:
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: http://localhost:8001
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
8.4.5 启动程序测试
启动7001:cloud-eureka-server7001
启动8001:cloud-provider-payment8001
启动9527网关:cloud-gateway-gateway9527
访问说明
添加网关前: http://localhost:8001/payment/get/31
添加网关后: http://localhost:9527/payment/get/31
8.5 通过微服务名实现动态路由
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
一个eureka7001+两个服务提供者8001/8002
修改uri属性:uri: lb://服务名称
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
8.5.1 配置yml文件
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
# uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
8.5.2 启动程序测试
访问:http://localhost:9527/payment/get/31
8.6 Predicate的使用
8.6.1 是什么
启动我们的gatewat9527,查看启动日志
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapper基础框架的一部分。
Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合
Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给 Route。Spring Cloud Gateway包含许
多内置的Route Predicate Factories。
所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and 。
8.6.2 常用的Route Predicate
predicates: #断言,匹配方式,可以有多个,多个同时存在时,必须同时满足才能路由
#- Path=/payment/get/** #断言,路径相匹配的进行路由
#- After=2022-11-26T15:40:50.789+08:00[Asia/Shanghai] #在指定的时间之后访问
#- Before=2022-11-26T15:40:50.789+08:00[Asia/Shanghai] #在指定的时间之前访问
#- Between=2022-11-26T15:40:50.789+08:00[Asia/Shanghai], 2022-11-26T16:40:50.789+08:00[Asia/Shanghai] #在两个时间之间访问
#- Cookie=chocolate, ch.p #请求头携带参数,参数名cookie 参数值chocolate=ch.p
#- Header=X-Request-Id, \d+ #请求头携带参数,参数名X-Request-Id,参数值\d+(正则表达式,最少一个数字)
#- Host=localhost:9527,**.com.org #请求主机地址为localhost:9527或者**.com.org时
#- Method=GET,POST #请求方式为GET或POST
#- Query=green #请求参数中包含名为 green 的参数
8.7 Filter的使用
8.7.1 概述
Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。
- Gateway 提供两种过滤器方式:“pre”和“post”
- pre 过滤器,在转发之前执行,可以做参数校验、权限校验、流量监控、日志输出、协议转换等。
- post 过滤器,在响应之前执行,可以做响应内容、响应头的修改,日志的输出,流量监控等。
- Gateway 还提供了两种类型过滤器
- GatewayFilter:局部过滤器,针对单个路由
- GlobalFilter :全局过滤器,针对所有路由
8.7.2 局部过滤器
GatewayFilter 局部过滤器,是针对单个路由的过滤器。
- 在Spring Cloud Gateway 组件中提供了大量内置的局部过滤器,对请求和响应做过滤操作。
- 遵循约定大于配置的思想,只需要在配置文件配置局部过滤器名称,并为其指定对应的值,就可以让其生效。
8.7.3 全局过滤器
- GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。
- Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。
- 自定义全局过滤器步骤:
- 定义类实现 GlobalFilter 和 Ordered接口
- 复写方法
- 完成逻辑处理
8.7.4 GatewayFilter(31种之多)
https://docs.spring.io/spring-cloud-gateway/docs/2.2.6.RELEASE/reference/html/
8.7.5 GlobalFilter
8.7.6 常用的GatewayFilter
AddRequestParameter
yml配置
filters: #内置的过滤器
- AddRequestParameter=red, blue #添加请求参数,red=blue
在服务提供者cloud-provider-payment8001中编写代码获取参数
@GetMapping("/payment/get/{id}")
public CommonResult<Payment> getById(@PathVariable Long id, HttpServletRequest request) throws JsonProcessingException {
//获取所有的请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
//转为json, 使用的API为jackson
ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.writeValueAsString(parameterMap));
Payment payment = paymentService.getById(id);
return new CommonResult<Payment>(200,"查询成功----端口"+serverPort,payment);
}
重启服务测试
8.8 自定义过滤器
自定义全局过滤器可以实现全局日志记录、统一网关鉴权等功能。
8.8.1 自定义一个类LoginFilter实现GlobalFilter,Ordered接口
@Slf4j
@Component
public class LoginFilter implements GlobalFilter, Ordered {
/**
* 模拟登陆过滤器
* 参数中需要传递一个username
* 如果没有,则判定为未登录,拦截请求
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("**网关**请求进入自定义的过滤器>>>>>>>>>>>>>>>>>>>>");
//获取请求参数username
String username = exchange.getRequest().getQueryParams().getFirst("username");
if(username == null){
//1.拦截请求>>>>>>>>>>>>>>>>>
//设置响应的状态码406
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
//阻止请求继续执行,直接由网关完成响应
return exchange.getResponse().setComplete();
}
//2.放行,让请求继续往后执行>>>>>>>>>>>>>>>>>
return chain.filter(exchange);
}
@Override
public int getOrder() {
//过滤器的优先级,数值越小,优先级越高
return 0;
}
}
8.8.2 重启网关测试
9.SpringCloud Sleuth分布式链路请求跟踪
9.1 概述
Spring Cloud Sleuth 其实是一个工具,它在整个分布式系统中能跟踪一个用户请求的过程,捕获这些跟踪数据,就能构建微服务的整个调用链的视图,这是调试和监控微服务的关键工具。
- 耗时分析
- 可视化错误
- 链路优化
Zipkin 是 Twitter 的一个开源项目,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。
9.2 搭建链路监控步骤
Spring Cloud Sleuth和OpenZipkin(也称为Zipkin)集成。Zipkin是一个分布式跟踪平台,可用于跟踪跨多个服务调用的事务。Zipkin允许开发人员以图形方式查看事务占用的时间量,并分解在调用中涉及的每个微服务所用的时间。在微服务架构中,Zipkin是识别性能问题的宝贵工具。
建立Spring Cloud Sleuth和Zipkin涉及4项操作:
- 将Spring Cloud Sleuth和Zipkin JAR文件添加到捕获跟踪数据的服务中;
- 在每个服务中配置Spring属性以指向收集跟踪数据的Zipkin服务器;
- 定义每个客户端所使用的采样策略,便于向Zipkin发送跟踪信息。
9.2.1 zipkin
下载:https://github.com/openzipkin/zipkin
curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar
运行控制台
http://localhost:9411/zipkin/
9.2.2 服务提供者cloud-provider-payment8001
1.添加依赖
<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
2.配置yml文件
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
#采样率值介于0~1之间,1表示全部采样
probability: 1
3.业务类PaymentController
@GetMapping("/payment/zipkin")
public String paymentZipkin(){
return "hi ,i'am paymentzipkin server,welcome to atguigu,O(∩_∩)O哈哈~";
}
9.2.3 服务消费者(调用方)cloud-consumer-order8000
1.添加依赖
<!--包含了sleuth+zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
2.配置yml文件
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
#采样率值介于0~1之间,1表示全部采样
probability: 1
3.业务类OrderController
//==> zipkin+sleuth
@GetMapping("/consumer/payment/zipkin")
public String paymentZipkin(){
String result = restTemplate.getForObject("http://CLOUD-PAYMENT-SERVICE"+"/payment/zipkin/", String.class);
return result;
}
9.2.4 重启项目测试
① 访问:http://localhost:9411/zipkin/
②访问:http://localhost:8000/consumer/payment/zipkin
③ 打开zipkin页面,点击run query
④点击show,就可以看见详情
10 SpringCloud Alibaba入门简介
10.1 为啥会有SpringCloud Alibaba
Spring Cloud Netflix项目进入维护模式
https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now
说明:
- Spring Cloud Netflix项目进入维护模式
- 最近,Netflix宣布Hystrix正在进入维护模式。自2016年以来,Ribbon已处于类似状态。
- 虽然Hystrix和Ribbon现已处于维护模式,但它们仍然在Netflix大规模部署。
- Hystrix Dashboard和Turbine已被Atlas取代。这些项目的最后一次提交是2年和4年前。Zuul1和Archaius1都被后来不兼容的版本所取代。这不包括Eureka或并发限制模块。
什么是维护模式?
- 将模块置于维护模式,意味着SpringCloud团队将不会再向模块添加新功能。我们将修复block级别的bug以及安全问题,我们也会考虑并审查社区的小型pull request。
- 我们打算继续支持这些模块,直到Greenwich版本被普遍采用至少一年。
进入维护模式意味着什么?
- Spring Cloud Netflix将不再开放新的组件
- 我们都知道SpringCloud版本迭代算是比较快的,因而出现了很多重大ISSU都还来不及Fix就又推出另一个Release了。
- 进入维护模式意思就是目前以致以后一段时间SpringCloud netflix提供的服务和功能就这么多了,不再开发新的组件和功能了。以后将以维护和Merge分支Pull Request为主。
- 新组件功能将以其他替代的方式实现
10.2 SpringCloud Alibaba带来了什么?
10.2.1 概述
诞生:2018.10.31,Spring Cloud Alibaba正式入驻了Spring Cloud官网孵化器,并在Maven中央库发布了第一个版本。
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
10.2.2 作用
- 服务限流降级: 默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
- 服务注册与发现: 适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
- 分布式配置管理: 支持分布式系统中的外部化配置,配置更改时自动刷新。
- 消息驱动能力: 基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
- 分布式事务: 使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
- 阿里云对象存储: 阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度: 提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
- 阿里云短信服务: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
10.2.3 依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
10.2.4 如何用
一整套解决方案,简单理解就是替换Netflix那一套
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: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
11.SpringCloud Alibaba Nacos服务注册和配置中心
11.1 Nacos简介
- 前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service
- 一个更易于构建云原生应用的动态服务发现,配置管理和服务管理中心
- Nacos:Dynamic Naming and Configuration Service
- Nacos就是注册中心+配置中心的组合
- 等价于:
Nacos = Eureka+Config+Bus
- 能干嘛
- 替代Eureka做服务注册中心
- 替代Config做服务配置中心
11.2 各种注册中心比较
据说nacos在阿里巴巴内部有超过10万的实例运行,已经过了类似双十一等各种大型流量的考验
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
11.3 安装并运行Nacos
11.3.1 下载Nacos
https://github.com/alibaba/nacos/releases/tag/1.4.2
11.3.2 安装并运行Nacos
解压安装包,直接在命令行启动
startup.cmd -m standalone
默认:MODE="cluster"集群方式启动,如果单机启动需要设置-m standalone参数,否则,启动失败。
11.3.4 运行后在浏览器访问
http://localhost:8848/nacos
默认账号密码都是nacos
11.3.5 主页面如下
11.4 Nacos作为服务注册中心演示
11.4.1 官方网站
https://spring.io/projects/spring-cloud-alibaba#learn
11.4.2 基于Nacos的服务提供者
1.向父类springcloudparent中添加新的依赖
<!--spring cloud alibaba 2.2.6.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
2.新建Module:cloudalibaba-provider-payment19001,并导入依赖
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3.编写yml配置文件
server:
port: 19001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery: #nacos注册中心>>>>>>>>>>>>>>>>
server-addr: localhost:8848 #配置Nacos地址
#springboot-actuator监控组件
management:
endpoints:
web:
exposure:
include: '*' #默认只公开了/health和/info端点,要想暴露所有端点只需设置成星号
4.添加启动类,在启动类上添加@EnableDiscoveryClient注解
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/get/{id}")
public String get(@PathVariable("id") Integer id) {
return "支付服务调用成功" + id + ",端口:" + serverPort;
}
}
6.启动程序测试
访问:http://localhost:19001/payment/get/1
nacos服务注册中心+服务提供者19001都ok了
8.为了下一章节演示nacos的负载均衡,参照19001新建19002
11.4.3 基于Nacos的服务消费者
1. 新建Module:cloudalibaba-consumer-nacos-order18000,并导入依赖
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.tlc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
为什么支持负载均衡
2.配置yml文件
server:
port: 18000
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
3.创建启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderNacosMain18000 {
public static void main(String[] args){
SpringApplication.run(OrderNacosMain18000.class,args);
}
}
4.创建配置文件,将RestTemplete加载到spring容器中
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5.创建controller控制层,调用服务提供者
@RestController
@Slf4j
public class OrderNacosController{
@Autowired //需要自己创建并存入Spring容器,此处才能注入
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/get/{id}")
public String get(@PathVariable("id") Integer id){
//远程访问 支付服务 提供的接口
String result = restTemplate.getForObject("http://nacos-payment-provider/payment/get/" + id, String.class);
return "消费者远程访问获取到的数据:"+result;
}
}
6.测试
访问:http://localhost:18000/consumer/payment/get/1
11.4.4 服务注册中心对比
1. Nacos和CAP
CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。
如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足。
因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。
2. Nacos支持AP和CP模式的切换
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
11.5 Nacos作为服务配置中心演示
11.5.1 Nacos作为配置中心-基础配置
1. 创建Module:cloudalibaba-config-nacos-client3377,并导入依赖
<dependencies>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web + actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般基础配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.配置yml文件
Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
bootstrap.yml
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #服务注册中心地址
config:
server-addr: localhost:8848 #配置中心地址
file-extension: yaml #指定yaml格式的配置(yml和yaml都可以)
#${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
#nacos-config-client-dev.yaml (一定要与file-extension值保持一致)
application.yml
spring:
profiles:
active: dev #表示开发环境
3.添加启动类
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConfigClientMain3377 {
public static void main(String[] args) {
SpringApplication.run(NacosConfigClientMain3377.class,args);
}
}
4.编写控制层ConfigClientController
@RestController
@RefreshScope //通过SpringCould原生注解@RefreshScope实现配置自动更新
public class ConfigClientController{
@Value("${config.info}") //对应nacos配置:nacos-config-client-dev.yaml
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}
5.在Nacos中添加配置信息
Nacos中的匹配规则
Nacos中的dataid的组成格式与SpringBoot配置文件中的匹配规则
官网 https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
最后公式:
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
nacos-config-client-dev.yaml
Nacos界面配置对应
config:
info: "config info for dev,from nacos config center."
设置DataId
公式: ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
小总结说明
历史配置:Nacos会记录配置文件的历史版本默认保留30天
启动前需要在nacos客户端-配置管理-配置管理栏目下有没有对应的yaml配置文件
运行cloud-config-nacos-client3377的主启动类
调用接口查看配置信息: http://localhost:3377/config/info
8. 自带动态刷新
修改Nacos中的yaml配置文件,查看配置已经刷新
11.5.2. Nacos作为配置中心-分类配置
1.问题
多环境多项目管理
- 问题1
- 实际开发中,通常一个系统会准备
- dev开发环境
- test测试环境
- prod生产环境
- 如何保证指定环境启动时服务能正确读取到 Nacos上相应环境的配置文件呢?
- 实际开发中,通常一个系统会准备
- 问题2
- 一个大型分布式微服务系统会有很多微服务子项目
- 每一个微服务项目又会相应的开发环境、测试环境、预发环境、正式环境….
- 那怎么对这些微服务配置进行管理呢?
2. Nacos的图形化管理界面
- 配置管理
- 命名空间
3. Namespace+Group+Data ID三者关系?为什么这么设计?
最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
默认情况:Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT - Nacos默认的命名空间是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的 Namespace之间是隔离的。 - Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去。Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ),给广州机房的Service微服务起一个集群名字(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。 - 最后是Instance,就是微服务的实例。
4.案例
(1).DataID方案
指定spring.profiles.active和配置文件的DataID来使不同环境下读取不同的配置
默认空间+默认分组+新建dev和test两个DataID
新建dev配置DataID
新建test配置DataID
通过spring.profile.active属性就能进行多环境下配置文件的读取
测试
http://localhost:3377/config/info
配置是什么就加载什么
(2).Group方案
通过Group实现环境区分
新建Group
在nacos图形界面控制台上面新建配置文件DataID
bootstrap+application
在config下增加一条group的配置即可。可配置为DEV_GROUP或TEST_GROUP
(3)Namespace方案
新建dev/test的Namespace
回到服务管理-服务列表查看
按照域名配置填写
nacos-config-client-dev.yaml
config:
info: 9f62d48c-ef2e-4d83-a9fb-c9db5833f93b DEFAULT_GROUP nacos-config-client-dev.yaml
YML
bootstrap
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
group: DEV_GROUP
namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# nacos-config-client-dev.yaml
application
spring:
profiles:
active: dev # 表示开发环境
#active: test # 表示测试环境
#active: info
测试结果:
11.6 Nacos集群和持久化配置(重要)
11.6.1 官网说明
https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
1.官网架构图
集群部署架构图
因此开源的时候推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面
- http://ip1:port/openAPI 直连ip模式,机器挂则需要修改ip才可以使用。
- http://SLB:port/openAPI 挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。
- http://nacos.com:port/openAPI 域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式
2. 上图官网翻译,真实情况
3. 说明
默认Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。
为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL 的存储
。
https://github.com/alibaba/nacos/blob/master/distribution/conf/application.properties
https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql
https://nacos.io/zh-cn/docs/deployment.html
11.6.2 Nacos持久化配置解释
1. Nacos默认自带的是嵌入式数据库derby
https://github.com/alibaba/nacos/blob/develop/config/pom.xml
2. derby到mysql切换配置步骤
①在nacos-server-1.4.2\nacos\conf目录下找到sql脚本nacos-mysql.sql执行脚本
②在nacos-server-1.4.2\nacos\conf目录下找到application.properties
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC
db.user=root
db.password=root
3. 启动nacos,可以看到是个全新的空记录界面,以前是记录进derby
4. 测试:新建配置,发现配置信息写入了MySQL数据库
11.4.3. Linux版Nacos+MySQL生产环境配置
1. 预计需要,1个nginx+3个nacos注册中心+1个mysql
2. Nacos下载linux版本,进行环境准备
https://github.com/alibaba/nacos/releases/tag/1.4.2
nacos-server-1.4.2.tar.gz
3. 集群配置步骤(重点)
①.创建/opt/nacoscluster目录,解压3个节点
②.Linux服务器上mysql数据库配置
- 其中一个节点conf/目录下,找到nacos_mysql.sql文件,创建数据库并导入表结构
③.三个节点conf/application.properties配置
修改每一个节点下server.port,分别设置为:8848,8849,8850,并添加以下配置
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config? characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=root
④三个节点/conf下配置cluster.conf
192.168.6.100:8848
192.168.6.100:8849
192.168.6.100:8850
⑤.启动三个节点前,修改内存大小
启动时,需要修改startup.sh文件内存大小,否则,内存可能不够用。
⑥启动三个节点
查看进程:ps -ef | grep nacos
登录任意节点查看集群:
⑦.Nginx的配置,由它作为负载均衡器
修改nginx的配置文件:vim /usr/local/nginx/conf/nginx.conf
nginx.conf
upstream nacoscluster{
server localhost:8848;
server localhost:8849;
server localhost:8850;
}
server{
listen 1111;
server_name localhost;
location / {
proxy_pass http://nacoscluster;
}
....省略
4.截止到此处,1个Nginx+3个nacos注册中心+1个mysql
测试通过nginx访问nacos
http://192.168.6.100:1111/nacos
新建一个配置测试
linux服务器的mysql插入一条记录
5. 测试微服务从配置中心获取配置
微服务nacos-config-client启动注册进nacos集群
Yml
访问URL:
6. 高可用小总结
12 SpringCloud Alibaba Sentinel实现熔断与限流
12.1 Sentiel介绍
-
https://github.com/alibaba/Sentinel
-
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
-
一句话解释,之前我们讲解过的Hystrix
-
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
-
Sentinel 具有以下特征:
- 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制: Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
-
Sentinel 的主要特性:
-
Sentinel 的开源生态:
-
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
-
Sentinel 社区正在将流量治理相关标准抽出到 OpenSergo 标准中,Sentinel 作为流量治理标准实现。有关 Sentinel 流控降级与容错 spec 的最新进展,请参考 opensergo-specification,也欢迎社区一起来完善标准与实现。
-
下载:https://github.com/alibaba/Sentinel/releases
-
使用方式:https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_spring_cloud_alibaba_sentinel
-
请使用groupId为com.alibaba.cloud和artifactId为spring-cloud- start-alibaba-naco -discovery的启动器。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 解决服务使用中的各种问题
- 服务雪崩
- 服务降级
- 服务熔断
- 服务限流
12.2 安装Sentinel控制台
12.2.1 Sentinel两个核心组件
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
- 后台
- 前台8080
12.2.2 安装步骤
1. 下载
https://github.com/alibaba/Sentinel/releases
下载到本地sentinel-dashboard-1.8.2.jar
2. 运行命令
前提
java8环境OK
8080端口不能被占用
命令
java -jar sentinel-dashboard-1.8.2.jar
3. 访问sentinel管理界面
http://localhost:8080
登录账号密码均为sentinel
12.3 初始化演示工程
12.3.1 启动Nacos8848成功
访问:http://localhost:8848/nacos/
12.3.2 案例
1.建立新的模块:cloudalibaba-sentinel-service18001,并导入依赖
<dependencies>
<dependency>
<groupId>com.tlc</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.配置yml文件
server:
port: 18001
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默认8719,应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用HttpServer
management:
endpoints:
web:
exposure:
include: '*'
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints
3.编写启动类
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp18001 {
public static void main(String[] args) {
SpringApplication.run(MainApp18001.class,args);
}
}
4.编写控制层方法FlowLimitController
@RestController
public class FlowLimitController{
@GetMapping("/testA")
public String testA() {
return "------testA";
}
@GetMapping("/testB")
public String testB() {
return "------testB";
}
}
5.启动服务测试
①启动Sentinel8080
②启动微服务cloudalibaba-sentinel-service18001,然后查看Sentinel8080后台
空空如也,啥都没有
Sentinel采用的懒加载说明
执行一次访问即可
http://localhost:18001/testA
http://localhost:18001/testB
结论
sentinel8080正在监控微服务8401
12.4 流控规则
12.4.1 基本概述
- 流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
- FlowSlot 会根据预设的规则,结合前面 NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot 统计出来的实时信息进行流量控制。
- FlowSlot 会根据预设的规则,结合前面 NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot 统计出来的实时信息进行流量控制。
- 同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。
- 同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。
- resource:资源名,即限流规则的作用对象
- count: 限流阈值
- grade: 限流阈值类型(QPS 或并发线程数)
- limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
- strategy: 调用关系限流策略
- controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
- 控制规则界面
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel?可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阔值类型单机阔值:
- QPS(每秒钟的请求数量):当调用该pi的QPS达到阔值的时候,进行限流
- 线程数:当调用该pi的线程数达到阔值的时候,进行限流
- 是否集群:不需要集群
- 流控模式:
- 直接:api达到限流条件时,直接限流
- 关联:当关联的资源原达到阔值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资原进来的流量,如果达到阔值,就进行限流)【api级别的针对来源】
- 流控效果:
- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor冷加载因子,默认3)的值,从值/codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,
否则无效
12.4.2 流量模式
1.直接(默认)
- 直接->快速失败
- 系统默认
- 测试QPS
- 配置及说明
- 表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
- 快速点击访问: http://localhost:8401/testA
- 结果
- Blocked by Sentinel (flow limiting)
- 测试线程数
- 快速点击访问: http://localhost:8401/testA
- 结果
- 不会出现Blocked by Sentinel (flow limiting)(线程处理请求很快)
- 但是,在映射方法里添加sleep后,同样也会出现Blocked by Sentinel (flow limiting)默认提示信息。
- 不会出现Blocked by Sentinel (flow limiting)(线程处理请求很快)
- 思考???
- 直接调用默认报错信息,技术方面OK but,是否应该有我们自己的后续处理?
- 类似有一个fallback的兜底方法?
2.关联
- 是什么?
- 当关联的资源达到阈值时,就限流自己
- 当与A关联的资源B达到阈值后,就限流自己
- B惹事,A挂了
3.配置A
- 设置效果:
- 当关联资源/testB的QPS阀值超过1时,就限流/testA的REST访问地址,当关联资源到阀值后闲置配置的的资源名。
4. JMeter模拟并发密集访问testB
- 当关联资源/testB的QPS阀值超过1时,就限流/testA的REST访问地址,当关联资源到阀值后闲置配置的的资源名。
- 运行
- 大批量线程高并发访问B,导致A失效了
- 运行后发现testA挂了
- 点击访问http://localhost:8401/testA
- 结果
- Blocked by Sentinel (flow limiting)
5.链路
- 多个请求调用了同一个微服务
12.4.3 流控效果
1. 直接->快速失败(默认的流控处理)
- 直接失败,抛出异常:Blocked by Sentinel (flow limiting)
- 源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
2. 预热
-
说明
- 公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值
-
官网: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6#%E5%9F%BA%E4%BA%8Eqps%E5%B9%B6%E5%8F%91%E6%95%B0%E7%9A%84%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
-
Warm Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过“冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考流量控制-Warm Up文档,具体的例子可以参见WarmUpFlowDemo。 -
通常冷启动的过程系统允许通过的QPS曲线如下图所示:
默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值。 -
限流 冷启动https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81—%E5%86%B7%E5%90%AF%E5%8A%A8
-
源码
- com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
- com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
-
Warmup配置
- 默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
- 案例:阈值为10 + 预热时长设置5秒。
- 系统初始化的阈值为10/3约等于3,即阈值刚开始为3;然后过了5秒后阈值才慢慢升高,恢复到10
- 多次点击http://localhost:8401/testB
- 刚开始不行,后续慢慢OK
- 应用场景
- 如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。
3. 排队等待
- 匀速排队,让请求以均匀的速度通过,阈值类型必须设置成QPS,否则无效。
- 设置含义:/testB每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。
- 匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考流量控制-匀速器模式,具体的例子可以参见PaceFlowDemo。
- 该方式的作用如下图所示:
- 这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这祥的场景,在某一秒有大量的清求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐斩处理这些请求,而不是在第一秒直接拒绝多余的请求。
- 源码:
- com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
- 测试
- 增加打印语句
@GetMapping("/testB")
public String testB() {
log.info(Thread.currentThread().getName()+"\t ...testB");
return "------testB";
}
- 增加线程组:直接10个线程并发,排队被依次处理
12.5 熔断规则
12.5.1 基本概述
概述
- 除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
- 现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
熔断策略
Sentinel 提供以下几种熔断策略:
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
- 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。
界面展示
12.5.2 熔断策略实战
1.慢调用比例
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
①编写控制层代码
@GetMapping("/testA")
public String testA() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "------testA";
}
②配置熔断规则
③测试
- 访问测试: http://localhost:18001/testA
- 5秒内打进10个请求,由于每次请求都大于RT,并且比例阈值100%,所以,熔断器打开。
2.异常比例
当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
①编写控制层代码
@GetMapping("/testB")
public String testB() {
int age = 10/0;
return "------testB";
}
②配置熔断规则
③测试
- 访问测试: http://localhost:18001/testB
- 5秒内打进10个请求,由于每次请求都抛异常,异常比例阈值100%超过50%,所以,熔断器打开,10s后半开。如果再次访问有异常,则继续熔断。
3.异常数
当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
①编写控制层代码
@GetMapping("/testB")
public String testB(){
int age = 10/0;
return "------testB 测试异常数";
}
②配置熔断规则
③测试
- 访问测试: http://localhost:18001/testB
- 5秒内打进10个请求,由于每次请求都抛异常,异常数超过5个,所以,熔断器打开,10s后半开。如果再次访问有异常,则继续熔断。
12.6 热点规则
12.6.1 基本概述
-
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
-
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
-
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
12.6.2 承上启下复习
- 兜底方法
- 分为系统默认和客户自定义,两种
- 之前的case,限流出问题后,都是用sentinel系统默认的提示: Blocked by Sentinel(flow limiting)
- 我们能不能自定义?类似hystrix,某个方法出现问题了,就找对应的兜底降级方法?
- 结论
- 从@HystrixCommand到@SentinelResource
12.6.3 代码实现
1.编写controller控制层代码
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2) {
//int age = 10/0;
return "------testHotKey";
}
//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
return "------deal_testHotKey,o(╥﹏╥)o";
}
2.配置热点规则
- 默认
- @SentinelResource(value = “testHotKey”)
- 异常打到了前台用户界面,不友好
- 自定义兜底方法
- @SentinelResource(value = “testHotKey”,blockHandler = “deal_testHotKey”) //value值与资源名一致即可
- 方法testHostKey里面第一个参数只要QPS超过每秒1次,马上降级处理
3.重启服务测试程序
- error (1秒1下可以,但是,超过则降级,和p1参数有关)
- http://localhost:18001/testHotKey?p1=abc
- error(1秒1下可以,但是,超过则降级,和p1参数有关)
- http://localhost:18001/testHotKey?p1=abc&p2=33
- right(狂点不会触发降级,与p2参数无关)
- http://localhost:18001/testHotKey?p2=abc
12.6.4 参数例外项
- 上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
- 特殊情况
- 普通
- 超过1秒钟一个后,达到阈值1后马上被限流
- 我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
- 特例
- 假如当p1的值等于5时,它的阈值可以达到200
- 普通
- 配置
- 添加按钮不能忘
- 测试
- http://localhost:18001/testHotKey?p1=5 对
- http://localhost:18001/testHotKey?p1=3 错
- 当p1等于5的时候,阈值变为200
- 当p1不等于5的时候,阈值就是平常的1
- 前提条件
- 热点参数的注意点,参数必须是基本类型或者String
- 热点参数的注意点,参数必须是基本类型或者String
12.6.5 其他
- 手动添加一个异常看看…
- @SentinelResource
- 处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理
- RuntimeException
- Int age = 10/0;这个是java运行时报出的运行时异常RuntimeException,@SentinelResource不管
- 总结:
- @SentinelResource主管配置出错,运行出错该走异常走异常
12.7 系统规则
12.7.1 基本概述
- https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
- 在开始之前,我们先了解一下系统保护的目的:
- 保证系统不被拖垮
- 在系统稳定的前提下,保持系统的吞吐量
- 长期以来,系统保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。这个思路给我们带来了不可避免的两个问题:
- load 是一个“结果”,如果根据 load 的情况来调节流量的通过率,那么就始终有延迟性。也就意味着通过率的任何调整,都会过一段时间才能看到效果。当前通过率是使 load 恶化的一个动作,那么也至少要过 1 秒之后才能观测到;同理,如果当前通过率调整是让 load 好转的一个动作,也需要 1 秒之后才能继续调整,这样就浪费了系统的处理能力。所以我们看到的曲线,总是会有抖动。
恢复慢。想象一下这样的一个场景(真实),出现了这样一个问题,下游应用不可靠,导致应用 RT 很高,从而 load 到了一个很高的点。过了一段时间之后下游应用恢复了,应用 RT 也相应减少。这个时候,其实应该大幅度增大流量的通过率;但是由于这个时候 load 仍然很高,通过率的恢复仍然不高。 - TCP BBR 的思想给了我们一个很大的启发。我们应该根据系统能够处理的请求,和允许进来的请求,来做平衡,而不是根据一个间接的指标(系统 load)来做限流。最终我们追求的目标是 在系统不被拖垮的情况下,提高系统的吞吐率,而不是 load 一定要到低于某个阈值。如果我们还是按照固有的思维,超过特定的 load 就禁止流量进入,系统 load 恢复就放开流量,这样做的结果是无论我们怎么调参数,调比例,都是按照果来调节因,都无法取得良好的效果。
- Sentinel 在系统自适应保护的做法是,用 load1 作为启动自适应保护的因子,而允许通过的流量由处理请求的能力,即请求的响应时间以及当前系统正在处理的请求速率来决定。
12.7.2 系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
- CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
12.7.3 配置全局QPS
12.8 @SentinelResource
12.8.1 基本概述
https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
@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(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 - exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
1.8.0 版本开始,defaultFallback 支持在类级别进行配置。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。
12.8.2 流控规则+自定义处理
1.编写控制层方法
//注意: 此处的value不能是/testC,否则请求/testC时会直接调用自定义的兜底方法
@SentinelResource(value = "testC", blockHandler = "backup_testC")
@GetMapping("/testC")
public String testC() {
return "请求执行成功......testC";
}
//自定义兜底方法
public String backup_testC(BlockException e) {
e.printStackTrace();
return "自定义兜底数据......testC";
}
2.编写流量控制规则
12.8.3. 上面兜底方法面临的问题
- 如果使用系统默认的,没有体现我们自己的业务要求。
- 依照现有条件,我们自定义的处理方法又和业务代码耦合在一起,不直观。
- 每个业务方法都增加一个兜底的,那代码膨胀加剧。
- 全局统一的处理方法没有体现。
12.8.4. 集中自定义处理
1.自定义限流处理逻辑
- 创建CustomerBlockHandler类用于自定义限流处理逻辑
- 自定义限流处理类
- 方法必须是public static修饰的。
public class CustomerBlockHandler {
public static String handleException1(BlockException exception){
return "自定义限流处理信息.... CustomerBlockHandler --- 1";
}
public static String handleException2(BlockException exception){
return "自定义限流处理信息.... CustomerBlockHandler --- 2";
}
}
2.编写控制层FlowLimitController
@SentinelResource(value = "testD", blockHandler = "handleException1",
blockHandlerClass = CustomerBlockHandler.class)
@GetMapping("/testD")
public String testD() {
return "请求执行成功......testD";
}
3.重启程序测试
- 启动微服务后先调用一次
- http://localhost:18001/testD
- Sentinel控制台配置流控规则
- 测试后我们自定义的出来了
12.8.5. 更多注解属性说明
/**
* @SentinelResource 与 Hystrix 组件中的@HystrixCommand注解作用是类似的。
* value = "byResourceName" 用于设置资源名称,名称不能与方法的映射路径完全一致,否则正常的请求会直接调用降级方法
* blockHandler 用于引用降级方法。
* blockHandlerClass 用于引用降级方法的处理器类。注意:降级方法必须是static的。否则,无法解析
* blockHandler + blockHandlerClass 只处理配置违规,进行降级处理。代码出现异常,不执行的。
*
* blockHandler + fallback 同时存在,配置违规,代码也有异常,这时,走blockHandler配置文件降级处理
*
* exceptionsToIgnore 设置特定异常不需要降级处理,将异常原样抛出。
*/
@RequestMapping("/fallback/{id}")
@SentinelResource(value = "byFallbackName",
blockHandler = "handleException2", blockHandlerClass = CustomerBlockHandler.class,
fallback = "handleException3", fallbackClass = CustomerBlockHandler.class,
exceptionsToIgnore = IllegalArgumentException.class
)
public String fallback(@PathVariable("id") Long id) {
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
}
if (id == -1) {
CommonResult<Payment> result = new CommonResult<>(444, "数据不存在", null);
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return "请求成功,获取到数据" + id;
}
12.9 熔断框架比较
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔离策略 | 信号量隔离(并发控制) | 线程池隔离/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于慢调用比例、异常比例、异常数 | 基于异常比例 | 基于异常比例、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于 RxJava) | Ring Bit Buffer |
动态规则配置 | 支持近十种动态数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
单机限流 | 基于 QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter |
集群流控 | 支持 | 不支持 | 不支持 |
流量整形 | 支持预热模式与匀速排队控制效果 | 不支持 | 简单的 Rate Limiter 模式 |
系统自适应保护 | 支持 | 不支持 | 不支持 |
热点识别/防护 | 支持 | 不支持 | 不支持 |
多语言支持 | Java/Go/C++ | Java | Java |
Service Mesh 支持 | 支持 Envoy/Istio | 不支持 | 不支持 |
控制台 | 提供开箱即用的控制台,可配置规则、实时监控、机器发现等 | 简单的监控查看 | 不提供控制台,可对接其它监控系统 |
12.10 规则持久化
12.10.1 基本概述
- 一旦我们停止应用,该应用的Sentinel规则将消失,生产环境需要将配置规则进行持久化
- 将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效。
12.10.2 代码实现
1. 修改:cloudalibaba-sentinel-service18001
2.添加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
3.配置yml文件
server:
port: 18001
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
4. 添加Nacos业务规则配置
json内容
[
{
"resource": "/testA",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
5. 启动18001并访问接口
访问18001的任意接口,如:http://localhost:18001/testA
刷新sentinel发现业务规则有了
6. 快速访问测试接口
http://localhost:18001/testA
流控规则生效,响应默认的兜底数据
7.停止18001再看sentinel
因为应用停止,所以监测不到数据
8. 重新启动18001并访问接口
访问18001的接口:http://localhost:18001/testA
再看sentinel,配置出现了,持久化验证通过
9. 了解
若需要配置其它规则,参照对应类的属性进行json的配置
流控规则:com.alibaba.csp.sentinel.slots.block.flow.FlowRule
熔断/降级规则:com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule
热点规则:com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule
系统规则:com.alibaba.csp.sentinel.slots.system.SystemRule
授权规则:com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule
之前的配置可以让我们启动项目就加载nacos配置好的规则,但是在Sentinel 控制台上修改后,nacos中的配置文件不会修改。
如果要做到同步到nacos中,需要对源码进行扩展。