熔断
熔断含义:
在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整 体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
目的:
防止服务雪崩
如何实现的熔断:
熔断存在的基础服务之间的调用使用的组件是openfeign
调用的时候下游的服务如果响应慢或者服务宕机 熔断:
@FeignClient(fallback=””)
直接走的是fallback中的代码 不再去访问下游 牺牲局部保整体
Openfeign支持熔断,记得开启 在 qpplication.preperties
Sentinel的概念和功能
基本概念
资源
资源就是Sentinel要保护的东西
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,可以是一个服务,也可以是
一个方法,甚至可以是一段代码。
| 我们入门案例中的message方法就可以认为是一个资源
规则
规则就是用来定义如何进行保护资源的
作用在资源之上, 定义以什么样的方式保护资源,主要包括流量控制规则、熔断降级规则以及系统
保护规则。
Sentinel的主要功能
Sentinel的主要功能就是容错,主要体现为下面这三个:
流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
熔断降级
当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。Sentinel 对这个问题采取了两种手段:
l 通过并发线程数进行限制
Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
l 通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
系统负载保护
Sentinel 同时提供系统维度的自适应保护能力。当系统负载较高的时候,如果还持续让请求进入可能会导致系统崩溃,无法响应。在集群环境下,会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
总之一句话: 我们需要做的事情,就是在Sentinel的资源上配置各种各样的规则,来实现各种容错的功能。
使用 Sentinel
Sentinel 是一个 容错组件
地址: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
1 下载jar包,解压到文件夹
https://github.com/alibaba/Sentinel/releases/tag/1.8.1
2 启动控制台
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
使用 8080 启动 java -jar sentinel-dashboard-1.8.1.jar
使用 8080 启动 ( 默认用户名密码是 sentinel/sentinel )
使用 8888 启动 java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
使用 8888 启动
3 修改 product ,在里面加入有关控制台的配置
# 该端口号为sentinal于服务之间的交互 随便写只要不被占用
spring.cloud.sentinel.transport.port=9999
#sentinal服务所在的地址和端口号
spring.cloud.sentinel.transport.dashboard=localhost:8080#端口号也可以写 8888
然后访问 微服务项目 接着访问 sentinel客户端
进行流量控制 流控规则
资源名为:
资源名:唯一名称,默认是请求路径,可自定义
针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制
阈值类型/单机阈值:
l QPS(每秒请求数量): 当调用该接口的QPS达到阈值的时候,进行限流
l 线程数:当调用该接口的线程数达到阈值的时候,进行限流
更改访问路径
设置阈值类型为QPS,单机阈值为2。即每秒请求量大于2的时候开始限流
然后快速访问接口,观察效果。此时发现,当QPS > 2的时候,服务就不能正常响应,而是返回Blocked by Sentinel (flow limiting)结果。
自定义限流的一个异常响应
前后端分离的时候要 给前端一个响应 放到一个固定的 实现类 固定格式的响应
package com.aaa.exception;
import com.aaa.util.Result;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 注入到对应的容器里面 通用的注解
// 代表把当前的一个类 作为 Bean 注入到 Spring 容器 里边
@Component
public class MyExceptionHandler implements BlockExceptionHandler {
@Resource
private ObjectMapper objectMapper;
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
Result r = null;
if (e instanceof FlowException) {
r = new Result(100, "限流了", "");
}
//返回json数据
response.setStatus(500); // http 的状态码
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// new ObjectMapper().writeValue(response.getWriter(), r);
// 把 ObjectMapper() 当做 Bean 注入 以后使用可直接 @Resource 引入
// @Resource
// private ObjectMapper objectMapper;
objectMapper.writeValue(response.getWriter(), r);
// response.getWriter();
}
}
完全版本的异常响应固定的 实现类
@Component
public class MyExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
Result r = null;
if (e instanceof FlowException) {
r =new Result(100,"限流了","");
} else if (e instanceof DegradeException) {
r =r =new Result(101,"服务降级了");
} else if (e instanceof ParamFlowException) {
r =new Result(102,"热点参数限流了");
} else if (e instanceof SystemBlockException) {
r = new Result(103,"触发系统保护规则了");
} else if (e instanceof AuthorityException) {
r = new Result(104,"授权规则不通过");
}
//返回json数据
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getWriter(), r);
}
}
sentinel共有三种流控模式,分别是:
l 直接(默认):接口达到限流条件时,开启限流
l 关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]
l 链路:当从某个接口过来的资源达到限流条件时,开启限流
直接
就是默认的模式,上边写的方式
关联
链路流控模式
链路流控模式
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。
注解:@SentinelResource
我们可以对某一个方法进行限流控制,无论是谁在何处调用了它,这里需要使用到@SentinelResource
,一旦方法被标注,那么就会进行监控@SentinelResource
,一旦方法被标注,那么就会进行监控。
第1步: 编写一个service,在里面添加一个方法message
@Service
public class ProductService {
@SentinelResource("message")
public void message(){
System.out.println("hello====message!!!");
}
}
第2步: 在Controller中声明两个方法,分别调用service中的方法message
@RestController
public class MessageController {
@Resource
private ProductService service;
@GetMapping("message1")
public String message1(){
service.message();
return "message1";
}
@GetMapping("message2")
public String message2(){
service.message();
return "message2";
}
}
第3步: 禁止收敛URL的入口 context
在application.properties中添加:
spring.cloud.sentinel.web-context-unify=false
#用于控制是否收敛context。将其配置为 false 即可根据不同的URL 进行链路限流。
第4步: 控制台配置限流规则