Sentinel限流熔断降级

Sentinel是阿里开源的一款流量治理组件,它以流量为切入点从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。不同于大多数其它的流量治理组件,使用Sentinel非常简单和直观,Sentinel提供了一个Dashboard服务,我们的微服务只需要引入集成Sentinel的依赖,然后在Dashboard上以图形界面的方式配置流量治理规则即可。

注:SpringCloudAlibaba的Sentinel和Redis的Sentinel没有任何关系,只是两个项目恰好重名了,这里不要混淆。

官方文档:https://sentinelguard.io/zh-cn/index.html

SpringCloud微服务集成Sentinel

使用Sentinel需要先启动它的Dashboard服务。Sentinel Dashboard本质是一个SpringBoot工程,我们可以直接下载Jar包运行,或者也可以从Github上克隆源码在本地定制化开发后启动,这里简单起见,我们先演示预编译Jar包的用法,软件包可以从Github Release页面获取。

Github Release页面:https://github.com/alibaba/Sentinel/releases

下载完成后,我们可以直接用命令行启动Sentinel Dashboard服务,它默认使用8080端口。

java -jar sentinel-dashboard-1.8.7.jar

启动完成后访问http://localhost:8080即可打开Sentinel Dashboard界面。

这里我们可以看到需要用户名和密码来登录,它们默认都是sentinel。在生产环境中,我们可能需要设置我们自己的用户名和密码,这些都可以通过命令行设置JVM的系统参数-Dsentinel.dashboard.auth.username-Dsentinel.dashboard.auth.password指定,对于端口号等其他设置也可以按照此方法修改。

Sentinel Dashboard启动完成后,我们还需要在微服务工程的pom.xml中引入Sentinel起步依赖。

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

除此之外,我们还需要在application.properties中添加一些配置,使得我们的服务集成到Dashboard。

spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080

spring.cloud.sentinel.transport.dashboard配置了Dashbaord服务的地址。

此时我们的环境就搭建完成了,Sentinel默认支持以SpringMVC的接口作为EndPoint(即流控监控的端点),访问集成Sentinel的微服务中的一些接口后,我们可以在Sentinel控制台中查看相关的信息。我们可以点击实时监控,查看Sentinel监控的各种信息和图标,此外我们还可以点击簇点链路查看注册到Sentinel的端点,并针对性的添加规则。

流量控制

流量控制用于限制一个端点的QPS或处理线程数。下面例子中,我们限制了/api/v1/order/getOrderById接口的QPS为1,我们配置后可以尝试用Jmeter等压测工具访问该接口查看效果。

触发流控的请求会快速失败,返回状态码429及如下错误信息。

Blocked by Sentinel (flow limiting)

如果想要自定义触发流控时的处理逻辑,我们可以使用@SentinelResource注解,有关该内容参考后文的介绍。

Sentinel的流控功能有许多不同的模式:阈值类型包括QPS并发线程数,前者基于接口的每秒请求数进行流控,后者基于实时的并发线程数进行限流。

流控模式包括直接关联链路三种模式,直接模式比较简单,就是单纯的对一个接口进行流控;关联模式支持指定一个关联端点,该端点通常是一个优先级较低的服务,触发阈值时对这个关联端点的请求进行限流;链路模式则是只统计从指定端点进入的请求,是对请求来源的限流。

流控效果也包括三种,快速失败预热排队等待,快速失败很容易理解,触发流控后Sentinel会立刻抛出异常并通过相应的异常处理器处理失败逻辑;预热模式的QPS限制是动态慢开启的,Sentinel会通过一定算法逐渐将QPS限制提升到配置的值;排队模式会有一个队列,超过QPS限制的请求会在超时时间内排队等待,而不会立刻失败。

除了上述规则,Sentinel还支持热点限流,它能够统计一个方法参数出现指定值的次数作为限流依据,例如一个查询产品的接口,我们将productId作为限流依据并指定QPS限制100,那么此时如果ID为1001的产品QPS达到100就会触发流控,但其它ID的产品则仍能正常查询;热点限流的另一种常见用途是对恶意刷量请求的防御,例如以用户ID作为流控参数,当某个用户的请求达到阈值时判断该用户为恶意用户,对其进行流控。

服务熔断

服务熔断在服务调用超时或失败等指标满足一定阈值时触发,触发熔断后服务会直接快速失败或执行降级逻辑。下面例子我们给/api/v1/order/getOrderById接口添加了一个熔断规则,它会在服务超时达到10%时熔断,相关的规则参数如下图所示。

触发流控的请求会快速失败,返回状态码429及如下错误信息。

Blocked by Sentinel (flow limiting)

Sentinel的熔断策略有三种,慢调用比例异常比例异常数,这三种策略都很好理解,慢调用比例就是基于一个超时时间统计超过该时间的调用比例作为熔断阈值;异常比例则是统计接口抛出异常的比例;异常数是统计接口抛出异常的次数。

使用@SentinelResource注解

前面例子中我们可以看到Sentinel默认对SpringMVC接口端点进行了监控,实际开发中我们需要流控和熔断的端点可能还有其它类型,@SentinelResource注解可以将任意SpringBean的public方法添加为Sentinel监控端点,此外该注解还可以配置流控和熔断的降级规则等信息。

package com.gacfox.demo.order.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.gacfox.demo.product.client.ProductClient;
import com.gacfox.demo.product.order.model.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@Slf4j
@RestController
@RequestMapping("/api/v1/order")
public class OrderController {
    @Resource
    private ProductClient productClient;

    @SentinelResource(value = "getOrderById", fallback = "fallback")
    @GetMapping("/getOrderById")
    public Order getOrderById(String orderId) {
        // ... 具体业务逻辑
    }

    private Order fallback(String orderId) {
        log.info("触发fallback");
        return null;
    }
}

上面代码中我们在SpringMVC接口上新增了一个@SentinelResource注解,使用fallback属性指定了Sentinel端点名以及限流和熔断触发时的处理函数。在Sentinel Dashboard中,我们可以找到对应名称的端点并进行配置。

@SentinelResource注解不仅可以添加到SpringMVC接口上,它也可以添加到任意SpringBean的public方法上实现限流和熔断,这里就不再赘述了。

SpringCloud OpenFeign集成

Sentinel支持和SpringCloud OpenFeign进行集成,我们直接在application.properties中进行配置即可。

feign.sentinel.enabled=true

配置中,feign.sentinel.enabled用于SpringCloud OpenFeign与Sentinel的集成,我们将其设置为true

OpenFeign集成后在Java代码中我们可以对OpenFeign接口增加fallbackFactory属性指定Fallback逻辑,下面是一个例子。

package com.gacfox.demo.product.client;

import com.gacfox.demo.product.model.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "product", path = "/api/v1/product", fallbackFactory = ProductClientFallbackFactory.class)
public interface ProductClient {
    @GetMapping("/getProductById")
    Product getProductById(@RequestParam(name = "productId") String productId);
}
package com.gacfox.demo.product.client;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ProductClientFallbackFactory implements FallbackFactory<ProductClient> {

    @Override
    public ProductClient create(Throwable cause) {
        return productId -> {
            log.info("openfeign fallback");
            return null;
        };
    }
}

注意FallbackFactory需要注册为SpringBean因此这里我们使用了@Component注解,如果没有注册成功程序启动时可能会报错。配置完成后,此时我们就可以在Dashboard中配置针对OpenFeign接口的流控规则了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。