美文网首页
Feign接口基于Sentinel配置默认熔断规则

Feign接口基于Sentinel配置默认熔断规则

作者: 初心myp | 来源:发表于2022-09-27 18:40 被阅读0次

    概述:基于sentinel配置熔断规则是针对每个接口的,如果feign接口配置规则,使用手动方式在sentinel控制台中配置,那是一件不现实的工作,所以针对feign接口需要配置默认的熔断规则,同时会将初始化的熔断规则存储到nacos中
    涉及到的核心功能类:FeignSentinelSupportConfig.java,自定义注解类:EnableFeignDegrade.java,工具类ClassScanner.java,SpringBeanUtil.java
    注:本文基于nacos作为注册中心及配置中心,sentinel熔断服务,feign远程调用服务!!!
    接下来上代码

    1.需要的pom依赖

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
            </dependency>
            <!--feign远程接口调用-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-loadbalancer</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.netflix.feign</groupId>
                <artifactId>feign-okhttp</artifactId>
                <version>8.18.0</version>
            </dependency>
            <dependency>
                <groupId>io.github.openfeign.form</groupId>
                <artifactId>feign-form</artifactId>
                <version>3.8.0</version>
            </dependency>
            <dependency>
                <groupId>io.github.openfeign.form</groupId>
                <artifactId>feign-form-spring</artifactId>
                <version>3.8.0</version>
            </dependency>
    

    2.主要功能类

    FeignSentinelSupportConfig.java默认配置规则类

    import com.alibaba.cloud.nacos.NacosConfigManager;
    import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
    import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
    import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
    import com.alibaba.csp.sentinel.util.StringUtil;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.PropertyNamingStrategy;
    import com.alibaba.fastjson.serializer.SerializeConfig;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    import com.alibaba.nacos.api.config.ConfigType;
    import com.alibaba.nacos.api.exception.NacosException;
    import com.sinochem.it.feign.annotation.EnableFeignDegrade;
    import com.sinochem.it.feign.utils.ClassScanner;
    import com.sinochem.it.feign.utils.SpringBeanUtil;
    import com.sinochem.it.sentinel.constant.CommonConstant;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.core.env.Environment;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Set;
    
    @Component
    public class FeignSentinelSupportConfig implements ApplicationRunner, EnvironmentAware {
    
        private static final Logger logger = LoggerFactory.getLogger(FeignSentinelSupportConfig.class);
    
        private final static String HTTP_PROTOCOL_PREFIX = "http://";
        private final static String ANNOTATION_VALUE_PREFIX = "${";
        private final static String ANNOTATION_VALUE_SUFFIX = "}";
        private Environment environment;//环境变量对象
    
        @Value("${spring.cloud.nacos.config.enabled:}")
        private Boolean nacosConfigEnable;
    
        @Value("${spring.application.name:}")
        private String appName;
    
        private Class<?> deduceMainApplicationClass() {
            try {
                StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                for (StackTraceElement stackTraceElement : stackTrace) {
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            } catch (ClassNotFoundException ex) {
    
            }
            return null;
        }
    
        @Override
        public void run(ApplicationArguments args) {
            Class<?> mainClass = deduceMainApplicationClass();
            logger.info("开始加载默认规则,mainClass:{}", mainClass);
            if (mainClass == null) {
                throw new RuntimeException("can not fount main class");
            }
            // 添加开关 如果不使用nacos配置中心,则直接return,不初始化默认的熔断规则
            if (nacosConfigEnable != null && !nacosConfigEnable) {
                logger.info("nacos配置中心关闭,加载默认规则结束");
                return;
            }
            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);
            }
            logger.info("默认降级规则处理完成");
        }
    
        private Set<Class> getFeignClientClass(String[] packageNames) {
            ClassScanner classScanner = new ClassScanner();
            Set<Class> feignClientClass = classScanner.scan(packageNames, FeignClient.class);
            return feignClientClass;
        }
    
        /**
         * 创建熔断规则列表数据
         * 如果全部feign接口设置默认策略太多,则可以通过自定义注解,标记哪些feign接口类需要设置
         *
         * @param cla feign接口类
         * @return
         */
        public List<DegradeRule> initRules(Class cla) {
            List<DegradeRule> degradeRuleList = new ArrayList<>();
            // 在该处获取自定义注解,来判断是否添加默认熔断规则  如果feign接口类上添加了@EnableFeignDegrade注解并且enable为false,则不为该类中接口添加默认熔断规则
            EnableFeignDegrade feignDegrade = (EnableFeignDegrade) cla.getAnnotation(EnableFeignDegrade.class);
            if (null != feignDegrade && !feignDegrade.enable()) {
                logger.info("{}类关闭了初始化默认熔断规则功能", cla.getName());
                return degradeRuleList;
            }
    
            FeignClient feignClient = (FeignClient) cla.getAnnotation(FeignClient.class);
            // 获取RequestMapping注解是否配置基础路径
            String classRequestMappingUrl = "";
            RequestMapping classRequestMapping = (RequestMapping) cla.getAnnotation(RequestMapping.class);
            if (null != classRequestMapping) {
                classRequestMappingUrl = classRequestMapping.value()[0];
            }
            // 这里区分四种情况
            // 1.@FeignClient(name = "demo01", url = "http://127.0.0.1:36001"
            // 2.@FeignClient(name = "demo01", url = "${base.url}"
            // 3.@FeignClient(name = "demo01"
            // 4.@FeignClient(name = "${demo01.serviceName}"
            // 标识是否配置url属性,拼接http://前缀时判断使用
            Boolean httpFlag = true;
            String baseUrl = null;
            String serviceUrl = feignClient.url();
            String serviceName = feignClient.name();
            // 如果url属性不为空并且${开头 }结尾,说明是动态配置的url
            if (StringUtil.isNotBlank(serviceUrl) && serviceUrl.startsWith(ANNOTATION_VALUE_PREFIX) && serviceUrl.endsWith(ANNOTATION_VALUE_SUFFIX)) {
                baseUrl = environment.resolvePlaceholders(serviceUrl);
            } else if (StringUtil.isNotBlank(serviceUrl)) {
                // 如果http路径最后一位是/ 则去掉斜杠
                baseUrl = !serviceUrl.endsWith("/") ? serviceUrl : serviceUrl.substring(0, serviceUrl.length() - 1);
            } else if (StringUtil.isBlank(serviceUrl) && serviceName.startsWith(ANNOTATION_VALUE_PREFIX) && serviceName.endsWith(ANNOTATION_VALUE_SUFFIX)) {
                baseUrl = environment.resolvePlaceholders(serviceName);
                httpFlag = false;
            } else {
                baseUrl = serviceName;
                httpFlag = false;
            }
    
            Method[] methods = cla.getDeclaredMethods();
            for (Method method : methods) {
                degradeRuleList.add(buildDegradeRule(getResourceName(classRequestMappingUrl, baseUrl, method, httpFlag)));
            }
            DegradeRuleManager.loadRules(degradeRuleList);
            return degradeRuleList;
    
        }
    
        /**
         * 初始化熔断规则
         *
         * @param feignClientPackages
         */
        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);
            }
    
            NacosConfigManager nacosConfigManager = SpringBeanUtil.getBean("nacosConfigManager", NacosConfigManager.class);
            List<DegradeRule> remoteDegradeRuleList = this.fetchRemoteRules(nacosConfigManager);
            //远程nacos没有规则,那就直接利用本地规则
            if (remoteDegradeRuleList == null || remoteDegradeRuleList.isEmpty()) {
                this.pushRules(nacosConfigManager, localDegradeRuleList);
                return;
            }
            //本地规则 合并 远程规则策略
            this.proess(localDegradeRuleList, remoteDegradeRuleList);
            //推送本地规则,到nacos
            this.pushRules(nacosConfigManager, localDegradeRuleList);
        }
    
        /***
         * 组装resourceName
         * @param crmu requestMapping路径
         * @param serviceName 服务名称或url
         * @param method 方法
         * @param httpFlag http标记
         * @return
         */
        private String getResourceName(String crmu, String serviceName, Method method, Boolean httpFlag) {
    //        crmu = crmu.startsWith("/") ? crmu : "/" + crmu;
            String resourceName = "";
            RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
            if (null != methodRequestMapping) {
                String mrm = methodRequestMapping.value()[0];
                // 获取@RequestMapping中的method参数,如果未配置,则默认为GET请求
                RequestMethod mMethod = null;
                if (methodRequestMapping.method().length > 0) {
                    mMethod = methodRequestMapping.method()[0];
                }
                // @RequestMapping 注解不指定method参数,则默认为GET请求
                if (!httpFlag) {
                    if (mMethod != null) {
                        resourceName = mMethod + ":" + HTTP_PROTOCOL_PREFIX + serviceName + crmu + (mrm.startsWith("/") ? mrm : "/" + mrm);
                    } else {
                        resourceName = "GET:" + HTTP_PROTOCOL_PREFIX + serviceName + crmu + (mrm.startsWith("/") ? mrm : "/" + mrm);
                    }
                } else {
                    if (mMethod != null) {
                        resourceName = mMethod + ":" + serviceName + crmu + (mrm.startsWith("/") ? mrm : "/" + mrm);
                    } else {
                        resourceName = "GET:" + serviceName + crmu + (mrm.startsWith("/") ? mrm : "/" + mrm);
                    }
                }
            }
            PostMapping methodPostMapping = method.getAnnotation(PostMapping.class);
            if (null != methodPostMapping) {
                String mpm = methodPostMapping.value()[0];
                // 未配置url属性,说明使用服务名进行服务之间调用,所以需要拼接http://  拼接结果:http://serviceName/demo/get  前面拼接的请求方式(POST:)是因为sentinel需要 不拼接熔断规则不生效
                if (!httpFlag) {
                    resourceName = "POST:" + HTTP_PROTOCOL_PREFIX + serviceName + crmu + (mpm.startsWith("/") ? mpm : "/" + mpm);
                } else { // 配置url属性,说明使用指定url调用 则不需要单独拼接http://  拼接结果为:http://127.0.0.1:8080/demo/get  前面拼接的请求方式(POST:)是因为sentinel需要 不拼接熔断规则不生效
                    resourceName = "POST:" + serviceName + crmu + (mpm.startsWith("/") ? mpm : "/" + mpm);
                }
            }
            GetMapping methodGetMapping = method.getAnnotation(GetMapping.class);
            if (null != methodGetMapping) {
                String mgm = methodGetMapping.value()[0];
                if (!httpFlag) {
                    resourceName = "GET:" + HTTP_PROTOCOL_PREFIX + serviceName + crmu + (mgm.startsWith("/") ? mgm : "/" + mgm);
                } else {
                    resourceName = "GET:" + serviceName + crmu + (mgm.startsWith("/") ? mgm : "/" + mgm);
                }
            }
            return resourceName;
        }
    
        /***
         * 创建默认熔断规则
         * @param resourceName
         * @return
         */
        private DegradeRule buildDegradeRule(String resourceName) {
            DegradeRule rule = new DegradeRule(resourceName)
                    // 熔断策略
                    .setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
                    // Max allowed response time  RT 慢调用标准值 接口响应时长超过2秒则被判定为慢调用
                    .setCount(2000)
                    // Retry timeout (in second)   窗口期  熔断时长
                    .setTimeWindow(30)
                    // Circuit breaker opens when slow request ratio > 80%  慢调用比例   判定是否熔断的条件之一
                    .setSlowRatioThreshold(0.8)
                    // 单位时长最小请求数   判定是否熔断的条件之一
                    .setMinRequestAmount(30)
                    // 统计时长也叫单位时长
                    .setStatIntervalMs(60000);
            return rule;
        }
    
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
        /************************************ nacos config ********************************************************/
    
        public String getConfig(NacosConfigManager nacosConfigManager, String dataId, String group) {
            try {
                return nacosConfigManager.getConfigService().getConfig(dataId, group, 10000l);
            } catch (NacosException e) {
                logger.error("NacosService publish e:{}", e);
            }
            return null;
        }
    
    
        private Boolean publish(NacosConfigManager nacosConfigManager, String dataId, String group, String content, String type) {
            try {
                return nacosConfigManager.getConfigService().publishConfig(dataId, group, content, type);
            } catch (NacosException e) {
                logger.error("NacosService publish e:{}", e);
            }
            return false;
        }
    
        public void pushRules(NacosConfigManager nacosConfigManager, List<DegradeRule> localDegradeRuleList) {
    
    //        String appName = (String) getAttrBootstrapYml("spring.application.name");
    //        if (StringUtil.isBlank(appName)) {
    //            appName = (String) getAttrBootstrapEnvYml("spring.application.name");
    //        }
            String dataId = appName + CommonConstant.DEGRADE_DATA_ID_POSTFIX;
            SerializeConfig serializeConfig = new SerializeConfig();
            serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.CamelCase;
            String contentStr = JSON.toJSONString(localDegradeRuleList, serializeConfig, SerializerFeature.PrettyFormat);
            publish(nacosConfigManager, dataId, CommonConstant.GROUP_ID, contentStr, ConfigType.JSON.getType());
        }
    
    
        public 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);
                }
            }
        }
    
    
        public List<DegradeRule> fetchRemoteRules(NacosConfigManager nacosConfigManager) {
    //        String appName = (String) getAttrBootstrapYml("spring.application.name");
    //        if (StringUtil.isBlank(appName)) {
    //            appName = (String) getAttrBootstrapEnvYml("spring.application.name");
    //        }
            String dataId = appName + CommonConstant.DEGRADE_DATA_ID_POSTFIX;
            return JSONObject.parseArray(this.getConfig(nacosConfigManager, dataId, CommonConstant.GROUP_ID), DegradeRule.class);
        }
    }
    
    

    EnableFeignDegrade.java自定义注解类

    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface EnableFeignDegrade {
    
    
        /**
         * 是否启用feign初始化熔断规则功能 默认使用
         *
         * @return
         */
        boolean enable() default true;
    
    }
    
    

    DefaultRuleNacosManager.java操作nacos持久化类 优化之后已经合并到FeignSentinelSupportConfig类中,如果分开两个类,启动服务注入时会出现加载问题

    import com.alibaba.cloud.nacos.NacosConfigManager;
    import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.PropertyNamingStrategy;
    import com.alibaba.fastjson.serializer.SerializeConfig;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    import com.alibaba.nacos.api.config.ConfigType;
    import com.alibaba.nacos.api.exception.NacosException;
    import com.sinochem.it.sentinel.constant.CommonConstant;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    @Component
    public class DefaultRuleNacosManager {
    
        private static final Logger logger = LoggerFactory.getLogger(DefaultRuleNacosManager.class);
    
        @Autowired
        private NacosConfigManager nacosConfigManager;
    
        @Value("${spring.application.name}")
        private String appName;
    
    
        public String getConfig(String dataId, String group) {
            try {
                return nacosConfigManager.getConfigService().getConfig(dataId, group, 10000l);
            } catch (NacosException e) {
                logger.error("NacosService publish e:{}", e);
            }
            return null;
        }
    
    
        private Boolean publish(String dataId, String group, String content, String type) {
            try {
                return nacosConfigManager.getConfigService().publishConfig(dataId, group, content, type);
            } catch (NacosException e) {
                logger.error("NacosService publish e:{}", e);
            }
            return false;
        }
    
        public void pushRules(List<DegradeRule> localDegradeRuleList) {
            String dataId = appName + CommonConstant.DEGRADE_DATA_ID_POSTFIX;
    
            SerializeConfig serializeConfig = new SerializeConfig();
            serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.CamelCase;
            String contentStr = JSON.toJSONString(localDegradeRuleList, serializeConfig, SerializerFeature.PrettyFormat);
            publish(dataId, CommonConstant.GROUP_ID, contentStr, ConfigType.JSON.getType());
        }
    
    
        public 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);
                }
            }
        }
    
    
        public List<DegradeRule> fetchRemoteRules() {
            String dataId = appName + CommonConstant.DEGRADE_DATA_ID_POSTFIX;
            return JSONObject.parseArray(this.getConfig(dataId, CommonConstant.GROUP_ID), DegradeRule.class);
        }
    }
    

    ClassScanner.java工具类

    import com.sinochem.it.feign.config.FeignSentinelSupportConfig;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.BeanDefinitionStoreException;
    import org.springframework.context.ResourceLoaderAware;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternUtils;
    import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
    import org.springframework.core.type.classreading.MetadataReader;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.core.type.filter.AnnotationTypeFilter;
    import org.springframework.core.type.filter.TypeFilter;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.SystemPropertyUtils;
    
    import java.io.IOException;
    import java.lang.annotation.Annotation;
    import java.util.HashSet;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Set;
    
    public class ClassScanner implements ResourceLoaderAware {
    
        private static final Logger log = LoggerFactory.getLogger(FeignSentinelSupportConfig.class);
    
        private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
    
        private final List<TypeFilter> includeFilters = new LinkedList<>();
        private final List<TypeFilter> excludeFilters = new LinkedList<>();
    
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
            this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
        }
    
        public void addIncludeFilter(TypeFilter includeFilter) {
            this.includeFilters.add(includeFilter);
        }
    
        public void addExcludeFilter(TypeFilter excludeFilter) {
            this.excludeFilters.add(0, excludeFilter);
        }
    
        public void resetFilters(boolean useDefaultFilters) {
            this.includeFilters.clear();
            this.excludeFilters.clear();
        }
    
        /**
         * 扫描指定路径指定注解的类
         * @param basePackage
         * @param annotations
         * @return
         */
        public static Set<Class> scan(String basePackage, Class<? extends Annotation>... annotations) {
            ClassScanner classScanner = new ClassScanner();
    
            for (Class<? extends Annotation> annotation : annotations) {
                classScanner.addIncludeFilter(new AnnotationTypeFilter(annotation));
            }
            return classScanner.doScan(basePackage);
        }
    
        /**
         * 扫描多个路径下包含指定注解的类
         * @param basePackages
         * @param annotations
         * @return
         */
        public static Set<Class> scan(String[] basePackages, Class<? extends Annotation>... annotations) {
            ClassScanner classScanner = new ClassScanner();
            for (Class<? extends Annotation> annotation : annotations) {
                classScanner.addIncludeFilter(new AnnotationTypeFilter(annotation));
            }
    
            Set<Class> classes = new HashSet<>();
            for (String basePackage : basePackages) {
                classes.addAll(classScanner.doScan(basePackage));
            }
    
            return classes;
        }
    
        /**
         * 扫描指定路径下的类信息
         * @param basePackage
         * @return
         */
        public Set<Class> doScan(String basePackage) {
            Set<Class> classes = new HashSet<>();
    
            // 扫描路径
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                    + ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage))
                    + "/**/*.class";
            try {
    
                // 获取指定扫描路径的资源
                Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
    
                for (Resource resource : resources) {
                    if (resource.isReadable()) {
    
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
    
                        if ((this.includeFilters.size() == 0 && this.excludeFilters.size() == 0) || matches(metadataReader)) {
    
                            try {
                                // 返回符合条件的资源
                                classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
                            } catch (ClassNotFoundException e) {
                                log.error("class forName 异常:", e);
                            }
    
                        }
                    }
                }
            } catch (IOException e) {
    
                log.error("扫描加载资源io异常:", e);
                throw new BeanDefinitionStoreException("I/O failure during classpath scanning", e);
            }
    
            return classes;
        }
    
    
        /**
         * 资源是否匹配
         * @param metadataReader
         * @return
         * @throws IOException
         */
        private boolean matches(MetadataReader metadataReader) throws IOException {
    
            for (TypeFilter excludeFilter : this.excludeFilters) {
                if (excludeFilter.match(metadataReader, this.metadataReaderFactory)) {
                    return false;
                }
            }
    
            for (TypeFilter includeFilter : this.includeFilters) {
                if (includeFilter.match(metadataReader, this.metadataReaderFactory)) {
                    return true;
                }
            }
            return false;
        }
    }
    

    SpringBeanUtil.java通过spring获取类工具类

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    @Component
    public class SpringBeanUtil implements ApplicationContextAware {
        private static Logger log = LoggerFactory.getLogger(SpringBeanUtil.class);
        private static ApplicationContext context;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if (context == null) {
                context = applicationContext;
            }
        }
    
        public static ApplicationContext getContext() {
            return context;
        }
    
        public static Object getBean(String name) {
            try {
                return getContext().getBean(name);
            } catch (Exception e) {
                log.error("系统异常", e);
                return null;
            }
        }
    
        public static <T> T getBean(Class<T> clazz) {
            try {
                return getContext().getBean(clazz);
            } catch (Exception e) {
                log.error("系统异常", e);
                return null;
            }
        }
    
        public static <T> T getBean(String name, Class<T> clazz) {
            try {
                return getContext().getBean(name, clazz);
            } catch (Exception e) {
                log.error("系统异常", e);
                return null;
            }
        }
    
    }
    

    3.主要测试类

    测试类demo01 feign接口被调用方
    import org.springframework.web.bind.annotation.*;
    import java.util.concurrent.ThreadLocalRandom;
    import java.util.concurrent.TimeUnit;
    
    @RestController
    @RequestMapping(value = "/demo01")
    public class TestController {
    
        @GetMapping("/get/{id}")
        public String get(@PathVariable String id) {
            int i = ThreadLocalRandom.current().nextInt(40, 60);
            System.out.println("睡眠随机值:" + i);
            sleep(i);
            return "Hello Demo01 test get 返回值 成功!!! id:" + id;
        }
    
        @GetMapping("/get1")
        public String get1(@RequestParam String id) {
            int i = ThreadLocalRandom.current().nextInt(40, 60);
            System.out.println("睡眠随机值:" + i);
            sleep(i);
            return "Hello Demo01 test get1 返回值 成功!!! id:" + id;
        }
    
        private static void sleep(int timeMs) {
            try {
                TimeUnit.MILLISECONDS.sleep(timeMs);
            } catch (InterruptedException e) {
                // ignore
            }
        }
    }
    

    application.yml文件

    server:
      port: 36001
    
    # 应用名称
    spring:
      application:
        name: demo01
      cloud:
        # nacos注册中心配置
        nacos:
          server-addr: 127.0.0.1:39999
          discovery:
            group: SINO_MSA_GROUP
            namespace: Dev
        # sentinel控制台配置,实现流控,降级等
        sentinel:
          transport:
            dashboard: 127.0.0.1:39000
    

    bootstrap.yml文件

    spring:
      cloud:
        nacos:
          config:
            server-addr: 127.0.0.1:39999
            group: SINO_MSA_GROUP
            name: demo01
            file-extension: yaml
            namespace: Dev
    

    pom.xml文件

            <!-- nacos注册中心 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            </dependency>
    

    测试类demo02 feign接口调用方
    import com.sinochem.it.demo02.feign.TestFeign;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping(value = "/demo02")
    public class TestController {
    
        @Autowired
        private TestFeign testFeign;
    
        @GetMapping(value = "/get")
        public String get() {
            long start = System.currentTimeMillis();
            String s = testFeign.demo01Get("123");
            System.out.println(System.currentTimeMillis() - start + "ms");
            return s;
        }
    
        @GetMapping(value = "/get1")
        public String get1() {
            long start = System.currentTimeMillis();
            String s = testFeign.demo01Get1("456");
            System.out.println(System.currentTimeMillis() - start + "ms");
            return s;
        }
    
        @PostMapping(value = "/post")
        public String post() {
            long start = System.currentTimeMillis();
            String s = testFeign.demo01Post();
            System.out.println(System.currentTimeMillis() - start + "ms");
            return s;
        }
    
        @RequestMapping(value = "/req", method = RequestMethod.POST)
        public String req() {
            long start = System.currentTimeMillis();
            String s = testFeign.demo01Request();
            System.out.println(System.currentTimeMillis() - start + "ms");
            return s;
        }
    }
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.*;
    
    //@FeignClient(name = "demo01", url = "http://127.0.0.1:36001",fallbackFactory = TestFeignFallbackFactory.class)
    //@FeignClient(name = "demo01", url = "${base.url}",fallbackFactory = TestFeignFallbackFactory.class)
    //@FeignClient(name = "demo01",fallbackFactory = TestFeignFallbackFactory.class)
    @FeignClient(name = "${demo01.serviceName}",fallbackFactory = TestFeignFallbackFactory.class)
    public interface TestFeign {
    
        @GetMapping(value = "/demo01/get/{id}")
        String demo01Get(@PathVariable String id);
    
        @GetMapping(value = "/demo01/get1")
        String demo01Get1(@RequestParam String id);
    
        @PostMapping(value = "/demo01/post")
        String demo01Post();
    
        @RequestMapping(value = "/demo01/request",method = RequestMethod.POST)
        String demo01Request();
    }
    
    import org.springframework.cloud.openfeign.FallbackFactory;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TestFeignFallbackFactory implements FallbackFactory<TestFeign> {
    
        @Override
        public TestFeign create(Throwable cause) {
            return new TestFeign() {
                @Override
                public String demo01Get(String id) {
                    return "GetDemo服务熔断降级至此!!!";
                }
    
                @Override
                public String demo01Get1(String id) {
                    return "GetDemo服务熔断降级至此!!!";
                }
    
                @Override
                public String demo01Post() {
                    return "PostDemo服务熔断降级至此!!!";
                }
    
                @Override
                public String demo01Request() {
                    return "RequestDemo服务熔断降级至此!!!";
                }
            };
        }
    }
    

    application.yml文件

    server:
      port: 36002
    
    # 应用名称
    spring:
      application:
        name: demo02
      cloud:
        # nacos注册中心配置
        nacos:
          server-addr: 127.0.0.1:39999
          discovery:
            group: SINO_MSA_GROUP
            namespace: Dev
        # sentinel控制台配置,实现流控,降级等
        sentinel:
          transport:
            dashboard: 127.0.0.1:39000
    
    feign:
      sentinel:
        enabled: true
    
    base.url: http://127.0.0.1:36001
    demo01.serviceName: demo01
    

    bootstrap.yml文件

    spring:
      cloud:
        nacos:
          config:
            server-addr: 127.0.0.1:39999
            group: SINO_MSA_GROUP
            name: demo02
            file-extension: yaml
            namespace: Dev
    

    pom.xml文件

            <!-- nacos注册中心 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>com.sinochem.it</groupId>
                <artifactId>sino-msa-feign</artifactId>
                <version>2.1.0-SNAPSHOT</version>
            </dependency>
    

    相关文章

      网友评论

          本文标题:Feign接口基于Sentinel配置默认熔断规则

          本文链接:https://www.haomeiwen.com/subject/tdxqartx.html