Spring Cloud Alibaba 04:Sentinel 服务限流降级

雪崩效应

解决方案

1、设置线程超时

2、设置限流

3、熔断器 Sentinel、Hystrix

1、pom.xml 引入依赖

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2、application 配置

# actuator暴露所有端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
# 与Sentinel DashBoard交互地址
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080

3、下载 Sentinel 控制台:https://github.com/alibaba/Sentinel/releases,启动:java -jar sentinel-dashboard-1.8.0.jar 账号密码皆为sentinel,启动nacos,启动provider模块和consumer模块,访问http://localhost:9090/index

1、流控规则

直接限流:直接对关联的url资源限流

开启sentinel,开启provider服务

对/index资源做流控,设置QPS为1(表示一秒钟只允许访问一次)

当访问一秒钟访问http://localhost:8001/index超过一次时,会限制显示被流控限制阻塞

关联限流:当被访问的url资源超过设定的阈值,限流关联的资源

controller新增list方法:

@GetMapping("/list")
public String list() {
    return "list";
}

添加测试依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

需要同时范文资源才能看到效果,测试类对http://localhost:8001/index访问

@Test
@DisplayName("测试关联流控模式")
void test1() throws InterruptedException {
    RestTemplate restTemplate = new RestTemplate();
    for (int i = 0; i < 100; ++i) {
        restTemplate.getForObject("http://localhost:8081/index", String.class);
        System.out.println("provider==>/index=======>" + i);
        //休眠200毫秒
        TimeUnit.MILLISECONDS.sleep(200);
    }
}

启动provider程序,设置流控规则如下:

设置完流控规则后启动测试程序,浏览器访问http://localhost:8001/list则出现以下情况,表示对index访问超过阈值,则关联资源list限流

链路限流:对更深层次资源限流(不仅仅局限于controller)

1、pom.xml 添加依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
</dependency>

2、application.yml

spring:
	cloud:
        sentinel:
          filter:
            enabled: false

3、写配置类

package com.godfrey.configuration;

import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

/**
 * @author godfrey
 * @since 2020-12-07
 */
@Configuration
public class FilterConfiguration {

    @Bean
    public FilterRegistrationBean<Filter> registrationBean() {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new CommonFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
        registrationBean.setName("sentinelFilter");
        return registrationBean;
    }
}

4、Service

@Service
public class ProviderService {

    @SentinelResource("test")  //test 保护资源名
    public void test() {
        System.out.println("test");
    }
}

5、Controller

private final ProviderService providerService;

@Autowired
public ProviderController(ProviderService providerService) {
    this.providerService = providerService;
}

@GetMapping("/test1")
public String test1() {
    this.providerService.test();
    return "test1";
}

@GetMapping("/test2")
public String test2() {
    this.providerService.test();
    return "test2";
}

为了对比,test1做链路限流,对test2不做限流,设置如下

一秒钟访问http://localhost:8081/test1超过一次时,会对service绑定资源限流

访问http://localhost:8081/test2则不会

2、流控效果

快速失败

直接抛出异常

Warm UP

给系统一个预热的时间,预热时间段内单机阈值较低,预热时间过后单机阈值增加,预热时间内当前的单机阈值是设置的阈值的三分之一,预热时间过后单机阈值恢复设置的值。

排队等待

当请求调用失败之后,不会立即抛出异常,等待下一次调用,时间范围是超时时间,在时间范围内如果请求则抛出异常。阀值类型必须设成QPS,否则无效

3、降级规则

RT

单个请求的响应时间超过阈值,则进入准降级状态,接下来 1 S 内连续 5 个请求响应时间均超过阈值,就进行降级,持续时间为时间窗口的值。

异常比例

每秒异常数量占通过量的比例大于阈值,就进行降级处理,持续时间为时间窗口的值。

异常数

1 分钟内的异常数超过阈值就进行降级处理,时间窗口的值要大于 60S,否则刚结束熔断又进入下一次熔断了。

4、热点规则

热点规则是流控规则的更细粒度操作,可以具体到对某个热点参数的限流,设置限流之后,如果带着限流参数的请求量超过阈值,则进行限流,时间为统计窗口时长。

必须要添加 @SentinelResource,即对资源进行流控。

@GetMapping("/hot")
@SentinelResource("hot")
public String hot(
    @RequestParam(value = "num1", required = false) Integer num1,
    @RequestParam(value = "num2", required = false) Integer num2) {
    return num1 + "-" + num2;
}

对参数num1进行限流

效果:

可以添加例外值:当传的对应参数值等于例外值的时候,读取的阈值为例外设定阈值

5、授权规则

给指定的资源设置流控应用(追加参数),可以对流控应用进行访问权限的设置,具体就是添加白名单和黑名单。

如何给请求指定流控应用,通过实现 RequestOriginParser 接口来完成,代码如下所示。

package com.godfrey.configuration;

import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * @author godfrey
 * @since 2020-12-26
 */
public class RequestOriginParserDefinition implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String name = httpServletRequest.getParameter("name");
        if (StringUtils.isEmpty(name)) {
            throw new RuntimeException("name is null");
        }
        return name;
    }
}

要让 RequestOriginParserDefinition 生效,需要在配置类中进行配置。

package com.godfrey.configuration;

import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * @author godfrey
 * @since 2020-12-26
 */
@Configuration
public class SentinelConfiguration {

    @PostConstruct
    public void init() {
        WebCallbackManager.setRequestOriginParser(new RequestOriginParserDefinition());
    }
}

6 自定义规则异常返回

创建异常处理类

package com.godfrey.execption;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author godfrey
 * @since 2020-12-26
 */
public class ExceptionHandler implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        httpServletResponse.setContentType("text/html;charset=utf-8");
        String msg = null;
        if (e instanceof FlowException) {
            msg = "限流";
        } else if (e instanceof DegradeException) {
            msg = "降级";
        }
        httpServletResponse.getWriter().write(msg);
    }
}

进行配置。

@Configuration
public class SentinelConfiguration {

	@PostConstruct
    public void init2() {
        WebCallbackManager.setUrlBlockHandler(new ExceptionHandler());
    }
}
posted @ 2020-12-07 16:52  对弈  阅读(425)  评论(0编辑  收藏  举报