Sentinel 流量防卫兵-SpringCloudAlibaba No.4

Posted Fire king

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sentinel 流量防卫兵-SpringCloudAlibaba No.4相关的知识,希望对你有一定的参考价值。

这里写目录标题

安装与部署

1.下载

github

2.配置idea启动


启动之后,就可以访问到Sentinel的监控页面了,用户名和密码都是sentinel,地址:http://localhost:8858/#/dashboard

3.需要的微服务加上依赖

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

4.需要的微服务的配置文件

注意对齐方式

spring:
  application:
    name: userservice
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
          # 添加监控页面地址即可
        dashboard: localhost:8858

其实重点是这段:

 sentinel:
      transport:
          # 添加监控页面地址即可
        dashboard: localhost:8858

现在启动我们的服务,然后访问一次服务,这样Sentinel中就会存在信息了(懒加载机制,不会一上来就加载)

流量控制

1.小试牛刀

打开管理页面的簇点链路模块:

这里演示对我们的借阅接口进行限流,点击流控,会看到让我们添加流控规则:

  • 阈值类型:QPS就是每秒钟的请求数量,并发线程数是按服务当前使用的线程数据进行统计的。
  • 流控模式:当达到阈值时,流控的对象,这里暂时只用直接。
  • 流控效果:就是我们上面所说的三种方案。

这里我们选择QPS、阈值设定为1,流控模式选择直接、流控效果选择快速失败,可以看到,当我们快速地进行请求时,会直接返回失败信息:

2.链路模式:

1.一视同仁

我们可以对某一个方法进行限流控制,无论是谁在何处调用了它,这里需要使用到@SentinelResource,一旦方法被标注,那么就会进行监控,比如我们这里创建两个请求映射,都来调用Service的被监控方法:

@RestController
public class BorrowController 

    @Resource
    BorrowService service;

    @RequestMapping("/borrow/uid")
    UserBorrowDetail findUserBorrows(@PathVariable("uid") int uid)
        return service.getUserBorrowDetailByUid(uid);
    

    @RequestMapping("/borrow2/uid")
    UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid)
        return service.getUserBorrowDetailByUid(uid);
    

@SentinelResource("getBorrow")中的getBorrow可以看作是getUserBorrowDetailByUid方法的资源名。

@Service
public class BorrowServiceImpl implements BorrowService

    @Resource
    BorrowMapper mapper;

    @Resource
    UserClient userClient;

    @Resource
    BookClient bookClient;

    @Override
    @SentinelResource("getBorrow")   //监控此方法,无论被谁执行都在监控范围内,这里给的value是自定义名称,这个注解可以加在任何方法上,包括Controller中的请求映射方法,跟HystrixCommand贼像
    public UserBorrowDetail getUserBorrowDetailByUid(int uid) 
        List<Borrow> borrow = mapper.getBorrowsByUid(uid);
        User user = userClient.getUserById(uid);
        List<Book> bookList = borrow
                .stream()
                .map(b -> bookClient.getBookById(b.getBid()))
                .collect(Collectors.toList());
        return new UserBorrowDetail(user, bookList);
    

spring:
  application:
    name: borrowservice
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8858
      # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
      web-context-unify: false

然后我们在Sentinel控制台中添加流控规则,注意是针对此方法,可以看到已经自动识别到borrow接口下调用了这个方法:

最后我们在浏览器中对这两个接口都进行测试,会发现,无论请求哪个接口,只要调用了Service中的getUserBorrowDetailByUid这个方法,都会被限流。注意限流的形式是后台直接抛出异常,至于怎么处理我们后面再说。

2.区别对待

那么这个链路选项实际上就是决定只限流从哪个方向来的调用,比如我们只对borrow2这个接口对getUserBorrowDetailByUid方法的调用进行限流,那么我们就可以为其指定链路:

然后我们会发现,限流效果只对我们配置的链路接口有效,而其他链路是不会被限流的。

除了直接对接口进行限流规则控制之外,我们也可以根据当前系统的资源使用情况,决定是否进行限流:

3.其他流控模式刻自行参悟

限流和异常处理

1.controller

@RequestMapping("/blocked")
JSONObject blocked()
    JSONObject object = new JSONObject();
    object.put("code", 403);
    object.put("success", false);
    object.put("massage", "您的请求频率过快,请稍后再试!");
    return object;

2.配置文件

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8858
      # 将刚刚编写的请求映射设定为限流页面
      block-page: /blocked

这样,当被限流时,就会被重定向到指定页面:

3.更加细粒度限流异常处理-方法级别

1.需要限流的方法

@Override
@SentinelResource(value = "getBorrow", blockHandler = "blocked")   //指定blockHandler,也就是被限流之后的替代解决方案,这样就不会使用默认的抛出异常的形式了
public UserBorrowDetail getUserBorrowDetailByUid(int uid) 
    List<Borrow> borrow = mapper.getBorrowsByUid(uid);
    User user = userClient.getUserById(uid);
    List<Book> bookList = borrow
            .stream()
            .map(b -> bookClient.getBookById(b.getBid()))
            .collect(Collectors.toList());
    return new UserBorrowDetail(user, bookList);


//替代方案,注意参数和返回值需要保持一致,并且参数最后还需要额外添加一个BlockException
public UserBorrowDetail blocked(int uid, BlockException e) 
    return new UserBorrowDetail(null, Collections.emptyList());

可以看到,一旦被限流将执行替代方案,最后返回的结果就是:

2.其他异常处理

注意blockHandler只能处理限流情况下抛出的异常,包括下面即将要介绍的热点参数限流也是同理,如果是方法本身抛出的其他类型异常,不在管控范围内,但是可以通过其他参数进行处理:

@RequestMapping("/test")
@SentinelResource(value = "test",
        fallback = "except",    //fallback指定出现异常时的替代方案
        exceptionsToIgnore = IOException.class)  //忽略那些异常,也就是说这些异常出现时不使用替代方案
String test()
    throw new RuntimeException("HelloWorld!");


//替代方法必须和原方法返回值和参数一致,最后可以添加一个Throwable作为参数接受异常
String except(Throwable t)
    return t.getMessage();

这样,其他的异常也可以有替代方案了:

特别注意这种方式会在没有配置blockHandler的情况下,将Sentinel机制内(也就是限流的异常)的异常也一并处理了,如果配置了blockHandler,那么在出现限流时,依然只会执行blockHandler指定的替代方案(因为限流是在方法执行之前进行的)
另外,实践领悟:

  • contoller级别会覆盖方法级别限流代替方案
  • 方法级别代替方案,如果对controller级别设置流控规则,出现的是默认代替方案而不是自定义的

4.热点参数限流

我们还可以对某一热点数据进行精准限流,比如在某一时刻,不同参数被携带访问的频率是不一样的:

  • http://localhost:8301/test?a=10 访问100次
  • http://localhost:8301/test?b=10 访问0次
  • http://localhost:8301/test?c=10 访问3次

由于携带参数a的请求比较多,我们就可以只对携带参数a的请求进行限流。

这里我们创建一个新的测试请求映射:数限流

1.controller

@RequestMapping("/test")
@SentinelResource("test")   //注意这里需要添加@SentinelResource才可以,用户资源名称就使用这里定义的资源名称
String findUserBorrows2(@RequestParam(value = "a", required = false) int a,
                        @RequestParam(value = "b", required = false) int b,
                        @RequestParam(value = "c",required = false) int c) 
    return "请求成功!a = "+a+", b = "+b+", c = "+c;

然后开始访问我们的测试接口,可以看到在携带参数a时,当访问频率超过设定值,就会直接被限流,这里是直接在后台抛出异常:

而我们使用其他参数或是不带a参数,那么就不会出现这种问题了:

除了直接对某个参数精准限流外,我们还可以对参数携带的指定值单独设定阈值,比如我们现在不仅希望对参数a限流,而且还希望当参数a的值为10时,QPS达到5再进行限流,那么就可以设定例外:

这样,当请求携带参数a,且参数a的值为10时,阈值将按照我们指定的特例进行计算。

服务熔断和降级

1.Sentinel中如何进行熔断和降级操作

打开管理页面,我们可以自由新增熔断规则:

其中,熔断策略有三种模式:

2.慢调用比例:

如果出现那种半天都处理不完的调用,有可能就是服务出现故障,导致卡顿,这个选项是按照最大响应时间(RT)进行判定,如果一次请求的处理时间超过了指定的RT,那么就被判定为慢调用,在一个统计时长内,如果请求数目大于最小请求数目,并且被判定为慢调用的请求比例已经超过阈值,将触发熔断。经过熔断时长之后,将会进入到半开状态进行试探(这里和Hystrix一致)

然后修改一下接口的执行,我们模拟一下慢调用:

@RequestMapping("/borrow2/uid")
UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid) throws InterruptedException 
    Thread.sleep(1000);
    return null;

重启,然后我们创建一个新的熔断规则:

可以看到,超时直接触发了熔断,进入到阻止页面:

3.异常比例:

这个与慢调用比例类似,不过这里判断的是出现异常的次数,与上面一样,我们也来进行一些小测试:

@RequestMapping("/borrow2/uid")
UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid) 
    throw new RuntimeException();

启动服务器,接着添加我们的熔断规则:

现在我们进行访问,会发现后台疯狂报错,然后就熔断了:

4.异常数:

这个和上面的唯一区别就是,只要达到指定的异常数量,就熔断,这里我们修改一下熔断规则:

现在我们再次不断访问此接口,可以发现,效果跟之前其实是差不多的,只是判断的策略稍微不同罢了:

Feign支持Sentinel

1.需要的服务在配置文件中的配置

使用Hystrix的时候,就可以直接对Feign的每个接口调用单独进行服务降级,而使用Sentinel,也是可以的,首先我们需要在配置文件中开启支持:

feign:
  sentinel:
    enabled: true

2.创建实现类

之后的步骤其实和之前是一模一样的,首先创建实现类:

@Component
public class UserClientFallback implements UserClient
    @Override
    public User getUserById(int uid) 
        User user = new User();
        user.setName("我是替代方案");
        return user;
    

然后直接启动就可以了,中途的时候我们吧用户服务全部下掉,可以看到正常使用替代方案:

这样Feign的配置就OK了,那么传统的RestTemplate呢?我们可以使用@SentinelRestTemplate注解实现:

以上是关于Sentinel 流量防卫兵-SpringCloudAlibaba No.4的主要内容,如果未能解决你的问题,请参考以下文章

Sentinel分布式系统的流量防卫兵

Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵基础实战

Sentinel 流量防卫兵-SpringCloudAlibaba No.4

#私藏项目实操分享#Alibaba中间件技术系列「Sentinel技术专题」分布式系统的流量防卫兵的基本介绍(入门源码介绍)

SpringCloud实战(十九)-Sentinel 流量控制

SpringCloud实战(十九)-Sentinel 流量控制