高级篇总结
- 项目旨在日常积累的知识,在真实合理的场景下的运用;并未考虑代码的优雅性
- 高可用集群篇见
核心内容
- 熟练使用SpringCloud Alibaba 和 SpringCloud 的配套组件
SpringCloud Alibaba
- SpringCloud Alibaba简介
- SpringCloud Alibaba Nacos【作为注册中心】
- SpringCloud Alibaba Nacos【作为配置中心】
- SpringCloud Alibaba Sentinel 限流
- SpringCloud Alibaba Seata 分布式事务
- SpringCloud Alibaba OSS 云存储
SpringCloud
- Fegin 声明式远程调用
- Gateway
- Sleuth+Zipkin 服务链路追踪
知识点总结
- 接口幂等性
- 本地事务&分布式事务
- 性能与压力测试
- 缓存&分布式锁
- ElasticSearch
- 异步&线程池
- 单点登录与社交登录
- 商城业务
- RabbitMQ
- 可靠消息机制
- 延时队列
- 支付
- 定时任务&分布式调度
秒杀服务
- 秒杀服务
- Sentinel 限流
- Sleuth&Zipkin 链路追踪与监控
秒杀业务
-
秒杀具有瞬间高并发的特点,针对这一特点,必须要做 限流+异步+缓存(页面静态化)+独立部署
-
限流方式:
- 1、前端限流,一些高并发的网站直接在前端开始限流,例如:小米的验证码设计
- 2、nginx限流,直接负载部分请求到错误的静态页面:令牌算法、漏斗算法
- 3、网关限流,限流的过滤器
- 4、代码中使用分布式信号量
- 5、rabbitmq限流(能者多劳:chanel.basicQos(1) ),保证发挥所有服务器的性能
-
利用商城后台管理系统(前后端分离),进行添加秒杀活动等操作
-
1、优惠营销 -- 每日秒杀 添加秒杀活动
-
2、绑定每个活动秒杀的商品
-
定时任务
简单定时
SpringBoot整合定时任务和异步任务
- 1、
@EnableScheduling和@Scheduled(cron = "* * * * * ?")开启定时任务,自动配置类是TaskSchedulingAutoConfiguration- 1.1、Spring中的cron表达式是由6位组成,不允许第7位的年
- 1.2、在周几的位置,1-7代表周一到周日;MON-SUN
- 1.3、定时任务不应该阻塞,默认是阻塞的
- 可以让业务以异步方式,自己提交到线程池
- 支持定时任务线程池;设置
taskSchedulingProperties - 让定时任务异步执行
@EnableAsync和@Async,自动配置类是TaskExecutionAutoConfiguration
- 最终使用异步+定时任务来完成定时任务不阻塞的功能
分布式定时任务
秒杀活动、商品上架
定时上架
-
为缓解数据库压力,定时将秒杀商品加入到缓存中
-
代码实现
-
存在问题:测试秒杀商品定时上传 ,整分钟上传一次,发现同一个商品会上传多次,这里就需要有接口幂等性保证:优化代码,保证已存在的key,就不再缓存
定时任务在分布式下的问题:多个线程同时执行定时器
解决方案:加分布式锁
解决:
1、加入分布式锁防止并发上架
2、优化redis中key,相同key的商品就不再上架
秒杀页面渲染
-
获取缓存的活动信息,当前时间所处于哪个活动
根据活动,从缓存中取出参与该活动的所有商品
-
商城首页显示,当日秒杀活动的商品
-
商品详情页,显示当前商品是否参与秒杀活动
秒杀流程设计
秒杀(高并发)系统关注的问题
- 1、**服务单一职责+独立部署:**秒杀服务即使自己扛不住压力导致宕机,不能影响其他服务
- 2、**秒杀链接加密:**防止恶意攻击,模拟秒杀请求,1000次/s攻击;防止链接暴露,自己的工作人员,提前秒杀商品
- 3、**库存预热+快速扣减:**秒杀读多写少,无需每次都实时校验库存,我们库存预热,放入redis进行缓存。信号量控制进来的秒杀请求
- 4、**动静分离:**nginx做好动静分离,保证秒杀和商品详情页的动态请求打到后台的服务集群;使用CDN网络,分担本集群压力
- 5、**恶意请求拦截:**识别非法攻击请求并进行拦截
- 6、**流量错峰:**使用各种手段,将流量分担到更大宽度的时间点,比如验证码、加入购物车
- 7、限流&熔断&降级:前端限流+后端限流;限制次数,限制总量,快速失败降级运行,熔断隔离防止雪崩
- 8、**队列削峰:**一万个商品,每个1000件秒杀,双11所有秒杀成功的请求,进入队列,慢慢创建订单,扣减库存即可
秒杀流程
-
流程图
-
订单下单成功,发送消息到MQ(流量削峰)
商品秒杀演示
-
商品秒杀:校验合法性、型号量扣减、幂等性设置、MQ削峰
限流 - SpringCloud Alibaba Sentinel
简介
熔断降级限流(*)
熔断
- 什么是熔断:A服务调用B服务的某个功能,由于网络不稳定或者B服务卡机,导致功能时间超长。如果这样子的次数太多,我们可以直接将B断路了(即A不再请求B接口),凡是调用B的直接返回降级数据,不必等待B的超长执行。这样B的故障问题,就不会级联影响到A
降级
- 什么是降级:整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级【停止服务,所有的调用直接返回降级数据】。以此缓解服务器资源的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户得到正确的响应
异同
- 相同点:
- 1、为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我
- 2、用户最终都是体验到某个功能不可用
- 不同点:
- 1、熔断是被调用方故障,触发的系统主动规则
- 2、降级是基于全局考虑,停止一些正常服务,释放资源
限流
- 什么是限流:对打入服务的请求流量进行控制,使服务能够承担不超过自己能力的流量压力
Sentinel简介
-
随着微服务的流行,服务和服务之间的稳定性变得越来越重要;Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
-
Sentinel特性:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等
Sentinel与Hystrix对比
-
sentinel与hystrix
功能 Sentinel Hystrix 隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号量隔离 熔断降级策略 基于响应时间、异常比率、异常数 基于异常比率 实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava) 动态规则配置 支持多种数据源 支持多种数据源 扩展性 多个扩展点 插件形式 基于注解的支持 支持 支持 限流 基于QPS、支持基于调用关系的限流 有限的支持 流量整形 支持预热模式、匀速器模式、预热排队模式 不支持 系统自适应保护 支持 不支持 控制台 可配置规则、查看秒级监控、机器发现等 简单的监控查看
整合Sentinel
使用步骤
-
Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果;我们说的资源,可以是任何东西,服务,服务里的方法,甚至是一段代码。使用 Sentinel 来进行资源保护,主要分为几个步骤:
- 定义资源
- 定义规则
- 检验规则是否生效
先把可能需要保护的资源定义好(埋点),之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源;对于主流的框架,我们提供适配,只需要按照适配中的说明配置,Sentinel 就会默认定义提供的服务,方法等为资源
定义资源
-
方式一:主流框架的默认适配
-
方式二:抛出异常的方式定义资源
-
方式三:返回布尔值方式定义资源
-
方式四:注解方式定义资源
-
方式五:异步调用支持
注意
blockHandler函数会在原方法被限流/降级/系统保护的时候调用,而fallback函数会针对所有类型的异常。请注意blockHandler和fallback函数的形式要求,更多指引可以参见 Sentinel 注解支持文档。
定义规则
-
Sentinel的所有规则都可以在内存中动态的查询及修改,修改之后立即生效。同时Sentinel也提供相关API,供您来定制自己的规则策略
Sentinel支持以下几种规则:
- 流量控制规则(FlowRule)
- 熔断降级规则(DegradeRule)
- 系统保护规则(SystemRule)
- 来源访问控制规则(AuthorityRule)
- 热点参数规则(ParamFlowRule)
-
重要属性
Field 说明 默认值 resource 资源名,资源名是限流规则的作用对象 count 限流阈值 grade 限流阈值类型,QPS模式(1)或并发线程数模式(0) QPS模式 limitApp 流控针对的调用来源 default,代表不区分调用来源 strategy 调用关系限流策略:直接、链路、关联 根据资源本身(直接) controlBehavior 流控效果(直接拒绝/排队等待/慢启动模式),不支持按调用关系限流 直接拒绝 clusterMode 是否集群限流 否 同一个资源可以同时有多个限流规则,检查规则时会一次检查
控制台
-
Sentinel 控制台包含如下功能:
- 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
- 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
- 规则管理和推送:统一管理推送规则。
- 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。
-
下载安装Sentinel
- 下载地址
- 尽量下载与导入依赖核心包相匹配的版本
-
启动控制台
#默认端口8080 java -jar sentinel-dashboard-1.7.1.jar访问本地:localhost:8080,默认用户名密码都是sentinel
整合Springboot
-
-
第一步:引入依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> -
测试用例
@SentinelResource注解用来标识资源是否被限流、降级;上述例子上该注解的属性sayHello表示资源名注:一般推荐将
@SentinelResource注解加到服务实现上,而在 Web 层直接使用 Spring Cloud Alibaba 自带的 Web 埋点适配;Sentinel Web 适配同样支持配置自定义流控处理逻辑,参考 相关文档
-
-
第二步:编写yml配置文件
spring: cloud: sentinel: transport: port: 8719 dashboard: localhost:8080 -
启动秒杀服务,调用其中一个接口,再观察sentinel的控制台
-
第三步:添加流控规则
添加完成后,页面快速刷新,频繁访问接口,观察结果
注意:在控制台调整参数【默认所有的流控设置都保存在内存中,重启失效】
-
第四步:配置Sentinel实时监控
每一个微服务都导入
spring-boot-starter-actuator依赖,信息(统计)审计模块暴露
endpointmanagement: endpoints: web: exposure: include: "*" -
第五步:自定义流控响应
流控
-
所有微服务都引入
actuator模块,并配置Sentinel控制台地址 -
流控模式&流控效果
流控模式
直接拒绝
- 直接拒绝(
RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException;这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时
关联
- 当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,
read_db和write_db这两个资源分别代表数据库读写,我们可以给read_db设置限流规则来达到写优先的目的:设置strategy为RuleConstant.STRATEGY_RELATE同时设置refResource为write_db。这样当写库操作过于频繁时,读数据的请求会被限流
链路
-
链路限流
流控效果
快速失败
- 直接返回降级结果
Warm Up
-
Warm Up(
RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo -
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
排队等待
-
匀速排队(
RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo -
该方式的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求
注意:匀速排队模式暂时不支持 QPS > 1000 的场景
熔断降级
-
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用
-
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置
对Feign的支持
注意:2.2.0 RELEASE的Sentinel 与 OpenFeign 整合会出现启动失败,因此需要切换版本
-
Sentinel适配了Feign组件,如果想要使用,除了引入
spring-cloud-starter-alibaba-sentinel的依赖外还需要2个步骤:- 配置文件打开Sentinel对Feign的支持:
feign.sentinel.enable=true - 加入
spring-cloud-starter-openfeign依赖使 Sentinel starter 中的自动化配置类生效
- 配置文件打开Sentinel对Feign的支持:
-
第一种:调用方的熔断保护(
feign.sentinel.enable=true),为配置熔断保护的情况下,停止秒杀服务-
访问商品详情时,远程调用秒杀服务失败,页面直接崩溃
-
开启fegin支持,并设置熔断异常处理,停止秒杀服务
页面可以正常访问,并且日志显示进入了降级保护
-
-
第二种:调用方手动指定远程服务的降级策略
一旦达到设置触发降级的条件,远程服务会被降级处理,触发降级回调方法
-
第三种:远程服务设置
- 超大流量的时候,必须牺牲一些远程服务,这时可以在服务的提供方(远程服务)指定降级策略;提供方是仍在运行,但至今返回设置的熔断数据(限流数据)
熔断&降级策略
- **慢调用比例 (
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 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:
GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫my_api,请求 path 模式为/foo/**和/baz/**的都归到my_api这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
Spring Cloud Gateway
-
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
- 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
-
使用
-
第一步:引入依赖
<!--sentinel-spring-cloud-gateway--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency> -
重启Sentinel服务与网关服务,这里的属性跟其他微服务略有差异
-
-
自定义网关限流回调数据
Sleuth+Zipkin服务链路追踪
为什么用
- 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元。由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位。主要体现在:一个请求可能需要调用多个服务,而内部服务的调用复杂性,决定了问题难以定位。所以微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出了问题,很快定位
- 链路追踪组件由Google的Dapper、Twitter的Zipkin、以及阿里的Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件
基本术语
-
Span(跨度):基本工作单元,发送一个远程调度任务就会产生一个Span,Span是一个64位ID唯一标识的,Trace是用另一个64位ID唯一标识的;Span还有其他数据信息:比如摘要、时间戳事件、Span的ID、以及进度ID
-
Trace(跟踪):一系列Span组成的一个树状结构。请求一个微服务系统的API接口,这个API接口,需要调用多个微服务,调用每个微服务都会产生一个新的span,所有由这个请求产生的Span组成了这个Trace
-
Annotation(标注):用来及时记录一个事件的,一些核心注解用来定义一个请求的开始和结束。这些注解包括以下:
- cs (Client Sent):客户端发送一个请求,这个注解描述了这个Span的开始
- sr(Server Received):服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络传输的时间
- ss(Server Sent):服务端发送响应;该注解表明请求处理的完成(当请求返回客户端),如果ss的时间戳减去sr时间戳,就可以得到服务器请求的时间
- cr(Client Received):客户端接收响应;此时Span的结束,如果cr的时间戳减去cs的时间戳便是整个请求所消耗的时间
上述的整个流程图如下:
整合Sleuth
-
第一步:导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> -
第二步:打开debug日志
logging.level.org.springframework.cloud.openfeign=debug logging.level.org.springframework.cloud.sleuth=debug
整合Zipkin可视化观察
-
通过Sleuth产生的调用链监控信息,可以得知微服务之间的调用链路,但监控信息只输出到控制台不方便查看。我们需要一个图形化的工具 zipkin;Zipkin是Twitter开源的分布式跟踪系统,主要用来收集系统的时序数据,从而追踪系统的调用问题
-
第一步:docker 安装 zipkin
docker run -d -p 9411:9411 openzipkin/zipkin -
第二步:引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>添加相关配置
zipkin: base-url: http://192.168.83.133:9411/ discovery-client-enabled: false sender: type: web sleuth: sampler: probability: 1
注意:这个zipkin与sleuth,启动时报redis连接超时
原因:spring-cloud-starter-zipkin 和 spring-boot-starter-data-redis 存在包冲突
解决方法:移除
spring-boot-starter-data-redis中对lettuce的引用,加入jedis的依赖
-
查看zipkin,商城所有的请求调用情况
Zipkin数据持久化
-
Zipkin默认是将监控数据存储在内存的,如果zipkin挂掉或者重启的话,那么监控数据就会丢失。所以如果想要搭建生产可用的Zipkin,就需要实现监控数据的持久化。而想要实现数据持久化,自然就是得将数据存储至数据库
- 内存(默认)
- MySQL
- ElasticSearch
- Cassandra
-
Zipkin支持的这几种存储方式中,内存显然是不适用于生产环境的,而使用MySQL的话,当数据量大时,查询较为缓慢,也不建议使用;Twitter官方使用的是Cassandra作为Zipkin存储数据库,但国内大规模使用Cassandra的公司较少,而且相关文档也不多
综上,故采用ElasticSearch是个比较好的选择,关于使用ElasticSearch作为Zipkin的存储数据库的官方文档如下:storage-elasticsearch
-
docker 方式启动存储
docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.83.133:9200 openzipkin/zipkin-dependencies