1 基础概念
熔断:A 服务调用 B 服务的某个功能,由于网络不稳定问题,或者 B 服务卡机,导致功能时间超长。如果这样子的次数太多,就可以直接将 B 断路了(A 不再请求 B 接口),凡是调用 B 的直接返回降级数据,不必等待 B 的超长执行。 这样 B 的故障问题,就不会级联影响到 A。
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
根据依赖版本下载相应的
sentinel-dashboard-1.6.3.jarhttps://github.com/alibaba/Sentinel/releases/download/1.6.3/sentinel-dashboard-1.6.3.jarReleases · alibaba/Sentinel · GitHub
然后使用命令台启动(server.port指端口,自行填写):
java -jar sentinel-dashboard-1.6.3.jar --server.port=8333
然后输入http://localhost:8333/ 即可看到页面
初始账号密码都是sentinel
配置文件中加入:
spring.cloud.sentinel.transport.dashboard=localhost:8333
spring.cloud.sentinel.transport.port=8719
=======================================================================
限流
启动项目,随便访问一个API
新增流控规则(每秒请求数为1):
可以看到出现了流控规则:
限流成功了
为了得到更多信息,对外暴露endpoint
在pom中导入:
org.springframework.boot
spring-boot-starter-actuator
配置文件中加入:
management.endpoints.web.exposure.include=*
重启项目,疯狂刷新刚刚的接口,就可以在实时监控中看到:
接下来自定义流量控制显示的内容(显示内容自定):
@Configuration
public class SeckillSentinelConfig {
public SeckillSentinelConfig() {
WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
R error = R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(), BizCodeEnum.TO_MANY_REQUEST.getMessage());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JSON.toJSONString(error));
}
});
}
}
因为流控规则保存在内存中,重启项目会消失,所以需要重新添加流控规则。再次重新刷新页面,就会出现自定义的显示内容了:
流控模式说明:
关联:对资源A的限流,是关联资源B流量大时才会触发
链路:对资源A的限流,是只有经过入口资源B时才触发,直接访问资源A是不触发的
流控效果说明:
Warm Up:经过10秒阈值会升到100
排队等待:多出来的访问请求超过3秒才失效,之前是一直等待
=======================================================================
熔断降级
在需要用到sentinel的所有微服务里都加入sentinel依赖和actuator依赖,并配置:
spring.cloud.sentinel.transport.dashboard=localhost:8333
management.endpoints.web.exposure.include=*
feign.sentinel.enabled=true
比如我的product服务调用seckill服务的接口,
则在定义feign的时候可以指定fallback内容:
@FeignClient(value = "gulimall-seckill",fallback = SeckillFeignServiceFallBack.class)
public interface SeckillFeignService {
/**
* 根据skuId查询商品是否参加秒杀活动
* @param skuId
* @return
*/
@GetMapping(value = "/sku/seckill/{skuId}")
R getSkuSeckilInfo(@PathVariable("skuId") Long skuId);
}
指定了fallback为SeckillFeignServiceFallBack(返回内容自定)
@Slf4j
@Component
public class SeckillFeignServiceFallBack implements SeckillFeignService {
@Override
public R getSkuSeckilInfo(Long skuId) {
log.info("熔断");
return R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(),BizCodeEnum.TO_MANY_REQUEST.getMessage());
}
}
停止seckill服务,访问product服务,可以看到日志平台提示熔断。
对seckill服务点击降级按钮:
RT: 当进入的请求的响应时间超过了1000毫秒,则实施熔断,10秒后检查若还是这样就继续熔断,否则结束熔断。
当访问量超大时,需要对一些服务做出服务,可直接让远程服务本身指定降级策略,这样虽然远程服务依然在运行,但运行是默认返回降级策略。
自定义一个叫“seckillSkus”的资源:
@Override
public List getCurrentSeckillSkus() {
try (Entry entry = SphU.entry("seckillSkus")) {
//业务
} catch (BlockException e) {
log.error("资源被限流{}",e.getMessage());
}
return null;
}
接着就可以利用这个资源名进行流控或者熔断降级了。
以上是使用代码的形式,优点为较为灵活。
然后就是以注解的形式:
@SentinelResource(value = "getCurrentSeckillSkusResource",blockHandler = "blockHandler")
因为指定了blockHandler,所以还要写一个blockHandler出来
public List blockHandler(BlockException e) {
log.error("getCurrentSeckillSkusResource被限流了,{}",e.getMessage());
return null;
}
效果是一样的,自定了一个叫getCurrentSeckillSkusResource的资源
========================================================================
网关层配置sentinel
除了上述所需导入的依赖和配置,还需要在网关模块加入以下的依赖:
com.alibaba.cloud
spring-cloud-alibaba-sentinel-gateway
为了看到新效果,需要使用1.7以上的版本,所以重新下载一个1.7以上的sentinel-dashboard-1.7.1.jar。然后启动服务,可以看到网关和其它的不同之处:
可以看到网关出现在了请求链路里:
对秒杀服务的网管进行配置:
其中Burst Size代表忽然流量暴增时额外增加的请求数
通过api管理可以同时管理多个api
还可以通过配置文件定制返回结果:
spring.cloud.sentinel.scg.fallback.content-type=application/json
spring.cloud.sentinel.scg.fallback.response-status=400
spring.cloud.sentinel.scg.fallback.response-body=wrong
或者写代码来配置:
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.fastjson.JSON;
import com.atguigu.common.exception.BizCodeEnum;
import com.atguigu.common.utils.R;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
public class SentinelGatewayConfig {
public SentinelGatewayConfig() {
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
//网关限流了请求,就会调用此回调
@Override
public Mono handleRequest(ServerWebExchange exchange, Throwable t) {
R error = R.error(BizCodeEnum.TO_MANY_REQUEST.getCode(), BizCodeEnum.TO_MANY_REQUEST.getMessage());
String errorJson = JSON.toJSONString(error);
Mono body = ServerResponse.ok().body(Mono.just(errorJson), String.class);
return body;
}
});
}
}