微服务的服务保护
服务的保护
微服务保护
雪崩问题
如果加入商品服务业务并发较高,占用过多Tomcat连接。就可能会导致商品服务的所有接口响应时间增加,延迟变高,升值是长时间阻塞直至查询失败。此时查询购物车业务需要等待商品查询结果,从而倒是购物车业务的响应时间也变长,甚至也阻塞直至无法访问。这样就导致整个服务走不通,导致雪崩。
或者说A模块依赖B模块,然后B模块现在宕机了,报错了,然后所有的请求都会走到A,不会再继续往下走下去,这样就会导致请求拥挤,就会导致线程阻塞,引发级联失败问题。(如下图)
现在比如有完整的一个项目,A、B、C、D、E共五个模块,走向是A->B->C->D->E。然后此时B模块里面出问题了,所以现在所有的请求都在A模块,走不到B,更走不到后面的CDE模块,此时就导致整个项目崩溃。像这种一个问题导致整个项目崩塌的问题就叫做雪崩。(如下图)
springCloud微服务保护方案
-
熔断(Circuit Breaker)
-
熔断机制用于再服务出现问题时快速失败,避免调用链路中的服务相互等待,导致整体系统响应变慢甚至不可用。
-
当服务的错误率达到一定程度时,断路器(相当于保险丝)会打开,直接返回错误,儿不是尝试调用服务。一段时间后,断路器会处于半开状态尝试盗用服务,如果服务恢复正常,则关闭断路器。
-
由断路器统计业务执行的异常比例,比如超出阈值则会熔断该业务,拦截访问该业务的一切请求。
-
-
降级(Degradation)
- 断路器会统计访问某个服务的请求数量,统计服务提供方的异常比例,当比例过高表明该接口会影响到其他服务,应该拒绝调用该接口,而是直接走降级逻辑。
- 降级逻辑 即提供一个简化的响应或者默认的响应来代替正常的服务调用。这样可以保证核心业务不受影响,非核心业务暂时被限制或关闭。
-
超时(Timeout)
- 设置合理的超时时间可以避免长时间等待响应导致的问题 。当请求超时,可以选择快速失败并返回错误信息,或者重拾等策略。
-
线程隔离(Thread Isolation)
- 线程隔离是指为每个服务分配独立的线程池,这样即使某个服务出现问题也不会影响到其他服务。
- 线程隔离的思想来自轮船的舱壁模式:
(请把此处的帆船自动脑补为轮船)
轮船的船舱会被隔板分割为n个相互隔离的密闭仓,加入一个地方漏水了,只有哪一个密闭仓进水,其余的密闭仓都是好的,不会进水。
- 限流(Rate Limiting)
限流时最常见的服务保护措施之一,其目的时为了防止服务因为过大的流量而奔溃。
对于某些关键资源或者参数的访问,可以采取特殊的限流措施来防止这些热点成为瓶颈。
限流往往会有一个限流器,数值高低起伏的并发请求曲线,金国限流器就变得非常平稳。这就像是水电站的大坝,起到蓄水的作用,可以通过开关控制水流的大小,让下游水流始终维持在一个平稳的量。
可以通过已下几种方式进行限流:
基于令牌桶算法:允许一定数量的请求通过,超出则拒绝或排队等待。
基于滑动窗口:再一段时间内对请求进行技术,超过阈值则触发限流。
实现服务保护的工具
- Hystrix:提供了熔断、限流、超时等功能。
- Resilience4j:是一个轻量级的库,提供了与Hystrix类似的功能,但设计更为现代和简洁。
- Sentinel:阿里巴巴开源的一款流量控制组件,特别适合微服务框架下的流量管理,提供了限流、熔断、降级等多种服务保护功能,并且支持热跟新规则。
熔断降级
熔断降级时解决雪崩问题的重要手段,包括熔断和降级两个方案。
熔断是由断路器统计hi服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。熔断发生在服务调用放即客户端。
降级是当遇到访问失败可以快速返回一些默认数据或者友好提示,用户体验会更好。熔断降级结合后是当线路断开后直接走降级线路避免再次去请求失败线路。降级方法需要在服务调用方即客户端实现。
断路器控制熔断和放行的流程如下:
断路器包括三个状态:
- closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例、异常数。超过阈值则切换到open状态
- open:打开状态,服务调用被熔断,访问被熔断服务地请求会被拒绝,快速失败,直接走降级逻辑。open状态5秒后会进入half-open状态
- half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。
- 请求成功:则切换到closed状态
- 请求失败:则切换到open状态
实现熔断降级做两件事:
- 编写服务降级逻辑:就是服务调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或默认数据。
- 异常统计和熔断:统计服务提供方的一场比例,当比例过高表明该接口会影响到其他服务,应该拒绝调用该接口,而是直接走降级逻辑。
访问http://192.168.101.68:9090/页面,就可以看到sentinel控制台
项目集成Sentinel
sentinel要完成熔断降级,熔断实在服务调用放即客户端,所以针对购物车服务请求商品服务实现熔断就需要再购物车服务集成sentienl。
1、引入sentinel依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、在ymal配置文件中添加
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true # 开启请求方式前缀可根据http请求方法区分簇点链路
3、重启项目、访问cart-service的任意断点后
访问192.168.101.68:9090
点击簇点链路菜单,会看到下面页面
所谓簇点链路,就是单机调试链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源。默认情况下,Sentinel会监控SpringMVC的每一个Endpoint(接口)
因此,我们看到/carts这个接口路劲就是其中一个簇点,我们可以对其进行限流、熔断、降级、隔离等保护措施。
实现降级
首先配置Feign使用Sentinel:
在购物车服务application.yml中配置如下:
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
实现FallbackFactory接口
接下来给FeignClient编写失败后的降级逻辑有两种方式:
- 方式一:FallbackClass,无法捕获到远程调用的异常
定义一个降级类实现FeignClient接口,并在@FeignClient注解中配置fallback属性
@FeignClient(name="item-service",path="/items",fallback=降级类名)
- 方式二:FallbackFactory,可以捕获到远程调用的异常,我们一般选择这种方式。定义一个降级类实现FallbackFactory接口,并在@FeignC列宁他注解中配置
@FeignClient(name="item-service",path="/items",fallbackFactory=降级类名.class)
步骤一:在hm-api模块中给ItemClient定义降级处理类,实现FallbackFactory
代码如下:(注意,写完后注意要在类上加一个@Component注解,把它加载到ioc容器中)
package com.hmall.api.item;
import com.hmall.api.item.dto.ItemDTO;
import com.hmall.api.item.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
@Component
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
@Override
public ItemClient create(Throwable cause) {
return new ItemClient() {
//降级方法
@Override
public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
System.out.println("我是根据id查询的接口");
return List.of();
}
@Override
public void deductStock(List<OrderDetailDTO> items) {
System.out.println("我是扣减库存接口");
}
};
}
}
配置fallbackFactory
步骤二:在hm-api模块中的itemClient接口中使用ItemClientFallbackFactory:
package com.hmall.api.item;
import com.hmall.api.item.dto.ItemDTO;
import com.hmall.api.item.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
@FeignClient(name = "item-service",path = "/items",fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {
@GetMapping
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
@PutMapping("/stock/deduct")
void deductStock(@RequestBody List<OrderDetailDTO> items);
}
注入降级Bean
步骤三:在cart-service启动类添加扫描包配置,扫描降级类并将bean注册到spring容器
package com.hmall.cart;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication(scanBasePackages = {"com.hmall.cart","com.hmall.api"})
@EnableFeignClients("com.hmall.api")
@MapperScan(basePackages = {"com.hmall.cart.mapper"})
public class CartApp {
public static void main(String[] args) {
SpringApplication.run(CartApp.class, args);
}
}
降级测试
重启cart-service,并将item-service定制来模拟item-service服务不可用
如果方法走到这里,说明就走了降级逻辑了
接口测试最终效果,购物车可以正常显示,但是由于获取的商品信息为空,这里newPrice显示为null
feign远程调用怎么实现降级?
- 我们使用的是OpenFeign实现微服务之间的远程调用,使用Sentinel实现熔断降级。
- 首先在服务提供方配置sentinel,引入sentinel的依赖,配置sentinel的地址
- 在服务调用方开启feign使用sentinel
- 在服务调用方白那些feign接口并编写降级逻辑,具体方法是编写降级类实现FallbackFactory接口,并在FeignClient注解中配置fallbackFactory
- 服务调用方当无法正常调用服务提供方接口时会走降级逻辑,并捕获到异常。
服务熔断
根据熔断方案,sentinel会统计异常比例、慢请求比例、异常数等数据,达到阈值时断路器打开即发生熔断,熔断状态下会走降级路线保证快速响应。下边在sentinel中配置熔断策略,找到查询商品信息的簇点链路
点击“熔断”,配置异常数,如下图:
2秒内最小请求数为2,异常数达到1发生熔断,熔断时长为20秒
测是异常数
继续停止item-service商品服务
启动cart-service购物车服务,请求查询购物车接口。
在测试之前,先把降级关了,注释fallbackFactory,否则测试看不出效果
运行程序
测试报错信息
连续测试多条
出现这个DegradeException异常,就说明熔断成功。
注意:加入熔断之后不要再重新启动项目,否则熔断点会失效,得重新添加
测试慢调用比例
测试之前需删除之前测试的熔断策略
添加新的熔断策略
注意:不要重启项目,只需清空控制台
需要在item-service的查询商品信息接口,添加线程休眠模拟处理时间,这样可以制造慢请求的效果。
重启项目,访问接口,注意:item接口也要起来,出现DegradeException这个异常,就算成功
测试异常比例
需要在item接口中制造一个异常
然后添加熔断规则,测试异常
看上游服务控制台,如果出现DegradeException这个异常,说明测试成功
请求限流
找到商品查询的簇点链路,点击“流控”
在弹出的菜单中填写QPS单机阈值为5,表示每秒最多处理5个请求
什么时QPS
QPS是“Queries Per Second”的缩写,中文通常称为“每秒查询率”或“每秒请求数”。
它是一个衡量系统性能的重要指标,特别是在评估 Web 服务器、数据库系统、API 接口等的负载能力时经常使用。
QPS 表示系统在一秒钟内能够处理的查询或请求的数量。这个指标可以帮助开发者和运维人员了解系统的处理能力和性能瓶颈。例如,如果一个 Web 服务的 QPS 为 100,则意味着该服务在一秒钟内最多可以处理 100 个请求。
我们用jmeter压力测试工具进行压力测试。
JMeter 是一个广泛使用的开源性能测试工具,主要用于测试 Web 应用程序的负载和性能。它是由 Apache Software Foundation 开发和维护的,完全用 Java 编写,因此可以在任何支持 Java 的平台上运行。
发送到桌面快捷方式
把雪崩测试脚本拖入打开的jmeter中
复制进去后点击限流测试
设置模拟线程数等参数
表示总共发1000个请求,用100秒时间发完,只执行一次
在“HTTP请求”界面设置压力测试请求的地址
设置好后点击启动
可以查看这两个
用jmeter测试,通过sentinel进行实时监控,通过QPS为6,拒绝QPS为4,符合我们的预期结果
线程隔离
方案介绍
线程隔离功能,无论是Hystix还是Sentinel都支持线程隔离。不过其实现方式不同。
线程隔离的有两种方式实现:
- 线程池隔离:给每个服务调用业务分配一个线程池,利用线程池本省实现隔离效果
- 信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求
Sentinel的线程隔离就是基于信号量隔离实现的,而Hystix两种都支持,但默认时基于线程池隔离。
点击查询商品的FeignClient对应的簇点资源后面的流控按钮:
在弹出的表单中填写下面内容:
注意,这里勾选的是并发线程数限制,也就是说这个查询功能最多使用5个线程,而不是5QPS。如果查询商品的接口每秒处理2个请求,则5个线程的实际QPS在10左右,而超出的请求自然会被拒绝。
cart-service调用商品查询控制在5个线程内,通过线程隔离即使商品服务出现问题也不会影响cart-service服务。
下边修改商品查询接口的代码,添加休眠500毫秒的代码,模拟一次请求需要500毫秒,一秒则可处理2次请求,5个线程可接收10 QPS左右。
将/carts的限流规则调大以免影响商品查询接口的限流控制。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!