springBoot整合Sentinel实现降级限流熔断

/ 0条评论 / 0 个点赞 / 1560人阅读

由于hystrix的停止更新,以及阿里Sentinel在历年双十一的贡献。项目中使用了Sentinel,今天我们来讲讲Sentinel的入门教程,本文使用1.6.3版本进行讲解

本文通过Sentinel_dashBoard进行讲解,当然不引入监控看板也能实现限流熔断降级功能,但是监控看板能够直观的看到请求的QPS,成功率等等,同时可以实时的进行降级限流策略的修改与新建。

sentinel_dashboard的引入

  https://github.com/alibaba/Sentinel/releases,下载sentinel-dashboard-1.6.3.jar

由于dashboard是springboot的项目,在CMD模式下使用命令

  

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.6.3.jar


进行控制看板服务的启动。

其中,-Dserver.port=8080  代表看板项目的端口号,

-Dcsp.sentinel.dashboard.server=localhost:8080  代表本看板服务将会注册到自己的看板上,

-Dproject.name=sentinel-dashboard  代表本看板服务的项目名称。

访问localhost:8080;输入用户名,密码,均是sentinel,如果要自定义用户名和密码,在启动命令加上-Dsentinel.dashboard.auth.username=sentinel,-Dsentinel.dashboard.auth.password=123456即可。

/upload/article/png/520520_20210820183554.png

我们可以看到控制台自身的服务已经注册到了控制台上。

接下来,引入需要接入sentinel功能的项目。

maven依赖

<dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>1.6.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.6.3</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.6.3</version>
        </dependency>

编写启动类

package sentile;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SentileApp {

    public static void main(String[] args) {
        SpringApplication.run(SentileApp.class, args);
    }
}


@SentinelResource

使用这个注解,可以对我们要进行限流的资源接口当出现异常时进行捕获,比如下面的这个接口 getByResource,@SentinelResource的使用很简单,最基本的就是配置资源名称,加上blockHandler ,blockHandler 里面是触发限流规则时的方法,即在这个方法中进行后续的业务处理,如抛出友好的提示等

@RestController
public class RateLimitController {

    @GetMapping("/getByResource")
    @SentinelResource(value = "getByResource",blockHandler = "handleException")
    public ResponseResult getByResource(){
        return ResponseResult.success("this is success result");
    }

    public ResponseResult handleException(BlockException blockExe){
        return ResponseResult.fail(412,"is block:" + blockExe.getMessage());
    }

}


blockHandler需要特别注意,必须和源方法的参数名还有返回值类型保持一致才会被解析成功。


同样,我们在dashboard中对这个接口进行配置,每秒1次的QPS,正常访问时,

/upload/article/png/520520_20210820184431.png

快速访问时,就走到了降级后的方法中了

/upload/article/png/520520_20210820184448.png

但是如果这么做的话,很明显的一个问题就是,异常处理的逻辑和业务耦合在一起,代码会越来越膨胀,后续不方便管理,因此我们单独提供一个类,这个类专门用于提供不同的异常方法,

public class CustomerBlockHandler {

    public static ResponseResult handleException(BlockException exe){
        return ResponseResult.fail(412,"is block:" + exe.getMessage());
    }
}

那么在接口中我们就可以这样调用

	@GetMapping("/getByResource1")
    @SentinelResource(value = "customerBlockHandler",
    blockHandlerClass = CustomerBlockHandler.class,
    blockHandler = "handleException")
    public ResponseResult getByResource1(){
        return ResponseResult.success("this is success result");
    }

同样,再在控制台配置限流规则后可以达到我们的效果,

/upload/article/png/520520_20210820184617.png

关于降级

还以前面的 /testFlow接口为例:

/upload/article/png/520520_20210820184132.png



服务降级可能大家并不陌生,即在一个应用中,当请求到某个服务接口长时间没有响应时,为了避免更多的请求进来造成服务的雪崩,我们采取一种机制,让程序快速返回结果,或者干脆抛出友好的提示,让调用者知道当前的服务不可用,这样就不至于出现服务端的线程资源被耗尽的情况

对于上述图中的配置,解释起来就是:超过了每秒请求的阈值之后,后面的请求再过来的时候,在5秒之内,服务处于不可用的状态,5秒之后,服务恢复,我们通过快速刷新接口,可以看到效果

/upload/article/png/520520_20210820184211.png

如果是异常数的配置,则触发条件如下:



有这么一种情况,当调用接口时,由于参数校验不通过,或者后端抛出了运行时的异常,如果没有合适的处理,将会出现下面的结果,

	@GetMapping("/createOrder")
    @SentinelResource(value = "fallback")
    public String createOrder(String id){
        if(id.equals("2")){
            throw new IllegalArgumentException("参数异常");
        }
        return orderService.createOrder(id);
    }

/upload/article/png/520520_20210820185006.png

显然,这是一种不太友好的情况,对于这种情况,就可以使用Sentinel的服务降级进行处理了,改造后的接口如下:

	@GetMapping("/createOrder")
    @SentinelResource(value = "fallback",fallback = "getFallBack")
    public String createOrder(String id){
        if(id.equals("2")){
            throw new IllegalArgumentException("参数异常");
        }
        return orderService.createOrder(id);
    }

    public static String getFallBack(String id,Throwable t){
        return id + ":=====>" + t.getMessage();
    }

再次访问时,可以看到出现了我们的降级方法,也可以理解为兜底的方法

/upload/article/png/520520_20210820185043.png

同样我们可以利用一个单独的类,统一处理这样的降级,如下:

	@GetMapping("/createOrder")
    @SentinelResource(value = "fallback",fallbackClass = MyFallBackHandler.class,fallback = "getFallBack")
    public String createOrder(String id){
        if(id.equals("2")){
            throw new IllegalArgumentException("参数异常");
        }
        return orderService.createOrder(id);
    }
public class MyFallBackHandler {

   public static String getFallBack(String id,Throwable t){
       return id + ":=====>" + t.getMessage();
   }

}

其实在真实的开发中,不管是单应用还是微服务之间的调用,一般情况下可能出现的异常我们基本上都是可以提前预知的,如超时异常,系统参数异常等,这样我们就可以预设部分的降级处理方法,在适当的接口上面进行引用即可