• Sentinel: 使用注解限流


    在前面我们对Sentinel做了一个详细的介绍,可以手动的通过Sentinel提供的SphU类来保护资源。这种做法不好的地方在于每个需要限制的地方都得写代码,从 0.1.1 版本开始,Sentinel 提供了 @SentinelResource 注解的方式,非常方便。

    要使用注解来保护资源需要引入下面的Maven依赖:

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

    引入之后我们需要配置SentinelResourceAspect切面让其生效,因为是通过SentinelResourceAspect切面来实现的,我这边以Spring Boot中使用进行配置示列:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
    
    @Configuration
    public class AopConfiguration {
    
        @Bean
        public SentinelResourceAspect sentinelResourceAspect() {
            return new SentinelResourceAspect();
        }
        
    }
    
    

    然后在需要限制的方法上加SentinelResource注解即可:

    @SentinelResource(value = "get", blockHandler = "exceptionHandler")
    @Override
    public String get(String id) {
       return "http://cxytiandi.com";
    }
    
    public String exceptionHandler(String id, BlockException e) {
       e.printStackTrace();
       return "错误发生在" + id;
    }
    

    SentinelResource:value

    表示资源名,必填项

    SentinelResource:blockHandler

    处理 BlockException 的方法名,可选项。若未配置,则将 BlockException 直接抛出。

    • blockHandler 函数访问范围需要是 public
    • 返回类型需要与原方法相匹配
    • 参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
    • blockHandler 函数默认需要和原方法在同一个类中

    如果你不想让异常处理方法跟业务方法在同一个类中,可以使用 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

    业务方法:

    @SentinelResource(value = "get2", blockHandler = "handleException", blockHandlerClass = { ExceptionUtil.class })
    @Override
    public String get2() {
    	return "http://cxytiandi.com";
    }
    

    异常处理类:

    import com.alibaba.csp.sentinel.slots.block.BlockException;
    
    public final class ExceptionUtil {
    
        public static String handleException(BlockException ex) {
            System.err.println("错误发生: " + ex.getClass().getCanonicalName());
            return "error";
        }
        
    }
    

    如何测试?

    我们可以在Spring Boot的启动类中定义规则,然后快速访问接口,就可以看出效果啦,或者用压力测试工具ab等。

    @SpringBootApplication
    public class App {
    	public static void main(String[] args) {
    		initFlowRules();
    		SpringApplication.run(App.class, args);
    	}
    	
    	private static void initFlowRules() {
    		List<FlowRule> rules = new ArrayList<>();
    		
    		FlowRule rule = new FlowRule();
    		rule.setResource("get");
    		rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    		rule.setCount(1);
    		rules.add(rule);
    		
    		rule = new FlowRule();
    		rule.setResource("get2");
    		rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    		rule.setCount(1);
    		rules.add(rule);
    		
    		FlowRuleManager.loadRules(rules);
    	}
    }
    

    源码分析

    只需要配置了SentinelResourceAspect就可以使用注解,我们来简单的看下SentinelResourceAspect的源码

    @Aspect
    public class SentinelResourceAspect extends AbstractSentinelAspectSupport {
    
        @Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
        public void sentinelResourceAnnotationPointcut() {
        }
    
        @Around("sentinelResourceAnnotationPointcut()")
        public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
            // 获取当前访问的方法
            Method originMethod = resolveMethod(pjp);
            // 获取方法上的SentinelResource注解
            SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
            if (annotation == null) {
                // Should not go through here.
                throw new IllegalStateException("Wrong state for SentinelResource annotation");
            }
            // 获取资源名
            String resourceName = getResourceName(annotation.value(), originMethod);
            EntryType entryType = annotation.entryType();
            Entry entry = null;
            try {
                entry = SphU.entry(resourceName, entryType, 1, pjp.getArgs());
                Object result = pjp.proceed();
                return result;
            } catch (BlockException ex) {
                // 处理被限制的异常,回调事先配置的异常处理方法
                return handleBlockException(pjp, annotation, ex);
            } catch (Throwable ex) {
                Tracer.trace(ex);
                throw ex;
            } finally {
                if (entry != null) {
                    entry.exit();
                }
            }
        }
    }
    

    上面是整个切面的代码,对所有加了SentinelResource注解的方法进去切入。细节代码在AbstractSentinelAspectSupport中,大家自己去看看。

    欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(http://cxytiandi.com/course)

    PS:目前星球中正在星主的带领下组队学习Sentinel,等你哦!

    微信扫码加入猿天地知识星球

    猿天地

  • 相关阅读:
    hdu 1381 Crazy Search
    hdu 5131 Song Jiang's rank list
    poj 2251 Dungeon Master
    hdu 4941 Magical Forest
    hdu 1728 逃离迷宫
    hdu 2612 Find a way
    hdu 3288 Resource Allocation
    hdu 1272 小希的迷宫
    hdu 5224 Tom and paper
    hdu 5104 Primes Problem
  • 原文地址:https://www.cnblogs.com/yinjihuan/p/10516045.html
Copyright © 2020-2023  润新知