当前位置:网站首页>Dynamically add default fusing rules to feign client based on sentinel + Nacos

Dynamically add default fusing rules to feign client based on sentinel + Nacos

2022-04-23 18:10:00 dawnsun001

As the business grows , Micro service is imperative ; Recently took over an old project to use sentinel+nacos Fusing and current limiting , The fuse rules are configured the same , I feel a little redundant , Inefficient development . Think about upgrading , Because the service depends on the interface, it is through feignClient Called , Yes feignClient Call interface , Automatic scanning and dynamic realization of fuse rules .

One 、 Home page scanning feignClient Dependent interface , Dynamically generate fusing rules , push nacos
 Insert picture description here

  1. Realization ApplicationRunner Interface , rewrite run Method , according to EnableFeignClients Specify the package to load feignClient Class collection

       @Override
       public void run(ApplicationArguments args) {
          
           Class<?> mainClass = deduceMainApplicationClass();
           log.info(" Start loading default rules ,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();
               // The statement feignClient The package name 
               if (feignClientDeclaredPackages.length == 0) {
          
                   feignClientPackages = new String[]{
          mainClass.getPackage().getName()};
               } else {
          
                   feignClientPackages = feignClientDeclaredPackages;
               }
               // Initialize demotion rule 
               initDeGradeRule(feignClientPackages);
           }
           log.info(" The default degradation rule processing is completed ");
       }
    
  2. according to feignClient Class collection , Initialize the fusing rule , And push 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();
            // long-range nacos There are no rules , Then use local rules directly 
            if (remoteDegradeRuleList == null || remoteDegradeRuleList.isEmpty()) {
          
                pushRules(localDegradeRuleList);
                return;
            }
            // Local rules   Merge   Remote rule policy 
            proess(localDegradeRuleList, remoteDegradeRuleList);
            // Push local rules , To nacos
            pushRules(localDegradeRuleList);
        }
    
  3. obtain 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. according to feignClient class object , Extract request mapping

        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. Map on request , Splice resource identification

        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. Use resource identification , Build default rules

        private DegradeRule buildDegradeRule(String resourceName) {
          
            DegradeRule rule = new DegradeRule();
            // Set resource name 
            rule.setResource(resourceName);
            // Set degradation rules  TR 10 ms
            rule.setCount(200);
            //  Type of rule  RT
            rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
            //  Window time 
            rule.setTimeWindow(10);
            rule.setMinRequestAmount(3);
            rule.setStatIntervalMs(30000);
            rule.setSlowRatioThreshold(0.6);
            return rule;
        }
    
  7. Get remote nacos To configure

        private List<DegradeRule> fetchRemoteRules() {
            return JSONObject.parseArray(nacosService.getConfig(dataId, groupId), DegradeRule.class);
        }
    
  8. Local rules Merge Remote rule policy

        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. Push the fusing rule to 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());
        }
    

Two 、 Use AOP cut feign call , Dynamically add fuse logic execution
 Insert picture description here
10. Reference resources Sentinel Of SentinelResourceAspect Inherit AbstractSentinelAspectSupport Realization feign Call interception

@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 Interface triggers resource:{} The rules , The interface has been “ Current limiting / Fuse / Downgrade ”, The business system returns the default null value , Abnormal information :{}", resource, var15);
                return null;
            } catch (Throwable var16) {
    
                this.traceException(var16);
            }
        } finally {
    
            if (entry != null) {
    
                entry.exit(1, pjp.getArgs());
            }
        }
        return null;
    }
}
  1. Configuration and use JDK A dynamic proxy , Otherwise intercept feignClient The call fails
spring:
  aop:
    proxy-target-class: false
  1. push nacosService rely on 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. obtain nacos To configure
    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;
    }

Seeing your letters is like seeing you in person. , Thank you for your patience in reading , I hope it will be of some help to you , Praise is a virtue , I wish you every success in your work Promoting to a higher position !

版权声明
本文为[dawnsun001]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230544289080.html