服务保护、分布式事务

简介: 本课程讲解微服务保护核心知识,涵盖雪崩问题、熔断降级、限流隔离等方案,学习Sentinel实现熔断限流,掌握FallbackFactory降级处理及Seata分布式事务原理与应用。

学习目标

  1. 能够说出什么是微服务雪崩
  2. 能够说出常用的微服务保护方案和技术方案
  3. 能够说出什么是熔断降级
  4. 能够基于FallbackFactory编写降级方法
  5. 能够使用Sentinel配置熔断策略并测试通过
  6. 能够使用Sentinel配置限流策略并测试通过
  7. 能够基于Sentinel注解编写降级方法
  8. 能够使用Sentinel配置线程隔离并测试通过
  9. 能够说出CAP原理
  10. 能够使用Seata实现分布式事务控制
  11. 能够说出Seata AT模式的工作原理

1 微服务保护

1.1.微服务保护方案

1.1.1 微服务雪崩问题

上次课我们学习了微服务之间的远程调用,微服务通过远程调用进行协作完成业务流程,试想如果出现下边的现象会导致什么问题:

假如商品服务业务并发较高,占用过多Tomcat连接。可能会导致商品服务的所有接口响应时间增加,延迟变高,甚至是长时间阻塞直至查询失败。

此时查询购物车业务需要等待商品查询结果,从而导致购物车业务的响应时间也变长,甚至也阻塞直至无法访问。而此时如果查询购物车的请求较多,可能导致购物车服务的Tomcat连接占用较多,所有接口的响应时间都会增加,整个服务性能很差, 甚至不可用。

依次类推,整个微服务群中与购物车服务、商品服务等有调用关系的服务可能都会出现问题,最终导致整个集群不可用。

这就是级联失败问题,或者叫雪崩问题。【因为一个底层服务不可用,最终导致整个服务集群不可用】

保证服务运行的健壮性,避免级联失败导致的雪崩问题,就属于微服务保护。这章我们就一起来学习一下微服务保护的常见方案以及对应的技术。

1.1.2 微服务保护方案

1.1.2.1 方案介绍

AI:Spring cloud微服务保护的方案

Spring Cloud微服务架构中的服务保护是非常重要的,它能够确保系统的稳定性和可用性,特别是在面对突发流量或者服务异常的情况下。常用的微服务保护方案包括但不限于以下几个方面:

  1. 熔断 (Circuit Breaker) 熔断机制用于在服务出现问题时快速失败,避免调用链路中的服务相互等待,导致整体系统响应变慢甚至不可用。

如何快速失败(fast fail)呢?当服务的错误率达到一定程度时,断路器(相当于保险丝)会打开,直接返回错误而不是尝试调用服务。一段时间后,断路器会处于半开状态尝试调用服务,如果服务恢复正常,则关闭断路器。

【知识拓展】

AI:fast fail和safe fail区别

答:

Fast Fail(快速失败):旨在快速暴露问题,防止错误扩散或导致更严重的后果,如医疗、金融场景。缺点是:导致系统中断,影响用户体验【直接抛异常】

Safe Fail(安全失败):旨在最大程度保证系统可用和安全性,如在线服务、云计算平台。缺点是:可能导致问题被掩盖,增加修复难度。【try-catch,返回一个默认值(即降级)】

断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。

  1. 降级 (Degradation) 断路器会统计访问某个服务的请求数量统计服务提供方的异常比例,当比例过高表明该接口会影响到其它服务,应该拒绝调用该接口,而是直接走降级逻辑

降级逻辑 即提供一个简化的响应或者默认的响应来代替正常的服务调用。这样可以保证核心业务不受影响,非核心业务暂时被限制或关闭。

熔断后,接口还通吗?

不通,直接异常

降级后,接口还通吗?

通,但返回的是降级逻辑,即类似一个默认值,故业务逻辑不一定闭环,后续还需要人工补偿

  1. 超时 (Timeout) 设置合理的超时时间可以避免长时间等待响应导致的问题。当请求超时时,可以选择快速失败并返回错误信息,或者重试等策略。

常见的远程调用框架,都设置了超时机制。

AI:目前Http、Dubbo、WebService都有超时机制吗?

答:是的,HTTPDubboWebService 都支持超时机制,但它们的实现方式和配置方法有所不同

HTTP连接超时、读取超时

Dubbo:服务调用超时(默认3s),超时后自动重试2次

WebService连接超时、读取超时

  1. 线程隔离 (Thread Isolation) 线程隔离是指为每个服务分配独立的线程池,这样即使某个服务出现问题也不会影响到其他服务。

线程隔离的思想来自轮船的舱壁模式:

轮船的船舱会被隔板分割为N个相互隔离的密闭舱,假如轮船触礁进水,只有损坏的部分密闭舱会进水,而其他舱由于相互隔离,并不会进水。这样就把进水控制在部分船体,避免了整个船舱进水而沉没。

为了避免某个接口故障或压力过大导致整个服务不可用,我们可以限定每个接口可以使用的资源范围,也就是将其“隔离”起来。

如图所示,我们给查询购物车业务限定可用线程数量上限为20,这样即便查询购物车的请求因为查询商品服务而出现故障,也不会导致服务器的线程资源被耗尽,不会影响到其它接口。

  1. 限流 (Rate Limiting) 限流是最常见的服务保护措施之一,其目的是为了防止服务因为过大的流量而崩溃。

对于某些关键资源或者参数的访问,可以采取特殊的限流措施来防止这些热点成为瓶颈。

限流往往会有一个限流器,数量高低起伏的并发请求曲线,经过限流器就变的非常平稳。这就像是水电站的大坝,起到蓄水的作用,可以通过开关控制水流出的大小,让下游水流始终维持在一个平稳的量。

可以通过以下几种方式进行限流(有兴趣的可以看看下面两种实现方案,前期可以仅做了解):

  • 基于令牌桶算法:允许一定数量的请求通过,超出则拒绝或排队等待。
  • 基于滑动窗口:在一段时间内对请求进行计数,超过阈值则触发限流。

1.1.2.2 实现工具

在Spring Cloud生态系统中,实现服务保护通常使用的工具包括:

Hystrix: 提供了熔断、限流、超时等功能,是SpringCloud原生组件。

Resilience4j: 是一个轻量级的库,提供了与Hystrix类似的功能,但设计更为现代和简洁。

Sentinel: 阿里巴巴开源的一款流量控制组件,特别适合微服务架构下的流量管理,提供了限流、熔断、降级等多种服务保护功能,并且支持热更新规则。

本课程讲解Sentinel。

1.2 熔断降级

1.2.1. 方案介绍

熔断降级是解决服务集群雪崩问题的重要手段,包括熔断和降级两个方案。

熔断是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。熔断发生在服务调用方即客户端

这么多报错(慢请求)是吧?行、都别玩了

降级是当遇到访问失败可以快速返回一些默认数据或者友好提示,用户体验会更好。熔断降级结合后是当线路断开后直接走降级线路避免再次去请求失败线路。降级方法需要在服务调用方即客户端实现。

这么多报错(慢请求)是吧?大哥你这样我就要挂了,小弟帮我顶顶(还有部分可以玩)

断路器控制熔断和放行的流程如下:

断路器包括三个状态:

  • closed:关闭状态【默认】,断路器放行所有请求,并开始统计异常比例、慢请求比例、异常数。超过阈值则切换到open状态
  • open:打开状态,服务调用被熔断,访问被熔断服务的所有请求会被拒绝,快速失败,直接走降级逻辑。Open状态5秒后会进入half-open状态
  • half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。
  • 请求成功:则切换到closed状态
  • 请求失败:则切换到open状态

实现熔断降级做两件事:

  • 编写服务降级逻辑:就是服务调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或默认数据。
  • 异常统计和熔断:统计服务提供方的异常比例,当比例过高表明该接口会影响到其它服务,应该拒绝调用该接口,而是直接走降级逻辑。这里我们用Sentinel完成。

1.2.2. Sentinel安装与集成

1.2.2.1 切换分支

将hmall-micro代码环境切换到dev_02分支。

注意:切换分支前要提交原当前分支的代码。

每位学生在dev_02分支练习完成后提交代码并切换回dev_01分支继续未完成的任务

工作中也经常这样来回切换分支,因为不同需求在不同分支里,我们经常都是并行开发

大家入职后,也可能同时负责3-4个项目,所以尽早习惯【多线程并行的开发模式】

1.2.2.2 安装Sentinel

实现服务保护的工具有很多,Spring Cloud Alibaba技术栈中Sentinel是实现服务保护的中间件。

Sentinel是阿里巴巴开源的一款服务保护框架,目前已经加入Spring Cloud Alibaba中。官方网站:

https://sentinelguard.io/zh-cn/

https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

Sentinel 的使用可以分为两个部分:

  • 核心库(Jar包):不依赖任何框架/库,能够运行于Java8及以上的版本的运行时环境,同时对 Dubbo/Spring Cloud 等框架也有较好的支持。在项目中引入依赖即可实现服务限流、隔离、熔断等功能。
  • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。

为了方便监控微服务,我们先把Sentinel的控制台搭建出来。

课前提供的虚拟机已经安装了sentinel,如下图:

使用课前提供的虚拟机需要设置sentinel容器的时区,如下:

先启动sentinel

docker start sentinel-dashboard

登录sentinel容器并设置时区

  • 进入容器:docker exec -it sentinel-dashboard /bin/bash
  • 执行命令:ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezon

设置完成效果如下 :

如果未使用课前提供的虚拟机,需要参考下边的内容安装sentinel:

1)下载jar包

下载地址:https://github.com/alibaba/Sentinel/releases

也可以直接使用课前资料提供的版本:

2)运行

将jar包拷贝到 虚拟机/data/soft/sentinel目录下重命名为sentinel-dashboard.jar

创建Dockerfile文件

FROM openjdk:11-jdk
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezo
ARG SENTINEL_VERSION=1.8.6
# copy sentinel jar
ADD ./sentinel-dashboard.jar /home/sentinel-dashboard.jar
RUN chmod -R +x /home/sentinel-dashboard.jar
ENTRYPOINT ["sh","-c","java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar $JAVA_OPTS /home/sentinel-dashboard.jar"]

执行命令创建镜像:

docker build -t sentinel-dashboard .

创建并启动容器:

docker run  --name sentinel-dashboard -d -p 9090:8090 sentinel-dashboard:latest

其它启动时可配置参数可参考官方文档:官网文档链接

3)访问

访问:http://192.168.101.68:9090/ 页面,就可以看到sentinel的控制台了:

需要输入账号和密码,默认都是:sentinel

登录后,即可看到控制台,默认会监控sentinel-dashboard服务本身:

本地运行sentinel

如果在测试时发现虚拟中的sentinel不能用,可以本地运行sentinel。

将sentinel的jar包放在任意非中文、不包含特殊字符的目录下,重命名为sentinel-dashboard.jar

然后运行如下命令启动控制台:

java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

访问:http://localhost:8090/ 页面

1.2.2.3 项目集成Sentinel

在虚拟机启动sentinel【上面已经执行过,这里是再次提醒、确认一下】

docker start sentinel-dashboard

接下来,我们在项目中集成 sentinel,我们在哪个项目中集成 sentinel?

sentinel要完成熔断降级,熔断是在服务调用方,所以针对购物车服务请求商品服务实现熔断就需要在购物车服务集成 sentienl。

这里可能部分同学有疑问,问什么不是服务提供方呢?所以我们顺便推导一下,假设是提供方熔断:

(1)提供方是熔断了,但是上游调用方还是有大量请求,压力依然存在,只是加快了下游的响应速度,前提是牺牲了原有的业务逻辑实现,并不能保障整体微服务的可靠性

(2)调用方熔断,就是我根本不调用你下游(你此刻慢、报错多那我就先不调用你),而是返回一个默认逻辑,这个默认逻辑实现应该由接口提供方实现

我们在cart-service模块中整合sentinel,连接sentinel-dashboard控制台,步骤如下: 1)引入sentinel依赖

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

2)配置控制台

修改application.yaml文件,添加下面内容:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.101.68:9090
        client-ip: 192.168.101.1
      http-method-specify: true # 开启请求方式前缀可根据http请求方法区分簇点链路

如果是在本机运行的sentinel要配置:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8090
      http-method-specify: true # 开启请求方式前缀可根据http请求方法区分簇点链路

3)访问cart-service的任意端点

重启cart-service、item-service,然后访问查询购物车接口:swagger链接

sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard控制台。并展示出统计信息:

点击簇点链路菜单,会看到下面的页面:

所谓簇点链路,就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源。默认情况下,Sentinel会监控SpringMVC的每一个Endpoint(接口)。

因此,我们看到/carts这个接口路径就是其中一个簇点,我们可以对其进行限流、熔断、降级、隔离等保护措施,稍后会详细讲解。

1.2.3. 实现降级

1.2.3.1 开启sentinel

下边我们先编写降级逻辑,再实现服务熔断。

AI(Cursor)提示詞

帮我在已有工程里,对于itemclient实现降级策略,技术使用Sentinel,注意实现方案是implements FallbackFactory,降级类写在api的工程里,并最终在cart-service调用item-service时使用

首先配置Feign使用Sentinel:

在购物车服务application.yml中配置如下(默认已写好):

feign:
  sentinel:
    enabled: true # 开启feign对sentinel的支持

1.2.3.2 实现FallbackFactory接口

接下来给FeignClient编写失败后的降级逻辑有两种方式:

  • 方式一:FallbackClass,无法捕获到远程调用的异常

定义一个降级类实现FeignClient接口,并在@FeignClient注解中配置fallback 属性,如下:

@FeignClient(name="item-service",path = "/items",fallback = 降级类名.class)
  • 方式二:FallbackFactory,可以捕获远程调用的异常,我们一般选择这种方式

定义一个降级类实现FallbackFactory接口,并在@FeignClient注解中配置fallbackFactory属性

@FeignClient(name="item-service",path = "/items",fallbackFactory= 降级类名.class)

这里我们演示方式二的失败降级处理。

步骤一:在hm-api模块中给ItemClient定义降级处理类,实现FallbackFactory接口:

代码如下:

package com.hmall.api.item;
import com.hmall.api.item.dto.ItemDTO;
import com.hmall.common.utils.CollUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
@Slf4j
@Component
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
    @Override
    public ItemClient create(Throwable cause) {
        return new ItemClient() {
            @Override
            public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
                log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);
                cause.printStackTrace();
                // 查询购物车允许失败,查询失败,返回空集合
                return CollUtils.emptyList();
            }
            @Override
            public void deductStock(List<OrderDetailDTO> items) {
                log.error("远程调用ItemClient#deductStock扣减库存失败,参数:{}",items,cause);
            }
        };
    }
}

1.2.3.3 配置fallbackFactory

步骤二:在hm-api模块中的ItemClient接口中使用ItemClientFallbackFactory

package com.hmall.api.item;
import com.hmall.api.item.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
/**
 * @author Mr.M
 * @version 1.0
 * @description 商品服务Feign接口
 * @date 2024/8/3 16:21
 */
@FeignClient(name="item-service",path = "/items",fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {
    @GetMapping
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
    @PutMapping("/stock/deduct")
    public void deductStock(@RequestBody List<OrderDetailDTO> items);
}

1.2.3.4 注入降级Bean

步骤三:在cart-service启动类添加扫描包配置,扫描降级类并将bean注册到spring容器

1.2.3.5 降级测试

最后测试降级效果。

重启cart-service,并将item-service停止来模拟item-service服务不可用

请求查询购物车接口,cart-service远程调用item-service,由于item-service不可用,Feign执行ItemClientFallbackFactory 中定义的降级逻辑

在ItemClientFallbackFactory 中打断点,在ItemClientFallbackFactory 中捕获到了远程调用的异常,降级方法返回空List<ItemDTO>。

接口测试最终效果,购物车可以正常显示,但由于获取的商品信息为空这里newPrice显示为null。

启动 ItemServiceApplication,再次请求购物车接口,验证发现此时返回了 newPrice数据

相关文章
|
2月前
|
人工智能 机器人 Java
黑马最新项目
AIGC项目涵盖大模型私有化部署、聊天机器人、RAG知识库及代码提示工具;天机AI集成SpringAI与多模型工作流;云岚到家聚焦微服务与分布式架构;四方保险构建统一支付与时序数据应用;星辰WMS与Dify项目即将发布。
109 0
黑马最新项目
|
2月前
|
canal 缓存 关系型数据库
微服务原理篇(Canal-Redis)
本文介绍了ES索引同步的常见方案,重点讲解Canal+MQ数据同步机制。通过解析MySQL的binlog日志,Canal模拟slave伪装接入主库,实现增量数据捕获,并结合RabbitMQ保证消息顺序性地同步至Elasticsearch。同时探讨了缓存一致性问题,提出使用分布式锁(如Redis)控制并发写操作,避免双写不一致。还涵盖Redis持久化、集群模式、过期淘汰策略及缓存三剑客(穿透、雪崩、击穿)的解决方案,系统梳理了高并发场景下的数据同步与缓存保障技术体系。
88 0
 微服务原理篇(Canal-Redis)
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
大模型专业名词解释手册
本手册由油炸小波设计提示词、Manus创作,系统梳理大语言模型核心概念,涵盖基础原理、训练技术、优化压缩、推理应用、评估调试及伦理安全六大模块,深入浅出解析LLM关键技术术语。
292 0
|
2月前
|
Arthas 存储 运维
记Arthas实现一次CPU排查与代码热更新
本文介绍使用Arthas排查Java应用CPU占用过高问题的完整流程,涵盖线程分析、阻塞定位、watch命令追踪异常、jad反编译实现热更新及火焰图分析,实现无需重启应用的高效故障排查与代码修复。
91 0
|
2月前
|
安全 Java 开发工具
1.工程搭建与验证
本文介绍如何搭建SpringBoot工程并整合SpringSecurity。通过阿里云脚手架快速创建项目,导入IDEA后引入web和security依赖,验证安全配置自动生效,最终实现登录认证功能,并提供完整代码仓库地址。
 1.工程搭建与验证
|
2月前
|
存储 搜索推荐 数据库
ES分布式搜索引擎入门
本课程介绍Elasticsearch的核心概念与应用,涵盖倒排索引原理、IK分词器使用、Java Client操作索引及查询等技能,帮助学员掌握高效搜索功能开发,适用于电商等高并发场景。
110 0
ES分布式搜索引擎入门
|
2月前
|
消息中间件 Java 数据安全/隐私保护
异步消息组件MQ基础
本课程学习RabbitMQ在微服务中的应用,涵盖MQ的应用场景、异步与同步调用区别、RabbitMQ的安装与配置、消息收发入门、工作队列模型及交换机类型(Fanout、Direct、Topic)等核心知识,并结合SpringAMQP实现消息通信与项目实战。
52 0
异步消息组件MQ基础
|
2月前
|
负载均衡 Java 应用服务中间件
微服务网关与配置中心 学习目标
本课程学习Spring Cloud Gateway网关的实现,掌握路由配置、负载均衡、内置过滤器(如StripPrefix)及全局过滤器的使用。重点包括自定义身份校验过滤器、JWT鉴权流程、微服务间用户信息传递,并结合Nacos实现服务发现与统一配置管理,完成前后端联调与权限控制。
67 0
微服务网关与配置中心 学习目标
|
2月前
|
存储 NoSQL 关系型数据库
4-MongoDB索引知识
MongoDB索引基于B-Tree结构,可高效支持查询,避免全表扫描。主要类型包括单字段、复合、地理空间、文本及哈希索引,适用于不同查询场景,提升数据检索性能。
|
2月前
|
存储 缓存 Java
自定义注解
本文介绍如何在Spring框架中实现自定义注解,结合AOP与过滤器完成日志、权限控制等功能。通过@Target、@Retention等元注解定义注解,利用AOP拦截方法执行,或通过过滤器实现登录验证。示例涵盖用户服务、控制器及注解实际应用,展示其在Web请求中的完整流程与扩展用途。