JAVA微服务_sentinel

Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
同hystrix相比,除了服务熔断,还可以进行服务降级、服务流控等。
sentinel有多种使用方式,常见的有单独使用dashboard、同Gateway结合使用、同Spring Cloud/Alibaba结合使用、同RPC组件结合使用等。建议结合项目选择。开源项目Ruoyi-Cloud选择结合Gateway使用。

在服务端用maven引入jar包;
控制面板dashboard可以从https://github.com/alibaba/Sentinel/releases下载sentinel-dashboard-$version.jar包。

Sentinel基本概念

  • 资源 resource:Sentinel中一切抽象成资源,服务、服务中的方法,甚至是一段代码,都可以被定义成资源,然后被Sentinel管理;
  • 规则 rules : 流量规则,熔断规则,负载规则,,,

五大规则

流控规则

降级规则
监控应用中资源调用请求,达到阈值时自动触发熔断降级。
热点规则

系统规则
系统自适应限流
授权规则
黑白名单控制

快速开始

本文以Spring Cloud Alibaba中引入Sentinel为例,其他方式引入请参考官方wiki。
在服务中引入Sentinel,然后在dashboard中配置规则。


<dependency>
	<groupId>com.alibaba.cloudgroupId>
	<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>

在配置文件中开启Sentinel

spring.cloud.sentinel.enabled=true
# 指定sentinel dashboard web地址
spring.cloud.sentinel.transport.dashboard=  #dashboard的ip:端口
#  指定snetinel组件和sentinel dashboard组件通信地址(用tcp更高效的通信,端口默认为8719)
spring.cloud.sentinel.transport.port=8719

DashBoard控制台方式

使用java -jar命令启动dashboard控制台:

java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\sentinel\sentinel-dashboard-1.8.0.jar

Sentinel提供了一个可视化的操作平台,安装好之后,在浏览器中输入(http://localhost:8718 (opens new window))就可以访问了,默认的用户名和密码都是sentinel

dashboard在有一次调用发生后,才能在web界面看到服务(这算懒加载么?)
调用后sentinel才会产生资源调用日志,有日志生成后才会dashboard出现服务,多调用几次才会出现qps等信息。

熔断 降级规则

用来避免服务雪崩。触发熔断后对该服务的调用不可用。
在dashboard中选择“降级规则”

Sentinel熔断策略

触发熔断(断路器打开)的三种策略
RT 根据请求响应时间熔断
异常比例
抛出的异常的比例。如设置为0.1,则比例超过10%时触发熔断
异常数

时间窗口(单位:秒)结束后,断路器关闭。

热点规则

也成为热点参数限流,对携带了指定参数的资源进行限流。
https://editor.csdn.net/md/?articleId=131038775,参数为articleId
只能使用QPS模式。

不能使用资源路径的方式配置,只能使用资源别名。
资源别名:在处理请求的方法上使用@SentinelResource(value="别名")注解指定别名。mvc模式的话就放在controller层方法上。

在[统计窗口时长]内,对带有第[参数索引]个参数的请求,超过阈值时进行限流。
参数索引从0开始计数,代表请求中的第几个参数。

@SentinelResource(value=“别名”, fallback=“”, blcokHandler=“限流处理method名”)
fallback处理业务异常,blockHandler处理限流。指定方法名。
限流处理method例子:
controller层方法带Integer id参数:

private String blockHandler(Integer id, BlockException e){
	if(e instanceof FlowException){
		return "当前请求过于火爆,已被降级";
	}
	if(e instanceof DegradeException){
		return "当前请求过于火爆,已被降级";
	}
	if(e instanceof ParaFlowException){
		return "当前请求过于火爆,已被参数热点限流";
	}
	return "服务器过于火爆,请稍后再试捏~(づ ̄3 ̄)づ╭❤~kira";
}

限流处理method可以通过这种方式拿到请求发来的参数。(要保持一致?)

流量控制(限流)

原理是监控应用流量的 QPS 或 并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被顺势的流量高峰冲垮,从而保障应用的高可用性;

  • Q: 怎么控制
    • A: dashboard设置规则时选择高级选项-流控模式
      • 直接 返回Blocked by Sentinel
      • 关联
      • 链路

流控模式 三种

配置资源:在编辑规则时设置的资源
关联资源:和配置资源相关联的资源

直接

当对配置资源的请求超过阈值时,对配置资源的请求的处理(三种处理方式在本文下一小节)

关联

当所关联的资源的请求超过阈值后,对当前资源进行的处理
如在以下例子中,当关联的"/aa"资源访问次数超过阈值,则"/demo"资源要受到流控处理
JAVA微服务_sentinel_第1张图片

链路

对链路上的资源的处理

流控效果/处理方式 三种

注意:只适用于QPS限流

  • 快速失败
    • 直接拒绝请求并抛出异常FlowExcrption
  • Warm up
    • 冷启动,预热
    • 在一定时间(预热时长)内将系统逐渐拉至高水位
  • 排队等待
    • 同Warm up相比,特点为始终匀速处理

Gateway方式

限流

Gateway配置类中添加限流规则

import javax.annotation.PostConstruct;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;

/**
 * 网关限流配置
 * 
 */
@Configuration
public class GatewayConfig
{
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter()
    {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit()
    {
        // 加载网关限流规则
        initGatewayRules();
    }

    /**
     * 网关限流规则   
     */
    private void initGatewayRules()
    {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("ruoyi-system")
                .setCount(3) // 限流阈值
                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
        // 加载网关限流规则
        GatewayRuleManager.loadRules(rules);
    }
}
  • 这种小事就不能交给配置文件吗?难道因为不是gateway自己的?
  • 那个异常处理可以在配置文件,详见“自定义异常”

分组限流

import java.util.HashSet;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.ruoyi.gateway.handler.SentinelFallbackHandler;

/**
 * 网关限流配置
 * 
 * @author ruoyi
 */
@Configuration
public class GatewayConfig
{
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelFallbackHandler sentinelGatewayExceptionHandler()
    {
        return new SentinelFallbackHandler();
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter()
    {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit()
    {
        // 加载网关限流规则
        initGatewayRules();
    }

    /**
     * 网关限流规则   
     */
    private void initGatewayRules()
    {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("system-api")
                .setCount(3) // 限流阈值
                .setIntervalSec(60)); // 统计时间窗口,单位是秒,默认是 1 秒
        rules.add(new GatewayFlowRule("code-api")
                .setCount(5) // 限流阈值
                .setIntervalSec(60));
        // 加载网关限流规则
        GatewayRuleManager.loadRules(rules);
        // 加载限流分组
        initCustomizedApis();
    }

    /**
     * 限流分组   
     */
    private void initCustomizedApis()
    {
        Set<ApiDefinition> definitions = new HashSet<>();
        // ruoyi-system 组
        ApiDefinition api1 = new ApiDefinition("system-api").setPredicateItems(new HashSet<ApiPredicateItem>()
        {
            private static final long serialVersionUID = 1L;
            {
                // 匹配 /user 以及其子路径的所有请求
                add(new ApiPathPredicateItem().setPattern("/system/user/**")
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
            }
        });
        // ruoyi-gen 组
        ApiDefinition api2 = new ApiDefinition("code-api").setPredicateItems(new HashSet<ApiPredicateItem>()
        {
            private static final long serialVersionUID = 1L;
            {
                // 只匹配 /job/list
                add(new ApiPathPredicateItem().setPattern("/code/gen/list"));
            }
        });
        definitions.add(api1);
        definitions.add(api2);
        // 加载限流分组
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}
  • 不过这样的话,,路径就写死在代码里了

自定义异常

为了展示更加友好的限流提示, Sentinel支持自定义异常处理。

方案一:yml配置

# Spring
spring: 
  cloud:
    sentinel:
      scg:
        fallback:
          mode: response
          response-body: '{"code":403,"msg":"请求超过最大数,请稍后再试"}'

方案二:GatewayConfig注入Bean

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
{
	return new SentinelFallbackHandler();
}

SentinelFallbackHandler.java

import java.nio.charset.StandardCharsets;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;

/**
 * 自定义限流异常处理
 *
 */
public class SentinelFallbackHandler implements WebExceptionHandler
{
    private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
    {
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] datas = "{\"code\":429,\"msg\":\"请求超过最大数,请稍后再试\"}".getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
        return serverHttpResponse.writeWith(Mono.just(buffer));
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
    {
        if (exchange.getResponse().isCommitted())
        {
            return Mono.error(ex);
        }
        if (!BlockException.isBlockException(ex))
        {
            return Mono.error(ex);
        }
        return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
    }

    private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
    {
        return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
    }
}

实现原理

在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)

这些功能插槽有些类似游戏或是组装电脑里“升级插槽”的概念,在原有基础上添加功能。可以自定义插槽

插槽介绍

NodeSelectorSlot

这个 slot 主要负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级。
资源确实是树状结构的,就像树形目录。结构上资源和url很像,也应当是树形结构

熔断降级设计理念
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池的方式,来对依赖(在我们的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。
缺点:

  • 增加了线程切换的成本
  • 需要预先给各个资源做线程池大小的分配,容易导致碎片化
  • 怎么线程池隔离?不如说实际上隔离时怎么做到的?内存地址拒绝访问?

核心类解析

Sentinel 对这个问题采取了两种手段:
通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
系统负载保护
Sentinel 同时对系统的维度提供保护。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

进一步学习

相关概念:

SPI:SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议,比如AT91RM9200。
官方文档:https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5

你可能感兴趣的:(java,微服务,sentinel)