概述:基于sentinel配置熔断规则是针对每个接口的,如果feign接口配置规则,使用手动方式在sentinel控制台中配置,那是一件不现实的工作,所以针对feign接口需要配置默认的熔断规则,同时会将初始化的熔断规则存储到nacos中
涉及到的核心功能类:FeignSentinelSupportConfig.java
,自定义注解类:EnableFeignDegrade.java
,工具类ClassScanner.java
,SpringBeanUtil.java
注:本文基于nacos作为注册中心及配置中心,sentinel熔断服务,feign远程调用服务!!!
接下来上代码
1.需要的pom依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
com.alibaba.csp
sentinel-datasource-nacos
org.springframework.cloud
spring-cloud-starter-openfeign
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.springframework.cloud
spring-cloud-loadbalancer
com.netflix.feign
feign-okhttp
8.18.0
io.github.openfeign.form
feign-form
3.8.0
io.github.openfeign.form
feign-form-spring
3.8.0
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 getFeignClientClass(String[] packageNames) {
ClassScanner classScanner = new ClassScanner();
Set feignClientClass = classScanner.scan(packageNames, FeignClient.class);
return feignClientClass;
}
/**
* 创建熔断规则列表数据
* 如果全部feign接口设置默认策略太多,则可以通过自定义注解,标记哪些feign接口类需要设置
*
* @param cla feign接口类
* @return
*/
public List initRules(Class cla) {
List 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 localDegradeRuleList = new ArrayList<>();
Set feignClientClass = getFeignClientClass(feignClientPackages);
for (Class clientClass : feignClientClass) {
List rules = initRules(clientClass);
localDegradeRuleList.addAll(rules);
}
NacosConfigManager nacosConfigManager = SpringBeanUtil.getBean("nacosConfigManager", NacosConfigManager.class);
List 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 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 localDegradeRuleList, List 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 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 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 localDegradeRuleList, List 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 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 includeFilters = new LinkedList<>();
private final List 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 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 scan(String[] basePackages, Class extends Annotation>... annotations) {
ClassScanner classScanner = new ClassScanner();
for (Class extends Annotation> annotation : annotations) {
classScanner.addIncludeFilter(new AnnotationTypeFilter(annotation));
}
Set classes = new HashSet<>();
for (String basePackage : basePackages) {
classes.addAll(classScanner.doScan(basePackage));
}
return classes;
}
/**
* 扫描指定路径下的类信息
* @param basePackage
* @return
*/
public Set doScan(String basePackage) {
Set 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 getBean(Class clazz) {
try {
return getContext().getBean(clazz);
} catch (Exception e) {
log.error("系统异常", e);
return null;
}
}
public static T getBean(String name, Class 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
文件
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
测试类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 {
@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
文件
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.sinochem.it
sino-msa-feign
2.1.0-SNAPSHOT