• sentinel源码分析-资源


    1、sentinel中如何创建资源?

    方式一:主流框架的默认适配

    为了减少开发的复杂程度,sentinel 对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了适配。只需要引入对应的依赖即可方便地整合 Sentinel。针对不同框架,默认的资源统计会有所区别。

    Web Servlet

    所有访问的 Web URL 就会被自动统计为 Sentinel 的资源。

    Dubbo

    限流粒度可以是服务接口和服务方法两种粒度:

    • 服务接口:资源为 接口全限定名,如 com.alibaba.csp.sentinel.demo.dubbo.FooService
    • 服务方法:资源为 接口全限定名:方法签名,如 com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)

    方式二:抛出异常的方式定义资源

    SphU 包含了 try-catch 风格的 API。用这种方式,当资源发生了限流之后会抛出 BlockException。这个时候可以捕捉异常,进行限流之后的逻辑处理。示例代码如下:

    // 1.5.0 版本开始可以利用 try-with-resources 特性
    // 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
    try (Entry entry = SphU.entry("resourceName")) {
      // 被保护的业务逻辑// do something here...
    } catch (BlockException ex) {
      // 资源访问阻止,被限流或被降级// 在此处进行相应的处理操作
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    特别地,若 entry 的时候传入了热点参数,那么 exit 的时候也一定要带上对应的参数(exit(count, args)),否则可能会有统计错误。这个时候不能使用 try-with-resources 的方式。另外通过 Tracer.trace(ex) 来统计异常信息时,由于 try-with-resources 语法中 catch 调用顺序的问题,会导致无法正确统计异常数,因此统计异常信息时也不能在 try-with-resources 的 catch 块中调用 Tracer.trace(ex)。

    1.5.0 之前的版本的示例:

    Entry entry = null;
    // 务必保证finally会被执行
    try {
      // 资源名可使用任意有业务语义的字符串
      entry = SphU.entry("自定义资源名");
      // 被保护的业务逻辑
      // do something...
    } catch (BlockException e1) {
      // 资源访问阻止,被限流或被降级
      // 进行相应的处理操作
    } finally {
      if (entry != null) {
        entry.exit();
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意: SphU.entry(xxx) 需要与 entry.exit() 方法成对出现,匹配调用,否则会导致调用链记录异常,抛出 ErrorEntryFreeException 异常。

    方式三:返回布尔值方式定义资源

    SphO 提供 if-else 风格的 API。用这种方式,当资源发生了限流之后会返回 false,这个时候可以根据返回值,进行限流之后的逻辑处理。示例代码如下:

      // 资源名可使用任意有业务语义的字符串
      if (SphO.entry("自定义资源名")) {
        // 务必保证finally会被执行try {
          /**
          * 被保护的业务逻辑
          */
        } finally {
          SphO.exit();
        }
      } else {
        // 资源访问阻止,被限流或被降级// 进行相应的处理操作
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    方式四:注解方式定义资源

    Sentinel 支持通过 @SentinelResource 注解定义资源并配置 blockHandler 和 fallback 函数来进行限流之后的处理。示例:

    // 原本的业务方法.
    @SentinelResource(blockHandler = "blockHandlerForGetUser")
    public User getUserById(String id) {
        throw new RuntimeException("getUserById command failed");
    }
    
    // blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
    public User blockHandlerForGetUser(String id, BlockException ex) {
        return new User("admin");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意 blockHandler 函数会在原方法被限流/降级/系统保护的时候调用,而 fallback 函数会针对所有类型的异常。请注意 blockHandler 和 fallback 函数的形式要求。

    方式五:异步调用支持

    Sentinel 支持异步调用链路的统计。在异步调用中,需要通过 SphU.asyncEntry(xxx) 方法定义资源,并通常需要在异步的回调函数中调用 exit 方法。以下是一个简单的示例:

    try {
        AsyncEntry entry = SphU.asyncEntry(resourceName);
    
        // 异步调用.
        doAsync(userId, result -> {
            try {
                // 在此处处理异步调用的结果.
            } finally {
                // 在回调结束后 exit.
                entry.exit();
            }
        });
    } catch (BlockException ex) {
        // Request blocked.// Handle the exception (e.g. retry or fallback).
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2、主流框架的资源是怎么默认创建?

    Dubbo:

    1、添加依赖
    sentinel-apache-dubbo3-adapter(兼容 Apache Dubbo 3.0.5 及以上版本,自 Sentinel 1.8.5 开始支持)
    sentinel-apache-dubbo-adapter(兼容 Apache Dubbo 2.7.x 及以上版本,自 Sentinel 1.5.1 开始支持) 
    sentinel-dubbo-adapter(兼容 Dubbo 2.6.x 版本)
    
    • 1
    • 2
    • 3

    SentinelDubboConsumerFilter类

    在这里插入图片描述
    可以看出,这种写法和方法二中1.5.0之前的写法是一样的。

    Web Servlet:

    1、添加依赖
    sentinel-web-servlet
    
    • 1

    看清这段代码需要的前置知识:SpringMVC中的Interceptor拦截器是链式的,可以同时存在多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行。所有的Interceptor中的preHandle方法都会在Controller方法调用之前调用。afterCompletion该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,也就是调用了controller方法中的业务代码后执行的。这个方法的主要作用是用于清理资源的。

    从源码中可以看出,SpringMVC在创建sentinel的资源是在preHandle这个方法中,清除是在afterCompletion方法中。

    AbstractSentinelInterceptor类

    在这里插入图片描述
    在这里插入图片描述

    3、为什么清除sentinel资源是在调用方法之后?

    在这里插入图片描述
    1、这里对业务异常进行添加。
    还记得sentinel中有针对异常数量的熔断机制,就是在这里添加业务异常,而sentinel的规则异常则在preHandle进行了捕获以及处理。
    2、这里为什么要对entry进行退出呢?
    因为sentinel在处理流程时是会用到一些使用了static修饰的map、list等集合,在entry退出时需要将这些数据清空,不然会造成内存溢出。

    4、还有那些主流框架有适配?

    可以从源码中查看以下框架都是有做适配的(截至1.8.5版本)
    在这里插入图片描述

    5、sentinel中资源都存在哪里?

    sentinel中的资源都存在NodeSelectorSlot类中,
    1、使用了双重校验的单例模式
    2、为什么hashmap要重新创建,而不是直接新增呢?
    个人觉得是节省内存,用多少就使用多少容量。因为hashmap的扩容是两倍扩容,所以越到后面,扩容的容量就越大,造成空闲内存的几率就越大。
    在这里插入图片描述

  • 相关阅读:
    长胜证券:黄金“狂飙”,饰品克价破600元,期货价格创十余年新高
    allegro画完封装怎么老报错保存不了呢
    读《文明之光》第2册总结
    写给刚入学大数据专业或迷茫在为几两碎银转行的你
    搜维尔科技:Touch触觉式力反馈设备与Touch X力反馈设备对比分析
    贷款问题——C语言
    怎么将ruoyi源代码与新业务代码分开写
    代码随想录算法公开课!
    .NET周刊【1月第1期 2024-01-07】
    segment方案解决VXLAN分布式网关DCI间互联
  • 原文地址:https://blog.csdn.net/qq_41434612/article/details/126438228