基于Sentinel+Nacos 对Feign Client 动态添加默认熔断规则

随着业务的发展,微服务势在必行;最近接手一个老项目使用sentinel+nacos做的熔断限流,熔断规则配置的都一样,感觉有点冗余,开发效率低下。想着改造升级一下,由于服务依赖接口都是通过feignClient调用的,对feignClient调用接口,自动扫描动态实现熔断规则。

一、首页扫描feignClient依赖接口,动态生成熔断规则,推送nacos
在这里插入图片描述

  1. 实现ApplicationRunner接口,重写run方法,根据EnableFeignClients指定包加载feignClient类集合

       @Override
       public void run(ApplicationArguments args) {
           Class<?> mainClass = deduceMainApplicationClass();
           log.info("开始加载默认规则,mainClass:{}", mainClass);
           if (mainClass == null) {
               throw new RuntimeException("can not fount main class");
           }
           EnableFeignClients enableFeignClientsAnnotation = mainClass.getAnnotation(EnableFeignClients.class);
           if (enableFeignClientsAnnotation != null) {
               String[] feignClientPackages;
               String[] feignClientDeclaredPackages = enableFeignClientsAnnotation.basePackages();
               //声明了feignClient的包名
               if (feignClientDeclaredPackages.length == 0) {
                   feignClientPackages = new String[]{mainClass.getPackage().getName()};
               } else {
                   feignClientPackages = feignClientDeclaredPackages;
               }
               //初始化降级规则
               initDeGradeRule(feignClientPackages);
           }
           log.info("默认降级规则处理完成");
       }
    
  2. 根据feignClient类集合,初始化熔断规则,并推送nacos

        private void initDeGradeRule(String[] feignClientPackages) {
            List<DegradeRule> localDegradeRuleList = new ArrayList<>();
            Set<Class> feignClientClass = getFeignClientClass(feignClientPackages);
            for (Class clientClass : feignClientClass) {
                List<DegradeRule> rules = initRules(clientClass);
                localDegradeRuleList.addAll(rules);
            }
            List<DegradeRule> remoteDegradeRuleList = fetchRemoteRules();
            //远程nacos没有规则,那就直接利用本地规则
            if (remoteDegradeRuleList == null || remoteDegradeRuleList.isEmpty()) {
                pushRules(localDegradeRuleList);
                return;
            }
            //本地规则 合并 远程规则策略
            proess(localDegradeRuleList, remoteDegradeRuleList);
            //推送本地规则,到nacos
            pushRules(localDegradeRuleList);
        }
    
  3. 获取FeignClient class set

        private Set<Class> getFeignClientClass(String[] packageNames) {
            ClassScanner classScanner = new ClassScanner();
            Set<Class> feignClientClass = new HashSet<>();
            for (String packageName : packageNames) {
                feignClientClass.addAll(classScanner.scan(packageName, FeignClient.class));
            }
            return feignClientClass;
        }
    
  4. 根据feignClient class对象,提取请求映射

        private final static String HTTP_PROTOCOL_PREFIX = "http://";
        private final static String ANNOTATION_VALUE_PREFIX = "${";
        private final static String ANNOTATION_VALUE_SUFFIX = "}";    
    public List<DegradeRule> initRules(Class cla) {
            List<DegradeRule> degradeRuleList = new ArrayList<>();
            FeignClient feignClient = (FeignClient) cla.getAnnotation(FeignClient.class);
            String classRequestMappingUrl = "";
            RequestMapping classRequestMapping = (RequestMapping) cla.getAnnotation(RequestMapping.class);
            if (null != classRequestMapping) {
                classRequestMappingUrl = classRequestMapping.value()[0];
            }
            String serviceName = feignClient.name();
            if (serviceName.startsWith(ANNOTATION_VALUE_PREFIX) && serviceName.endsWith(ANNOTATION_VALUE_SUFFIX)) {
                serviceName = this.environment.resolvePlaceholders(serviceName);
            }
            Method[] methods = cla.getDeclaredMethods();
            for (Method method : methods) {
                degradeRuleList.add(buildDegradeRule(getResourceName(classRequestMappingUrl, serviceName, method)));
            }
            DegradeRuleManager.loadRules(degradeRuleList);
            return degradeRuleList;
    
  5. 根据请求映射,拼接资源标识

        private String getResourceName(String crmu, String serviceName, Method method) {
            crmu = crmu.startsWith("/") ? crmu : "/" + crmu;
            String resourceName = "";
            RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
            if (null != methodRequestMapping) {
                String mrm = methodRequestMapping.value()[0];
                resourceName = HTTP_PROTOCOL_PREFIX + serviceName + crmu + (mrm.startsWith("/") ? mrm : "/" + mrm);
            }
            PostMapping methodPostMapping = method.getAnnotation(PostMapping.class);
            if (null != methodPostMapping) {
                String mpm = methodPostMapping.value()[0];
                resourceName = HTTP_PROTOCOL_PREFIX + serviceName + crmu + (mpm.startsWith("/") ? mpm : "/" + mpm);
            }
            return resourceName;
        }
    
  6. 使用资源标识,构建默认熔断规则

        private DegradeRule buildDegradeRule(String resourceName) {
            DegradeRule rule = new DegradeRule();
            //设置资源名
            rule.setResource(resourceName);
            //设置降级规则 TR 10 ms
            rule.setCount(200);
            // 规则类型 RT
            rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
            // 窗口时间
            rule.setTimeWindow(10);
            rule.setMinRequestAmount(3);
            rule.setStatIntervalMs(30000);
            rule.setSlowRatioThreshold(0.6);
            return rule;
        }
    
  7. 获取远程nacos配置

        private List<DegradeRule> fetchRemoteRules() {
            return JSONObject.parseArray(nacosService.getConfig(dataId, groupId), DegradeRule.class);
        }
    
  8. 本地规则 合并 远程规则策略

        private void proess(List<DegradeRule> localDegradeRuleList, List<DegradeRule> remoteDegradeRuleList) {
            for (DegradeRule rule : remoteDegradeRuleList) {
                if (localDegradeRuleList.contains(rule)) {
                    DegradeRule ldr = localDegradeRuleList.get(localDegradeRuleList.indexOf(rule));
                    if (ldr.equals(rule)) {
                        continue;
                    }
                    localDegradeRuleList.remove(ldr);
                    localDegradeRuleList.add(rule);
                } else {
                    localDegradeRuleList.add(rule);
                }
            }
        }
    
    
  9. 推送熔断规则到nacos

        private void pushRules(List<DegradeRule> localDegradeRuleList) {
            SerializeConfig serializeConfig = new SerializeConfig();
            serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.CamelCase;
            String contentStr = JSON.toJSONString(localDegradeRuleList, serializeConfig, SerializerFeature.PrettyFormat);
            nacosService.publish(dataId, groupId, contentStr, ConfigType.JSON.getType());
        }
    

二、使用AOP切feign调用,动态增加熔断逻辑执行
在这里插入图片描述
10. 参考Sentinel的 SentinelResourceAspect 继承AbstractSentinelAspectSupport 实现feign调用拦截

@Aspect
@Slf4j
@Component
public class FeignMethodAspect extends AbstractSentinelAspectSupport {

    @Around("within(feign.Client+)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        log.info("within(feign.Client+) pjp {}, args:{}", pjp, pjp.getArgs());
        String resourceName = pjp.getArgs()[0].toString().split(" ")[1];
        Entry entry = null;
        try {
            Object var18;
            try {
                entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON, EntryType.OUT, pjp.getArgs());
                Object result = pjp.proceed();
                var18 = result;
                return var18;
            } catch (BlockException var15) {
                String resource = var15.getRule().getResource();
                log.error("FeignClient接口触发了resource:{}规则,接口已经被“限流/熔断/降级”,业务系统返回默认null值,异常信息:{}", resource, var15);
                return null;
            } catch (Throwable var16) {
                this.traceException(var16);
            }
        } finally {
            if (entry != null) {
                entry.exit(1, pjp.getArgs());
            }
        }
        return null;
    }
}
  1. 配置使用JDK动态代理,否则拦截feignClient 调用会失败
spring:
  aop:
    proxy-target-class: false
  1. 推送nacosService 依赖NacosConfigManager
    public Boolean publish(String dataId, String group, String content, String type) {
        try {
            return nacosConfigManager.getConfigService().publishConfig(dataId, group, content, type);
        } catch (NacosException e) {
            log.error("NacosService publish e:{}", e);
        }
        return false;
    }
  1. 获取nacos配置
    public String getConfig(String dataId, String group) {
        try {
            return nacosConfigManager.getConfigService().getConfig(dataId, group, 10000l);
        } catch (NacosException e) {
            log.error("NacosService publish e:{}", e);
        }
        return Strings.EMPTY;
    }

见字如面,感谢您的耐心阅读,希望对您有所帮助,点赞是一种美德,祝您工作顺利 步步高升!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值