概述:基于sentinel配置熔斷規(guī)則是針對每個接口的疯特,如果feign接口配置規(guī)則,使用手動方式在sentinel控制臺中配置氏堤,那是一件不現(xiàn)實的工作,所以針對feign接口需要配置默認的熔斷規(guī)則捌归,同時會將初始化的熔斷規(guī)則存儲到nacos中
涉及到的核心功能類:FeignSentinelSupportConfig.java
,自定義注解類:EnableFeignDegrade.java
岭粤,工具類ClassScanner.java
,SpringBeanUtil.java
注:本文基于nacos作為注冊中心及配置中心惜索,sentinel熔斷服務(wù),feign遠程調(diào)用服務(wù)L杲健=碚住!
接下來上代碼
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遠程接口調(diào)用-->
<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
默認配置規(guī)則類
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;//環(huán)境變量對象
@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("開始加載默認規(guī)則虎囚,mainClass:{}", mainClass);
if (mainClass == null) {
throw new RuntimeException("can not fount main class");
}
// 添加開關(guān) 如果不使用nacos配置中心臼寄,則直接return,不初始化默認的熔斷規(guī)則
if (nacosConfigEnable != null && !nacosConfigEnable) {
logger.info("nacos配置中心關(guān)閉溜宽,加載默認規(guī)則結(jié)束");
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;
}
//初始化降級規(guī)則
initDeGradeRule(feignClientPackages);
}
logger.info("默認降級規(guī)則處理完成");
}
private Set<Class> getFeignClientClass(String[] packageNames) {
ClassScanner classScanner = new ClassScanner();
Set<Class> feignClientClass = classScanner.scan(packageNames, FeignClient.class);
return feignClientClass;
}
/**
* 創(chuàng)建熔斷規(guī)則列表數(shù)據(jù)
* 如果全部feign接口設(shè)置默認策略太多,則可以通過自定義注解质帅,標(biāo)記哪些feign接口類需要設(shè)置
*
* @param cla feign接口類
* @return
*/
public List<DegradeRule> initRules(Class cla) {
List<DegradeRule> degradeRuleList = new ArrayList<>();
// 在該處獲取自定義注解适揉,來判斷是否添加默認熔斷規(guī)則 如果feign接口類上添加了@EnableFeignDegrade注解并且enable為false,則不為該類中接口添加默認熔斷規(guī)則
EnableFeignDegrade feignDegrade = (EnableFeignDegrade) cla.getAnnotation(EnableFeignDegrade.class);
if (null != feignDegrade && !feignDegrade.enable()) {
logger.info("{}類關(guān)閉了初始化默認熔斷規(guī)則功能", cla.getName());
return degradeRuleList;
}
FeignClient feignClient = (FeignClient) cla.getAnnotation(FeignClient.class);
// 獲取RequestMapping注解是否配置基礎(chǔ)路徑
String classRequestMappingUrl = "";
RequestMapping classRequestMapping = (RequestMapping) cla.getAnnotation(RequestMapping.class);
if (null != classRequestMapping) {
classRequestMappingUrl = classRequestMapping.value()[0];
}
// 這里區(qū)分四種情況
// 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}"
// 標(biāo)識是否配置url屬性煤惩,拼接http://前綴時判斷使用
Boolean httpFlag = true;
String baseUrl = null;
String serviceUrl = feignClient.url();
String serviceName = feignClient.name();
// 如果url屬性不為空并且${開頭 }結(jié)尾嫉嘀,說明是動態(tài)配置的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;
}
/**
* 初始化熔斷規(guī)則
*
* @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沒有規(guī)則,那就直接利用本地規(guī)則
if (remoteDegradeRuleList == null || remoteDegradeRuleList.isEmpty()) {
this.pushRules(nacosConfigManager, localDegradeRuleList);
return;
}
//本地規(guī)則 合并 遠程規(guī)則策略
this.proess(localDegradeRuleList, remoteDegradeRuleList);
//推送本地規(guī)則魄揉,到nacos
this.pushRules(nacosConfigManager, localDegradeRuleList);
}
/***
* 組裝resourceName
* @param crmu requestMapping路徑
* @param serviceName 服務(wù)名稱或url
* @param method 方法
* @param httpFlag http標(biāo)記
* @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參數(shù)剪侮,如果未配置,則默認為GET請求
RequestMethod mMethod = null;
if (methodRequestMapping.method().length > 0) {
mMethod = methodRequestMapping.method()[0];
}
// @RequestMapping 注解不指定method參數(shù)洛退,則默認為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屬性瓣俯,說明使用服務(wù)名進行服務(wù)之間調(diào)用,所以需要拼接http:// 拼接結(jié)果:http://serviceName/demo/get 前面拼接的請求方式(POST:)是因為sentinel需要 不拼接熔斷規(guī)則不生效
if (!httpFlag) {
resourceName = "POST:" + HTTP_PROTOCOL_PREFIX + serviceName + crmu + (mpm.startsWith("/") ? mpm : "/" + mpm);
} else { // 配置url屬性兵怯,說明使用指定url調(diào)用 則不需要單獨拼接http:// 拼接結(jié)果為:http://127.0.0.1:8080/demo/get 前面拼接的請求方式(POST:)是因為sentinel需要 不拼接熔斷規(guī)則不生效
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;
}
/***
* 創(chuàng)建默認熔斷規(guī)則
* @param resourceName
* @return
*/
private DegradeRule buildDegradeRule(String resourceName) {
DegradeRule rule = new DegradeRule(resourceName)
// 熔斷策略
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
// Max allowed response time RT 慢調(diào)用標(biāo)準(zhǔn)值 接口響應(yīng)時長超過2秒則被判定為慢調(diào)用
.setCount(2000)
// Retry timeout (in second) 窗口期 熔斷時長
.setTimeWindow(30)
// Circuit breaker opens when slow request ratio > 80% 慢調(diào)用比例 判定是否熔斷的條件之一
.setSlowRatioThreshold(0.8)
// 單位時長最小請求數(shù) 判定是否熔斷的條件之一
.setMinRequestAmount(30)
// 統(tǒng)計時長也叫單位時長
.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初始化熔斷規(guī)則功能 默認使用
*
* @return
*/
boolean enable() default true;
}
DefaultRuleNacosManager.java
操作nacos持久化類 優(yōu)化之后已經(jīng)合并到FeignSentinelSupportConfig
類中彩匕,如果分開兩個類,啟動服務(wù)注入時會出現(xiàn)加載問題
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("系統(tǒng)異常", e);
return null;
}
}
public static <T> T getBean(Class<T> clazz) {
try {
return getContext().getBean(clazz);
} catch (Exception e) {
log.error("系統(tǒng)異常", e);
return null;
}
}
public static <T> T getBean(String name, Class<T> clazz) {
try {
return getContext().getBean(name, clazz);
} catch (Exception e) {
log.error("系統(tǒng)異常", e);
return null;
}
}
}
3.主要測試類
測試類demo01 feign接口被調(diào)用方
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 返回值 成功C角M找恰! 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 返回值 成功M噤觥P靼帧! id:" + id;
}
private static void sleep(int timeMs) {
try {
TimeUnit.MILLISECONDS.sleep(timeMs);
} catch (InterruptedException e) {
// ignore
}
}
}
application.yml
文件
server:
port: 36001
# 應(yīng)用名稱
spring:
application:
name: demo01
cloud:
# nacos注冊中心配置
nacos:
server-addr: 127.0.0.1:39999
discovery:
group: SINO_MSA_GROUP
namespace: Dev
# sentinel控制臺配置宙攻,實現(xiàn)流控奠货,降級等
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接口調(diào)用方
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服務(wù)熔斷降級至此!U秤拧仇味!";
}
@Override
public String demo01Get1(String id) {
return "GetDemo服務(wù)熔斷降級至此I胪纭!丹墨!";
}
@Override
public String demo01Post() {
return "PostDemo服務(wù)熔斷降級至此@缺椤!贩挣!";
}
@Override
public String demo01Request() {
return "RequestDemo服務(wù)熔斷降級至此:砬啊!王财!";
}
};
}
}
application.yml
文件
server:
port: 36002
# 應(yīng)用名稱
spring:
application:
name: demo02
cloud:
# nacos注冊中心配置
nacos:
server-addr: 127.0.0.1:39999
discovery:
group: SINO_MSA_GROUP
namespace: Dev
# sentinel控制臺配置卵迂,實現(xiàn)流控,降級等
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>