06谷粒商城-高级篇六

前言

人到洛阳花似锦,偏我来时不逢春

17.商城业务-秒杀服务

17.1后台添加秒杀商品

主要步骤:

  • 导入每日秒杀前端界面
  • gulimall-gateway网关服务配置gulimall-coupon路由
  • 优化秒杀关联商品接口

导入每日秒杀前端界面

image-20240820010232151

gulimall-gateway网关服务配置gulimall-coupon路由

image-20240820010322230

优化秒杀关联商品接口

image-20240820010519374

界面

image-20240820010549678

17.2定时任务&Cron表达式

创建gulimall-seckill秒杀服务

创建gulimall-seckill秒杀服务

image-20240820201135735

导入依赖,主要有rabbitmqredisspringsession

image-20240820202908624

配置application.yaml

image-20240820202930534

gulimall-seckill开启SpringSessionOpenFeignnacos,禁用datasource

image-20240820202945858

定时任务

语法:秒 分 时 日 月 周 年(Spring 不支持)

http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html

image-20240820203200736

cron 示例

image-20240820203225277

17.3SpringBoot整合定时任务与异步任务

开启定时任务

  • @EnableScheduling【spring 默认是使用自己的定时任务,如果想整合Quartz,参考官方】
  • @Scheduled
  • 定时任务配置类:TaskSchedulingAutoConfiguration
@Slf4j
@Component
@EnableScheduling
public class HelloScheduled {
    // 每s执行
    @Scheduled(cron = "0/1 * * * * ? ")
    public void hello() {
        log.info("hello...");
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
    }
}

image-20240820204927308

定时任务+异步任务

  • 方案1:业务方法自己作异步编排【CompletableFuture.runAsync
  • 方案2:修改定时任务线程池的线程个数【spring.task.scheduling.pool.size=5】(不一定生效,有BUG)
  • 方案3:让定时任务异步执行

方案3:让定时任务异步执行

  • 1.@EnableAsync
  • 2.@Async
  • 3.异步配置类:TaskExecutionAutoConfiguration
    • spring.task.execution.pool.core-size=5:核心线程数
    • spring.task.execution.pool.max-size=50:最大线程数

@EnableAsync@Async

@Slf4j
@Component
@EnableAsync
@EnableScheduling
public class HelloScheduled {
    // @Scheduled(cron = "*/5 * * ? * 4")
    // 每s执行
    @Async
    @Scheduled(cron = "0/1 * * * * ? ")
    public void hello() {
        log.info("hello...");
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }

    }

}

异步配置类

spring:
  task:
    execution:
      pool:
        core-size: 5 # 核心线程数
        max-size: 50 # 最大线程数

image-20240820205454470

17.4时间日期处理

主要步骤:

  • gulimall-seckill秒杀服务远程调用gulimall-coupon优惠卷服务查询最近三天的活动
  • gulimall-coupon优惠卷服务实现查询最近三天的活动
    • 处理最近三天的日期

gulimall-seckill秒杀服务远程调用gulimall-coupon优惠卷服务查询最近三天的活动

image-20240820213720100

gulimall-coupon优惠卷服务实现查询最近三天的活动

  • 处理最近三天的日期

image-20240820213758106

测试代码

@Test
    public void testTime(){
        LocalDate now = LocalDate.now();
        LocalDate plus1 = now.plusDays(1);
        LocalDate plus2 = now.plusDays(2);
        System.out.println(now);
        System.out.println(plus1);
        System.out.println(plus2);

        LocalTime min = LocalTime.MIN;
        LocalTime max = LocalTime.MAX;
        System.out.println(min);
        System.out.println(max);

        LocalDateTime start = LocalDateTime.of(now,min);
        LocalDateTime end = LocalDateTime.of(plus2,max);
        System.out.println(start);
        System.out.println(end);

        // 格式化最近三天的日期
        String format1 = start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String format2 = end.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println("开始时间:"+format1);
        System.out.println("结束时间:"+format2);
    }

image-20240820213630284

17.5秒杀商品上架-1

主要步骤:

  • 远程调用gulimall-coupon查询最近三天的活动
  • redis中缓存活动信息
  • redis中缓存活动关联的商品信息
    • 商品信息,远程调用gulimall-product,获取商品sku信息

redis中缓存活动信息

image-20240820215727896

redis中缓存活动关联的商品信息

  • 商品信息

image-20240820220014740

17.6秒杀商品上架-2

主要步骤:

  • 远程调用gulimall-coupon查询最近三天的活动
  • redis中缓存活动信息
  • redis中缓存活动关联的商品信息
    • 商品信息,远程调用gulimall-product,获取商品sku信息
    • 秒杀信息
    • 秒杀时间信息
    • 设置商品的随机码(防止恶意攻击)
    • 使用库存作为分布式Redisson信号量(限流)

image-20240820221151247

17.7秒杀商品上架-3

主要步骤:

  • 测试秒杀商品上架

启动定时任务开启秒杀商品上架,设置的cron表达式每分钟执行一次

@Slf4j
@Service
public class SeckillScheduled {

    @Autowired
    private SeckillService seckillService;

    // TODO 保证幂等性问题
    // @Scheduled(cron = "*/5 * * * * ?")
    @Scheduled(cron = "0 * * * * ?")
    public void uploadSeckillSkuLatest3Days() {
        log.info("上架秒杀的商品...");
        seckillService.uploadSeckillSkuLatest3Days();
    }
}

image-20240820223237920

缓存活动信息的keySESSION_CACHE_PREFIX + startTime + "_" + endTime

缓存活动信息的value活动场次Id + "_" + 商品skuId

image-20240820222346268

缓存秒杀商品的的keyseckill:skus

缓存秒杀商品的的value

  • key活动场次Id + "_" + 商品skuId
  • value:商品sku信息、秒杀信息、秒杀时间信息、商品的随机码(防止恶意攻击)、使用秒杀总量作为分布式Redisson信号量(限流)

image-20240820222618807

使用秒杀总量作为分布式Redisson信号量(限流)

分布式Redisson信号量的keyseckill:stock:

分布式Redisson信号量的value:秒杀总量

image-20240820223022448

17.8幂等性保证

主要步骤:

  • 秒杀商品上架时添加分布式锁
  • redis保存活动信息,判断seckill:sessions: + startTime + "_" + endTime是否已经存在
  • redis保存秒杀商品信息时,判断seckill:skus中是否存在活动场次Id + "-" + 商品skuId

秒杀商品上架时添加分布式锁

image-20240820232431085

redis保存活动信息,判断seckill:sessions: + startTime + "_" + endTime是否已经存在

image-20240820231955454

redis保存秒杀商品信息时,判断seckill:skus中是否存在活动场次Id + "-" + 商品skuId

image-20240820232211591

17.9查询秒杀商品

主要步骤:

  • 管理后台创建一个当前时间范围内的秒杀活动,并关联商品

  • 实现获取到当前可以参加秒杀商品的信息

    • 获取当前时间,获取活动场次的所有key,因为key中包含活动场次开始时间和结束时间,查询出活动场次中包含的所有秒杀商品的key(场次Id + "-" + 商品skuId
    • 根据开始时间和结束时间获取所有秒杀商品的key,再去seckill:skus获取所有的秒杀商品
  • 秒杀商品界面渲染

  • 管理员运行SwitchHost,配置seckill.gulimall.com转发

  • gulimall-gateway网关服务配置gulimall-seckill秒杀服务路由

管理后台创建一个当前时间范围内的秒杀活动,并关联商品

image-20240821045806681

实现获取到当前可以参加秒杀商品的信息

  • 获取当前时间,获取活动场次的所有key,因为key中包含活动场次开始时间和结束时间,查询出活动场次中包含的所有秒杀商品的key(场次Id + "-" + 商品skuId
  • 根据开始时间和结束时间获取所有秒杀商品的key,再去seckill:skus获取所有的秒杀商品

image-20240821005057301

秒杀商品界面渲染

image-20240821005150648

主界面秒杀商品渲染完成

image-20240821012027008

管理员运行SwitchHost,配置seckill.gulimall.com转发

image-20240821050902272

gulimall-gateway网关服务配置gulimall-seckill秒杀服务路由

- id: gulimall-seckill_route
  uri: lb://gulimall-seckill
  predicates:
    - Host=seckill.gulimall.com

image-20240821020332548

Bug:数据库时间和界面显示时间相差8小时

主要配置serverTimezone=Asia/Shanghaitime-zone

spring:
  application:
    name: gulimall-coupon
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.188.180:8848 # nacos地址
  datasource:
    url: jdbc:mysql://192.168.188.180:3306/mall_sms?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

image-20240821011941905

17.10秒杀页面渲染

主要步骤:

  • gulimall-seckill实现skuId查询商品秒杀信息
    • 查询seckill:skus(所有秒杀商品信息),获取所有keys
    • 遍历seckill:skus,判断key是否包含skuId
    • 如果包含skuId,判断当前时间是否在秒杀活动时间区间
  • gulimall-product使用OpenFeign添加gulimall-seckill的商品秒杀信息接口
  • gulimall-productskuItem方法获取sku商品详情时,异步编排调用gulimall-seckill查询商品秒杀信息
  • item.html商品详情界面添加秒杀信息

gulimall-seckill实现skuId查询商品秒杀信息

  • 查询seckill:skus(所有秒杀商品信息),获取所有keys
  • 遍历seckill:skus,判断key是否包含skuId
  • 如果包含skuId,判断当前时间是否在秒杀活动时间区间

image-20240821022103125

gulimall-product使用OpenFeign添加gulimall-seckill的商品秒杀信息接口

image-20240821022121262

gulimall-productskuItem方法获取sku商品详情时,CompletableFuture异步编排调用gulimall-seckill查询商品秒杀信息

image-20240821022153944

item.html商品详情界面添加秒杀信息

image-20240821022330374

商品详情界面

image-20240821022351377

17.11秒杀系统设计

秒杀系统设计

  • 单一职责
  • 秒杀链接加密
    • 随机码,秒杀开始才暴露
  • 库存预热+快速扣减(redis存储库存信号量,最终正常进入购物车的流量最多是库存数)
    • 按照库存信号量原子扣减
  • 动静分离
    • nginx/CDN
  • 恶意请求拦截
    • 网关层按照访问次数拦截脚本请求【异常请求】
  • 流量错峰
    • 【最重要是体现在秒杀开始的那一刻的错峰】判断登录状态、输入验证码、加入购物车、提交订单
  • 限流&熔断&降级
    • 前端限流:间隔1秒允许点击
    • 后端限流:
      • 限制次数:同一个用户10次放行2次
      • 限制总量:秒杀服务峰值处理能力10万,网关层放行不得超过10万,超过的等待两秒放行
    • 熔断:A->B->C,链路中B总是失败,则下次调用时直接返回错误不调用B
    • 降级:流量太大,秒杀模块将流量引导到降级页面,服务繁忙页【正常请求】
  • 队列削峰(杀手锏)
    • 扣减库存信号量成功的秒杀信息存入队列,订单系统监听队列创建订单(按照自己的处理能力消费

image-20240821023625624

image-20240821023631646

17.12登录检查

主要步骤:

  • gulimall-product商品服务的item.html商品详情页面,如果商品在秒杀活动时段显示立即抢购,否则显示加入购物车
  • 立即抢购需要参数:killId(场次活动Id + 商品skuId)、秒杀随机码、商品数量
  • gulimall-seckill调用秒杀接口/kill,需要登录操作,因此需要配置拦截器和springsession

gulimall-product商品服务的item.html商品详情页面,如果商品在秒杀活动时段显示立即抢购,否则显示加入购物车

image-20240821030212197

立即抢购需要参数:killId(场次活动Id + 商品skuId)、秒杀随机码、商品数量

image-20240821030230316

gulimall-seckill调用秒杀接口/kill,需要登录操作,因此需要配置拦截器和springsession

配置登录拦截器

image-20240821030309173

配置springsession

image-20240821030414408

17.13秒杀流程

image-20240821043532371

主要步骤:

  • 实现秒杀接口
    • 从Redis中获取中获取当前秒杀商品的详细信息
    • 合法性效验:判断当前这个秒杀请求是否在活动时间区间内(效验时间的合法性)
    • 效验随机码和商品id
    • 验证购物数量是否合理和库存量是否充足:判断信号量是否大于0,并且买的数量不能超过限购数量
    • 验证这个人是否已经买过了(幂等性处理),如果秒杀成功,就去占位。userId-sessionId-skuId

image-20240821044347519

17.14秒杀效果完成

流程图:

image-20240821044503294

两种方案:

  • 方案一:
    • 加入购物车(仍然走购物车流程,但价格按照秒杀价格计算),创建订单、锁定库存
    • 优点:只需要做好适配,无大改动
    • 缺点:将秒杀的流量带给了其他模块
    • image-20240821052907381

image-20240821052858858

  • 方案二:【采用方案二,队列削峰】
    • 直接发送MQ消息,订单根据消息创建订单(不需要锁定库存,库存预热了【信号量】),订单关闭增加信号量

image-20240821052925646

  • 加信号量

    • 优点:没有将秒杀的压力分担给其他模块,只有校验合法性没有远程调用、db操作
    • 缺点:订单等模块需要提供监听消费信息创建订单,如果订单崩了,会导致支付失败
  • 结果:

    • 假设一个请求50ms,一个线程1s能处理20个请求
    • Tomcat开启500个线程,1s能处理10000个请求

主要步骤:

  • 实现秒杀接口

    • 从Redis中获取中获取当前秒杀商品的详细信息
    • 合法性效验:判断当前这个秒杀请求是否在活动时间区间内(效验时间的合法性)
    • 效验随机码和商品id
    • 验证购物数量是否合理和库存量是否充足:判断信号量是否大于0,并且买的数量不能超过限购数量
    • 验证这个人是否已经买过了(幂等性处理),如果秒杀成功,就去占位。userId-sessionId-skuId
    • 完成以上验证,向order-event-exchange发送消息,routingKey设置为order.seckill.order
  • gulimall-order订单服务创建队列order.seckill.order.queue,并和order-event-exchange建立绑定关系

  • gulimall-order监听order.seckill.order.queue创建秒杀订单

实现秒杀接口

  • 从Redis中获取中获取当前秒杀商品的详细信息
  • 合法性效验:判断当前这个秒杀请求是否在活动时间区间内(效验时间的合法性)
  • 效验随机码和商品id
  • 验证购物数量是否合理和库存量是否充足:判断信号量是否大于0,并且买的数量不能超过限购数量
  • 验证这个人是否已经买过了(幂等性处理),如果秒杀成功,就去占位。userId-sessionId-skuId
  • 完成以上验证,向order-event-exchange发送消息,routingKey设置为order.seckill.order

image-20240821053022703

gulimall-order订单服务创建队列order.seckill.order.queue,并和order-event-exchange建立绑定关系

image-20240821053058111

gulimall-order监听order.seckill.order.queue创建秒杀订单

image-20240821053130132

17.15秒杀页面完成

主要步骤:

  • 秒杀成功,跳转到秒杀成功页面,然后进行支付-
  • gulimall-seckill导入thymeleaf,并关闭缓存
  • 秒杀成功界面渲染

秒杀成功,跳转到秒杀成功页面,然后进行支付

image-20240821221724185

gulimall-seckill导入thymeleaf,并关闭缓存

image-20240821221926563

秒杀成功界面渲染

image-20240821221803916

image-20240821221949603

18.Sentinel

18.1高并发方法论&简介

熔断

A 服务调用 B 服务的某个功能,由于网络不稳定问题,或者 B 服务卡机,导致功能时 间超长。如果这样子的次数太多。我们就可以直接将 B 断路了(A 不再请求 B 接口),凡是 调用 B 的直接返回降级数据,不必等待 B 的超长执行。 这样 B 的故障问题,就不会级联影 响到 A。

降级

整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和 页面进行有策略的降级[停止服务,所有的调用直接返回降级数据]。以此缓解服务器资源的 的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的相应。

异同:

相同点:

  • 1、为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我

  • 2、用户最终都是体验到某个功能不可用

不同点:

  • 1、熔断是被调用方故障,触发的系统主动规则
  • 2、降级是基于全局考虑,停止一些正常服务,释放资源

限流

对打入服务的请求流量进行控制,使服务能够承担不超过自己能力的流量压力

18.2基本概念

Sentinel 简介

官方文档:https://github.com/alibaba/Sentinel/wiki/介绍

项目地址:https://github.com/alibaba/Sentinel

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场 景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集 群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入 应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如 与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配 置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过 实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

image-20240822214331035

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时 对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

Sentinel 基本概念

资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提 供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文 档中,我们都会用资源来描述代码块。

只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下, 可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

规则

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规 则。所有规则可以动态实时调整。

Hystrix 与 Sentinel 比较

image-20240822213910554

18.3整合SpringBoot

主要步骤:

  • docker安装sentinel

  • SpringBoot整合sentinel

  • 测试

docker安装sentinel

如果拉取镜像失败,更换一下镜像地址,这种方式最方便

拉取到镜像后,后面可以把sentinel添加到docker-compose.yml

cat <<EOF > /etc/docker/daemon.json
{
     "max-concurrent-downloads": 10,
     "max-concurrent-uploads": 5,
     "default-shm-size": "1G",
     "debug": true,
     "experimental": false,
     "registry-mirrors":[
                "https://x9r52uz5.mirror.aliyuncs.com",
                "https://dockerhub.icu",
                "https://docker.chenby.cn",
                "https://docker.1panel.live",
                "https://docker.awsl9527.cn",
                "https://docker.anyhub.us.kg",
                "https://dhub.kubesre.xyz"
        ]
}
EOF
systemctl daemon-reload
systemctl restart docker

docker pull bladex/sentinel-dashboard
docker run --name sentinel -p 8858:8858 -td bladex/sentinel-dashboard

image-20240822224249621

访问http://你的虚拟机ip:8858/#/dashboard

用户名:sentinel

密码:sentinel

image-20240822224327944

image-20240822224315418

整合SpringBoot

Spring Cloud Alibaba Sentinel文档说明:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

这里以gulimall-seckill秒杀服务为主,正常来说所有服务都需要引用sentinel

导入依赖

<!--sentinel-->
<dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
 </dependency>

不知道为什么这里现在需要导入hibernate-validator,要不会报错,记录一下

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

image-20240822231157031

配置application.yamlsentinelspring.cloud节点下

spring:
  cloud:
    sentinel:
      transport:
        # 控制台信息
        dashboard: 192.168.188.180:8858

image-20240822231916005

测试

启动项目,设置/getCurrentSeckillSkusQPS为1,就是每s只允许1个请求

image-20240822232220409

访问接口http://seckill.gulimall.com/getCurrentSeckillSkus,连续多点几次,发现请求会直接返回失败

image-20240822232352858

后台控制台发现接口只被调用2次,说明收到了流控控制

image-20240822232336810

18.4自定义流控响应

主要步骤:

  • 自定义流控响应

  • application.yaml配置,暴露的 endpoint 路径为 /actuator/sentinel

  • /getCurrentSeckillSkus添加QPS

官方地址:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#endpoint-支持

image-20240823000643637

bug:导入Sentinel无WebCallbackManager包

https://blog.csdn.net/m0_70651612/article/details/125365554

image-20240823001853904

自定义流控响应

我用的最新版的sentinel,之前的WebCallbackManager实现的自定义响应好像不太好使,查询资料后发现需要实现sentinel-spring-webmvc-adapter包的BlockExceptionHandler

导入spring-boot-starter-actuator,是审计框架,计算springboot应用健康状况信息、请求的调用信息,dashboard可以拿到actuator数据作实时监控统计

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

导入sentinel-spring-webmvc-adapter,实现自定义响应

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-webmvc-adapter</artifactId>
</dependency>

实现BlockExceptionHandler

@Configuration
public class MySentinelConfig implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        R error = R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(), BizCodeEnum.TO_MANY_REQUEST.getMsg());
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().write(JSON.toJSONString(error));
    }
}

image-20240823004725135

application.yaml配置,暴露的 endpoint 路径为 /actuator/sentinel

management:
  endpoints:
    web:
      exposure:
        include: '*'

image-20240823005055418

/getCurrentSeckillSkus添加QPS

因为Sentinel 保存在服务内存中,每次重启服务都需要重新配置

image-20240823003649549

访问接口:http://seckill.gulimall.com/getCurrentSeckillSkus,F5多刷新几次

image-20240823003742016

审计界面

image-20240823005200631

18.5全服务引入

主要步骤:

  • 所有的服务导入sentinel相关包
  • 所有的服务application.yml配置sentinel
  • 把商城服务的所有的流程走一遍

所有的服务导入sentinel相关包

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--sentinel统计审计信息-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-webmvc-adapter</artifactId>
</dependency>

image-20240823012101728

所有的服务配置sentinel

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.188.180:8848 # nacos地址
    sentinel:
      transport:
        # 应用开启端口,接收dashboard限流规则
        # port: 8719
        # 控制台信息
        dashboard: 192.168.188.180:8858
management:
  endpoints:
    web:
      exposure:
        include: '*' 

image-20240823012039038

把服务的所有的流程走一遍,到支付即可

image-20240823012256700

Sentinel成功监控了所有的服务

image-20240823012323508

18.6流控模式&效果

官方文档:https://github.com/alibaba/Sentinel/wiki/流量控制

image-20240823013429767

18.7熔断降级

主要步骤:

  • 调用方的熔断保护:feign.sentinel.enable=true
  • 调用方手动指定远程服务的降级策略。远程服务被降级处理。触发我们的熔断回调方法
  • 超大流量的时候,必须牺牲一些远程服务。在服务的提供方(远程服务)指定降级策略;提供方是在运行,但是不允许自己的业务逻辑,返回的是默认的降级数据(限流的数据)

调用方的熔断保护

Feign支持文档:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#feign-支持

image-20240823021950541

导入依赖,使 Sentinel starter 中的自动化配置类生效

<!--sentinel Feign支持-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

添加配置,打开 Sentinel Feign 的支持

feign:
  sentinel:
    enabled: true

image-20240823023646847

我们发现gulimall-product商品服务中调用的openfeign远程和接口也显示出来了

image-20240823023916230

创建SeckillFeignServiceFallBack,实现SeckillFeignService

当远程调用SeckillFeignService失败时,会调用这个接口进行返回

image-20240823024251624

因为商品详情会远程调用gulimall-seckill判断是够是秒杀商品,关掉gulimall-seckill服务模拟宕机,发现界面依然显示,没有出错

image-20240823024014718

调用方手动指定远程服务的降级策略。远程服务被降级处理,触发我们的熔断回调方法

熔断策略文档:https://github.com/alibaba/Sentinel/wiki/熔断降级#熔断策略

image-20240823025132384

修改测试代码,/sku/seckill/{skuId}睡眠500ms,使该接口触发熔断

image-20240826014508196

配置熔断规则:

  • 最大 RT:这个值表示请求的最大响应时间阈值(单位为毫秒)。在这个配置中,最大 RT 设置为 1 ms,这意味着如果某个请求的响应时间超过 1 毫秒,就会触发统计。

  • 熔断时长:熔断器触发后的持续时间。在这里,熔断时长设置为 5 秒,即一旦熔断触发,该资源在 5 秒内将会被熔断,所有请求将直接返回异常。

  • 统计时长:这是用于统计的时间窗口(单位为毫秒)。在这个配置中,统计时长设置为 1000 ms(即 1 秒)。在这个时间窗口内,Sentinel 将会统计请求的响应时间,并根据配置的阈值来决定是否触发熔断。

  • 比阈值:表示在统计时长内,请求的 RT 超过最大 RT 阈值的比例阈值。在这里设置为 0.1(10%),意味着如果在统计时长内,有 10% 的请求的响应时间超过了最大 RT,则触发熔断。

  • 最小请求数:这是触发熔断所需的最小请求数。在这个配置中,设置为 1,意味着只要有一个请求超过了最大 RT 且满足比阈值的条件,熔断就会触发。

总结来说,这个配置的意思是:如果在 1 秒内有至少 1 个请求且其中 10% 以上的请求的响应时间超过了 1 毫秒,那么这个资源将被熔断 5 秒。

image-20240826014110157

超大流量的时候,必须牺牲一些远程服务。在服务的提供方(远程服务)指定降级策略;提供方是在运行。但是不运行自己的业务逻辑,返回的是默认的降级数据(限流的数据)

我们给gulimall-seckill服务的/getCurrentSeckillSkus的接口配置熔断策略

  • 最大 RT:设置为 1 ms,表示如果请求的响应时间超过 1 毫秒,这个请求将会被纳入统计。
  • 熔断时长:设置为 5 秒,表示一旦触发熔断,这个资源会在接下来的 5 秒内直接返回异常,不会真正执行请求。
  • 统计时长:设置为 1000 ms,表示 Sentinel 会在 1 秒的时间窗口内统计请求的响应时间。
  • 比阈值:设置为 0.3(30%),表示在统计时长内,如果有 30% 以上的请求响应时间超过了最大 RT(1 ms),将会触发熔断。
  • 最小请求数:设置为 3,表示在统计时长内,至少有 3 个请求时,才会开始计算并判断是否触发熔断。

总结来说,这个配置表示:如果在 1 秒内,至少有 3 个请求被处理,并且其中 30% 的请求响应时间超过了 1 毫秒,那么这个资源将会熔断 5 秒。

image-20240826015704180

快速刷新请求会触发熔断策略

image-20240826015855696

给所有的服务配置Sentinel Feign 的支持和Sentinel的配置

image-20240826020040579

18.8自定义受保护资源

主要步骤:

基于代码 :try (Entry entry = SphU.entry("seckillSkus")) {}catch (BlockException e) { log.error("资源被限流{}", e.getMessage()); }

image-20240826022512679

配置seckillSkus流控规则,每s最多一个请求

image-20240826022953944

官方文档

image-20240826023543397

基于注解:@SentinelResource(value = "getCurrentSeckillSkusResource", blockHandler = "blockHandler")

image-20240826022845733

配置getCurrentSeckillSkusResource流控规则,每s最多一个请求

image-20240826022928256

官方文档

image-20240826023627295

18.9网关流控

主要步骤:

官网地址:https://github.com/alibaba/Sentinel/wiki/网关限流

image-20240826030201039

导入spring-cloud-alibaba-sentinel-gateway

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

image-20240826024704694

配置网关流控

image-20240826024949798

快速访问http://seckill.gulimall.com/getCurrentSeckillSkus,发现

image-20240826025239181

配置gulimall-seckill_route请求Header必须带上hello,并且值必须是world

image-20240826025526063

浏览器请求http://seckill.gulimall.com/getCurrentSeckillSkus不会被拦截

使用postman带上Headerhello=world请求会被限流

image-20240826025737009

18.10定制网关流控返回

主要步骤:

gulimall-gateway网关服务重写GatewayCallbackManager.setBlockHandler

image-20240826031158140

配置gulimall-seckill_route流控规则

image-20240826031107103

测试

image-20240826031134387

19.Sleuth-链路追踪

19.1基本概念&整合

sleuth文档: https://spring.io/projects/spring-cloud-sleuth
started: https://docs.spring.io/spring-cloud-sleuth/docs/3.1.0/reference/html/getting-started.html#getting-started

为什么用

微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务 单元。由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位。主要 体现在,一个请求可能需要调用很多个服务,而内部服务的调用复杂性,决定了问题难以 定位。所以微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与, 参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出了问题,很快定位。

链路追踪组件有 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 时间戳便可以得到整个请求所消耗的时间。

官方文档:https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started

如果服务调用顺序如下

image-20240826234832207

那么用以上概念完整的表示出来如下:

image-20240826234734915

整合Sleuth

所有的服务提供者与消费者导入依赖

<!-- sleuth 链路追踪-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

打开 debug 日志

logging:
  level:
    com.peng: debug
    org.springframework.cloud.openfeign: debug
    org.springframework.cloud.sleuth: debug

image-20240826235302260

19.2整合Zipkin效果

docker安装zipkin

安装命令

docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin

image-20240827000558089

整合zipkin

导入依赖

我用的spring-cloud版本没有Zipkin

image-20240827015454147

所以要自己指定版本

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>

image-20240827015740716

zipkin中包含sleuth,所以只需要配置zipkin即可

image-20240827015700087

配置zipkinsleuth

image-20240827015621410

访问:http://192.168.188.180:9411

image-20240827015913386

zipkin数据持久化

持久化类型选择elasticsearch,因为elasticsearch查询速度更快

docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.188.180:9200 openzipkin/zipkin-dependencies

19.3Zipkin界面分析

查询界面

image-20240827020623074

提交订单

image-20240827021148266

订单完成,向rabbitmq发送消息

image-20240827021235036

订单确认页,显示异步编排调用接口

image-20240827021713237

查看所有服务依赖

image-20240827022036462

查看gulimall-gateway服务依赖

image-20240827022056100

20.分布式高级篇总结

01-EleasticSearch、商品上架、首页、nginx动静分离、Apache JMeter使用、缓存、Redisson、SpringCache
02-检索服务、CompletableFuture异步编排、商品详情
03-认证服务、SpringSession、单点登录、购物车
04-RabbitMQ、订单确认
05-Seata、RabbitMQ延时队列、支付宝支付、订单服务
06-秒杀服务、Sentinel、Sleuth、Zipkin

image-20240827022904684

创作不易,感谢支持。

wxzf
posted @ 2024-10-14 00:59  peng_boke  阅读(38)  评论(0编辑  收藏  举报