提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
前段时间看到一个视频讲openfeign的动态使用方法,于是自己就写了一下,开始使用还算正常,但是在一些其他场景的情况下出问题了,所以就自己研究了下解决方案,记录一下。当然还有很多其他的场景没有覆盖到,希望有兴趣的多试试
一、使用openfeign
我们还是先按原来的使用方式先使用下openfeign
1、引入pom依赖
<!--添加openfeign相关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!--哨兵-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
说明下,下面的哨兵后面会用到,所以这里先引入
2、添加nacos配置
#feign 超时配置
feign.okhttp.enabled=true
#启用此配置会覆盖ribbon的重试功能,并且此配置只在client为jdk是才有效
feign.client.config.default.connectTimeout=1000
feign.client.config.default.readTimeout=10000
# Sentinel自动化配置是否生效
spring.cloud.sentinel.eager=true
# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=sentinel.xxx.com:8201
# 开启feign熔断
feign.sentinel.enabled=true
# 应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
spring.cloud.sentinel.transport.port=8202
3、启动类添加启动注解
/**
* @author jy
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients({"com.dili"})
public class DiliToolApplication {
public static void main(String[] args) {
SpringApplication.run(DiliToolApplication.class, args);
}
}
4、编写RPC接口
/**
* @Author jy
* @Date 2023/7/4 9:47
* @Version 1.0
*/
@FeignClient(value = "pay-service", contextId = "PayRpc", configuration = PayServiceFeignConfig.class, url = "${payService.url:}")
public interface PayRpc {
/**
* 查询余额(扩展)
* @author jy
* @date 2023/7/4
*/
@RequestMapping(value = "/payment/api/gateway.do?service=payment.fund.service:queryEx", method = RequestMethod.POST)
BaseOutput<BalanceResponseDto> getAccountBalanceEx(CreateTradeRequestDto createTradeRequest);
}
说明下,PayServiceFeignConfig就是一个拦截器类,在调用时加一些可能需要的东西
/**
* ps:不要在该类上面加诸如 @Configuration,否则会变成全局配置
* @Auther: jy
* @Date: 2023/7/4 10:01
*/
public class PayServiceFeignConfig {
private static final Logger log = LoggerFactory.getLogger(PayServiceFeignConfig.class);
private static final String APPID = "1010";
private static final String TOKEN = "abcd1010";
/**
* 拦截器
*/
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
template.header("appid", APPID);
template.header("token", TOKEN);
template.header("mchId", 14 + "");
};
}
}
5、编写测试类
/**
* @Author jy
* @Date 2023/7/4 9:42
* @Version 1.0
*/
@RestController
@RequestMapping("/api/feign")
public class TestController {
Logger logger = LoggerFactory.getLogger(TestController.class);
@Resource
private PayRpc payRpc;
/**
*
* @return
*/
@GetMapping("/test")
@ResponseBody
public String test(String num) {
logger.info("测试入参,{}",num);
CreateTradeRequestDto requestDto = new CreateTradeRequestDto();
requestDto.setAccountId(109751L);
BaseOutput<BalanceResponseDto> resolver = payRpc.getAccountBalanceEx(requestDto);
logger.info("调用支付系统结果返回,{}",resolver.getMessage());
return "ok";
}
}
6、测试结果
这里返回了支付系统的提示,说明我们的调用是成功,只是这个账号错误了,对于openfeign使用的学习没有关系。
通常情况下我们大抵就这样使用了。每次添加一个服务需要调用时就加一个RPC接口,服务少的还好说,像我们公司10多个服务,有些服务还按功能需求分别写RPC接口的,那就多了。某天正好刷到一个动态调用的视频于是自己学习了一下
二、动态调用
将原来的RPC接口全部去掉注释,使之服务启动时无法注册到容器,同时为了彻底去掉openfeign原来使用方式,直接将启动类上的@EnableFeignClients一并注释掉;由于我们前面开启了哨兵模式的,我们先把它关了
#feign 超时配置
feign.okhttp.enabled=true
#启用此配置会覆盖ribbon的重试功能,并且此配置只在client为jdk是才有效
feign.client.config.default.connectTimeout=1000
feign.client.config.default.readTimeout=10000
# Sentinel自动化配置是否生效
spring.cloud.sentinel.eager=false//关了
# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=sentinel.diligrp.com:8201
# 开启feign熔断
feign.sentinel.enabled=false//这样就关了
# 应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
spring.cloud.sentinel.transport.port=8202
1、编写动态类
/**
* @Author jy
* @Date 2023/7/4 10:56
* @Version 1.0
*/
public interface DynamicService {
/**
*
* @param url
* @param params
* @return
*/
@RequestMapping(value = "{url}",method = RequestMethod.POST)
Object executePostApi(@PathVariable("url") String url, @RequestBody Object params);
/**
*
* @param url
* @param params
* @return
*/
@RequestMapping(value = "{url}",method = RequestMethod.GET)
Object executeGetApi(@PathVariable("url") String url, @SpringQueryMap Object params);
}
/**
* @Author jy
* @Date 2023/7/4 10:50
* @Version 1.0
*/
@Component
public class DynamicFeignClientFactory<T> {
private FeignClientBuilder feignClientBuilder;
/**
* @param applicationContext
*/
public DynamicFeignClientFactory(ApplicationContext applicationContext) {
this.feignClientBuilder = new FeignClientBuilder(applicationContext);
}
/**
* @param type
* @param serviceId
* @return
*/
public T getFeignClient(final Class<T> type, String serviceId, Class fallback, Class fallbackFactory) {
return (T) this.feignClientBuilder.forType(type, serviceId).fallback(fallback).fallbackFactory(fallbackFactory).build();
}
}
/**
* @Author jy
* @Date 2023/7/4 11:04
* @Version 1.0
*/
@Component
public class DynamicClient {
@Resource
DynamicFeignClientFactory<DynamicService> dynamicServiceDynamicFeignClientFactory;
/**
* @param serviceId
* @param url
* @param params
* @return
*/
public Object executePostApi(String serviceId, String url, Object params, Class fallback, Class fallbackFactory) {
DynamicService dynamicService = dynamicServiceDynamicFeignClientFactory.getFeignClient(DynamicService.class, serviceId, fallback, fallbackFactory);
return dynamicService.executePostApi(url, params);
}
/**
* @param serviceId
* @param url
* @param params
* @return
*/
public Object executeGetApi(String serviceId, String url, Object params, Class fallback, Class fallbackFactory) {
DynamicService dynamicService = dynamicServiceDynamicFeignClientFactory.getFeignClient(DynamicService.class, serviceId, fallback, fallbackFactory);
return dynamicService.executeGetApi(url, params);
}
}
2、编写测试方法
/**
*
* @return
*/
@GetMapping("/testV2")
@ResponseBody
public String testV2(String num) {
logger.info("测试入参,{}",num);
//调用账户服务查询账户信息
UserAccountCardQuery param = new UserAccountCardQuery();
param.setExcludeUnusualState(0);
param.setFirmId(14L);
param.setPage(1);
param.setRows(10);
Object object = dynamicClient.executePostApi("account-service", "api/account/getPage", param, void.class, void.class);
CreateTradeRequestDto requestDto = new CreateTradeRequestDto();
logger.info("动态版,账户结果,{}",object);
return "ok";
}
测试结果
从结果上得出,我们的调用是正常的,说明我们的动态调用就是可以的,那么这样就可以了吗?肯定不是这么简单的,这只是一种调用模式;单纯的POST请求和GET请求大致不差,我们就不一一测试了,下面我们测试下POST和GET请求一起的模式
将原来的测试修改为动态模式的调用
注意!!!我们的请求是POST,但是在路径上加了参数(我们公司就有这样的接口,没办法,所以测到了)
/**
*
* @return
*/
@GetMapping("/test")
@ResponseBody
public String test(String num) {
logger.info("测试入参,{}",num);
CreateTradeRequestDto requestDto = new CreateTradeRequestDto();
requestDto.setAccountId(109751L);
//BaseOutput<BalanceResponseDto> resolver = payRpc.getAccountBalanceEx(requestDto);
Object object = dynamicClient.executePostApi("pay-service", "/payment/api/gateway.do?service=payment.fund.service:queryEx", requestDto, void.class, void.class);
logger.info("改为动态模式,调用支付系统结果返回,{}",object);
return "ok";
}
测试结果
结果报了404因为我们的请求路径出现了乱码,为什么会这样呢?通过源码走查,我们发现由于我们使用的是表达式方式传递url,所以最终会到下图位置
进一步往下走
从这里可以看出,在这里我们的入参路径都还是正常的,再往下走一点,还是在这个方法里路径就乱码了
对于?、=、:的编码解析出现了问题,那么%2F呢,这个源码里是直接用String的替换方法给换成/了。所以最后出现的就是前面几个乱码。既然找到问题了,那我们该怎么处理呢?说实话,最开始我想的是有没有办法,将前面的第二个参数true设置成false,这样也可以处理这个问题,但是没有找到设置的地方(有办法的朋友可以留言下,谢谢)。后来想到了另一个办法,既然这里乱码了,那就不管它了,我在拦截器里重新替换一次就可以了,于是对代码进行了如下修改
添加拦截器
/**
* @Author jy
* @Date 2023/6/25 16:54
* @Version 1.0
*/
@Configuration
public class DynamicRoutingConfig {
private static String URI = "url";
@Bean
public RequestInterceptor cloudContextInterceptor() {
return template -> {
Map<String, Collection<String>> headerMap = template.headers();
//因为获取的是请求里的header所以肯定是不为空的,所以只需判断是否包含url的key并不为空
if (headerMap.containsKey(URI)&& CollectionUtil.isNotEmpty(headerMap.get(URI))) {
Target target = template.feignTarget();
Target.HardCodedTarget hardCodedTarget = new Target.HardCodedTarget(target.type(),target.name(),target.url());
String uri = headerMap.get(URI).stream().collect(Collectors.toList()).get(0);
template.feignTarget(hardCodedTarget);
template.uri(uri);
}
};
}
}
在原来的基础上请求注解上加了headers将url传递到拦截器重新赋值
public interface DynamicService {
/**
*
* @param url
* @param params
* @return
*/
@RequestMapping(value = "{url}",method = RequestMethod.POST,headers = {"url={url}"})
Object executePostApi(@PathVariable("url") String url, @RequestBody Object params);
/**
* 注意这里的url就不要包含?和参数之类的,放在下面的入参里即可,会自动拼接成原来get请求的模样的
* @param url
* @param params
* @return
*/
@RequestMapping(value = "{url}",method = RequestMethod.GET)
Object executeGetApi(@PathVariable("url") String url, @SpringQueryMap Object params);
}
经过以上修改后,我们再试试之前的请求
如图所示,成功调用到了对方服务。那么我们又要问了,这样就算成功了吗?肯定不是的,前面我们的禁止掉的哨兵模式还没有打开呢?那我们就打开哨兵吧
三、openfeign+sentinel
将配置恢复到最开始的true,再试试前面的调用。
从结果中,我们可以看出DynamicService的代理类是没有完成创建的,在spring容器中也没有对应的对象。结合下图,其实连BeanDefinition都没有创建。
我们都知道openfeign在启动类上会加一个开关注解EnableFeignClients来控制,那么我们这里把这个注解加上就可以了吗?答案是不可以的,还是会报同样的错误,因为我们的DynamicService是没有加FeignClient注解,所以也是扫描不到的。那这个问题怎么解决呢?我这里提供两个方案供大家参考。
1、方案一
我们前面已经说了,是因为在spring中拿不到对象,拿不到对象是没有开始扫描,同时没有扫描到自己,那我们就索性开启扫描,并给DynamicService加上FeignClient注解;这里需要注意的是,我们在给name值时是随意给的,这个不重要,因为最终走的是我们下面参数提供的,这里只是因为必须给一个所以就随意了。后面是加的回调类。
/**
* @Author jy
* @Date 2023/7/4 10:56
* @Version 1.0
*/
@FeignClient(name = "a",fallback = DynamicServiceCallBack.class)
public interface DynamicService {
/**
*
* @param url
* @param params
* @return
*/
@RequestMapping(value = "{url}",method = RequestMethod.POST,headers = {"url={url}"})
Object executePostApi(@PathVariable("url") String url, @RequestBody Object params);
/**
*
* @param url
* @param params
* @return
*/
@RequestMapping(value = "{url}",method = RequestMethod.GET)
Object executeGetApi(@PathVariable("url") String url, @SpringQueryMap Object params);
}
经过改造后,再试试
可以了,服务又可以正常调用了,同时我们还测了一下降级处理
也是正常回调到的,但是问题也出来了,这样的操作,我们的降级处理类只能是固定的(当然对于想统一处理异常降级机制的刚好),对于想按各自接口来定义降级处理的,就不行了。所以有了第二个思路
2、方案二
前面的报错地方我们已经知道,再仔细一看,获取bean对象后,其实没有拿来做什么就获取了一个对象属性。本着缺什么补什么的原则,缺少BeanDefinition对象,那我们就加,于是我采用了spring的后置处理器,自定义了一个对象按openfeign的形式注入了一个class对象为DynamicService的BeanDefinition对象,这样一来调用是没有问题了,但是降级处理是没有办法实现了,这样相当于阉割了一些功能,这肯定是不行的,于是再想其他办法。既然获取方法不行,我们就改这个获取方式,修改原来的获取降级处理类。
原来的
Object feignClientFactoryBean = Builder.this.applicationContext
.getBean("&" + target.type().getName());
Class fallback = (Class) getFieldValue(feignClientFactoryBean,
"fallback");
Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
"fallbackFactory");
String beanName = (String) getFieldValue(feignClientFactoryBean,
"contextId");
if (!StringUtils.hasText(beanName)) {
beanName = (String) getFieldValue(feignClientFactoryBean, "name");
}
要修改这里的话,就需要重写SentinelFeign,因为是final修饰的类,无法继承,所以得重新写一个新类,连带的SentinelInvocationHandler、SentinelFeignAutoConfiguration都需要重新编写,好在基本是照原来的复制下来,部分修改即可。同时还有一个用来传递fallback等信息的ThreaLocal工具类。
配置类,将原来的配置类顶替掉
/**
* sentinel 配置
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(SentinelFeignAutoConfiguration.class)
public class SentinelAutoConfiguration {
@Bean
@Primary
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.sentinel.enabled")
public Feign.Builder feignSentinelBuilderPrimary() {
return CloudSentinelFeign.builder();
}
}
**
* 支持自动降级注入 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelFeign}
*/
public final class CloudSentinelFeign {
private CloudSentinelFeign() {
}
public static Builder builder() {
return new Builder();
}
public static final class Builder extends Feign.Builder implements ApplicationContextAware {
private Contract contract = new Contract.Default();
private ApplicationContext applicationContext;
private FeignContext feignContext;
@Override
public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
throw new UnsupportedOperationException();
}
@Override
public Builder contract(Contract contract) {
this.contract = contract;
return this;
}
@Override
public Feign build() {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
// 查找 FeignClient 上的 降级策略
Class<?> fallback = (Class<?>) DynamicFeignHolder.getFallBack();
Class<?> fallbackFactory = (Class<?>) DynamicFeignHolder.getFallBackFactory();
String beanName = target.name();
Object fallbackInstance;
FallbackFactory<?> fallbackFactoryInstance;
if (void.class != fallback&&null!=fallback) {
fallbackInstance = getFromContext(beanName, "fallback", fallback, target.type());
return new CloudSentinelInvocationHandler(target, dispatch,
new FallbackFactory.Default(fallbackInstance));
}
if (void.class != fallbackFactory&&null!=fallbackFactory) {
fallbackFactoryInstance = (FallbackFactory<?>) getFromContext(beanName, "fallbackFactory",
fallbackFactory, FallbackFactory.class);
return new CloudSentinelInvocationHandler(target, dispatch, fallbackFactoryInstance);
}
return new CloudSentinelInvocationHandler(target, dispatch);
}
private Object getFromContext(String name, String type, Class<?> fallbackType, Class<?> targetType) {
Object fallbackInstance = feignContext.getInstance(name, fallbackType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No %s instance of type %s found for feign client %s", type, fallbackType, name));
}
if (!targetType.isAssignableFrom(fallbackType)) {
throw new IllegalStateException(String.format(
"Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
type, fallbackType, targetType, name));
}
return fallbackInstance;
}
});
super.contract(new SentinelContractHolder(contract));
return super.build();
}
private Object getFieldValue(Object instance, String fieldName) {
Field field = ReflectionUtils.findField(instance.getClass(), fieldName);
field.setAccessible(true);
try {
return field.get(instance);
} catch (IllegalAccessException e) {
// ignore
}
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
feignContext = this.applicationContext.getBean(FeignContext.class);
}
}
}
这里主要是重写create方法
/**
* 支持自动降级注入 重写 {@link SentinelInvocationHandler}
*/
@Slf4j
public class CloudSentinelInvocationHandler implements InvocationHandler {
private final Target<?> target;
private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
private FallbackFactory<?> fallbackFactory;
private Map<Method, Method> fallbackMethodMap;
public CloudSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,
FallbackFactory<?> fallbackFactory) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
this.fallbackFactory = fallbackFactory;
this.fallbackMethodMap = toFallbackMethod(dispatch);
}
public CloudSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler = args.length > 0 && args[0] != null
? Proxy.getInvocationHandler(args[0])
: null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
Object result;
InvocationHandlerFactory.MethodHandler methodHandler = this.dispatch.get(method);
// only handle by HardCodedTarget
if (target instanceof Target.HardCodedTarget) {
Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
.get(hardCodedTarget.type().getName()
+ Feign.configKey(hardCodedTarget.type(), method));
// resource default is HttpMethod:protocol://url
if (methodMetadata == null) {
result = methodHandler.invoke(args);
} else {
String resourceName = methodMetadata.template().method().toUpperCase()
+ ":" + hardCodedTarget.url() + methodMetadata.template().path();
Entry entry = null;
try {
ContextUtil.enter(resourceName);
entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
result = methodHandler.invoke(args);
} catch (Throwable ex) {
// fallback handle
if (!BlockException.isBlockException(ex)) {
Tracer.traceEntry(ex, entry);
}
if (fallbackFactory != null) {
try {
Object fallbackResult = fallbackMethodMap.get(method)
.invoke(fallbackFactory.create(ex), args);
return fallbackResult;
} catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an
// interface
throw new AssertionError(e);
} catch (InvocationTargetException e) {
throw new AssertionError(e.getCause());
}
} else {
//主动降级注入的关键点
// 若是Result类型 执行自动降级返回Result
if (Result.class == method.getReturnType()) {
log.error("feign 服务间调用异常", ex);
//SentinelExceptionUtil自己封装的判断sentinel熔断类型
return null;
} else {
throw ex;
}
}
} finally {
if (entry != null) {
entry.exit(1, args);
}
ContextUtil.exit();
}
}
} else {
// other target type using default strategy
result = methodHandler.invoke(args);
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SentinelInvocationHandler) {
CloudSentinelInvocationHandler other = (CloudSentinelInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
Map<Method, Method> result = new LinkedHashMap<>();
for (Method method : dispatch.keySet()) {
method.setAccessible(true);
result.put(method, method);
}
return result;
}
}
该类就是单纯的复制即可,因为原类是类路径保护的无法引用。
/**
* @Author jy
* @Date 2023/7/4 11:21
* @Version 1.0
*/
public class DynamicFeignHolder {
private static final ThreadLocal<Object> HOLDER = new ThreadLocal<>();
private static final ThreadLocal<Object> FACTORY = new ThreadLocal<>();
/**
* 添加
* @param obj
*/
public static void setFallBack(Object obj) {
HOLDER.set(obj);
}
/**
* 获取
* @return
*/
public static Object getFallBack() {
return HOLDER.get();
}
/**
* 清除
*/
public static void clearFallBack() {
HOLDER.remove();
}
/**
* 添加
* @param obj
*/
public static void setFallBackFactory(Object obj) {
FACTORY.set(obj);
}
/**
* 获取
* @return
*/
public static Object getFallBackFactory() {
return FACTORY.get();
}
/**
* 清除
*/
public static void clearFallBackFactory() {
FACTORY.remove();
}
}
用于传递fallback和fallbackFactory的
原来的调用也需要做稍许改变,增加了降级类的传递操作及ThreadLocal销毁的动作
/**
* @Author jy
* @Date 2023/7/4 10:50
* @Version 1.0
*/
@Component
public class DynamicFeignClientFactory<T> {
private FeignClientBuilder feignClientBuilder;
/**
* @param applicationContext
*/
public DynamicFeignClientFactory(ApplicationContext applicationContext) {
this.feignClientBuilder = new FeignClientBuilder(applicationContext);
}
/**
* @param type
* @param serviceId
* @return
*/
public T getFeignClient(final Class<T> type, String serviceId, Class fallback, Class fallbackFactory) {
if (ObjectUtil.isNotEmpty(fallback)) {
DynamicFeignHolder.setFallBack(fallback);
}
if (ObjectUtil.isNotEmpty(fallbackFactory)) {
DynamicFeignHolder.setFallBackFactory(fallbackFactory);
}
return (T) this.feignClientBuilder.forType(type, serviceId).fallback(fallback).fallbackFactory(fallbackFactory).build();
}
}
/**
* @Author jy
* @Date 2023/7/4 11:04
* @Version 1.0
*/
@Component
public class DynamicClient {
@Resource
DynamicFeignClientFactory<DynamicService> dynamicServiceDynamicFeignClientFactory;
/**
* @param serviceId
* @param url
* @param params
* @return
*/
public Object executePostApi(String serviceId, String url, Object params, Class fallback, Class fallbackFactory) {
try {
DynamicService dynamicService = dynamicServiceDynamicFeignClientFactory.getFeignClient(DynamicService.class, serviceId, fallback, fallbackFactory);
return dynamicService.executePostApi(url, params);
} finally {
DynamicFeignHolder.clearFallBack();
DynamicFeignHolder.clearFallBackFactory();
}
}
/**
* @param serviceId
* @param url
* @param params
* @return
*/
public Object executeGetApi(String serviceId, String url, Object params, Class fallback, Class fallbackFactory) {
try {
DynamicService dynamicService = dynamicServiceDynamicFeignClientFactory.getFeignClient(DynamicService.class, serviceId, fallback, fallbackFactory);
return dynamicService.executeGetApi(url, params);
} finally {
DynamicFeignHolder.clearFallBack();
DynamicFeignHolder.clearFallBackFactory();
}
}
}
启动类上的开关注解也可去掉。至此改造也就完成了,经过测试调用成功,降级处理也顺利启用。在我们项目中出现的两个问题算是解决了,不过还有没有其他问题这个就没办法保证了,毕竟测试的人力、时间、场景有限。
总结
这样改造好像也没有比写RPC接口方便多少,O(∩_∩)O哈哈~ 算了只当记录学习过程,总结经验,继续加油吧!!!!!!望大家多指教