问题描述:
现有三个工程 A,B,C。 A工程会访问B工程接口,也会访问C工程接口,原来的做法是写好两个接口类去访问,如下所示,但是访问时出现问题,当第一次访问时,A调用B成功,A调用C成功。但第二次访问时,A调用B时,报错404
@FeignClient
public interface A{
/**
*
* @Title getConfigsOfService
* @Description 获取服务的配置
* @param name
* @return
* @return String
*/
@GetMapping(value = "/data_control_web/ambari/service/getConfigsOfService")
public String getConfigsOfService(@RequestParam("serviceName") String serviceName,
@RequestParam("engineeringId") int engineeringId);
}
@FeignClient
public interface B{
/**
*
* @Title detailFileResourceVersion
* @Description 获取文件详情
* @param fileResourceId
* @param version
* @param engineeringId
* @return
* @return String
*/
@GetMapping("/data_resource_manage/resource/file/version/detail")
public String detailFileResourceVersion(@RequestParam("fileResourceId") int fileResourceId,
@RequestParam("version") String version,
@RequestParam("engineeringId") int engineeringId);
}
解决方法:
A,B接口去掉@FeignClient注解,再编写一个类RemoteXxxClient,通过反射方式注解两个接口,如下图所示
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import feign.Contract;
import feign.Feign;
import feign.Request;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.http.MediaType;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* Feign配置类,负责实例化所需要的伪客户端
*/
@Component
public class RemoteXxxClient {
public final static String DATA_RESOURCE_MANAGER = "DATA-RESOURCE-MANAGER";
public final static String DATA_CONTROL_WEB = "DATA-CONTROL-WEB";
/**
* FeignClientFactoryBean 该工厂类中 设置builder属性时就是通过该对象,源码中可看到
*/
protected FeignContext feignContext;
/**
* 通过注入Eureka实例对象,就不用手动指定url,只需要指定服务名即可
*/
protected EurekaClient eurekaClient;
private static final Map<String, Object> FEIGN_CLIENTS = new ConcurrentHashMap<>();
@Autowired
public void init(final EurekaClient eurekaClient, final FeignContext feignContext) {
this.eurekaClient = eurekaClient;
this.feignContext = feignContext;
}
@Bean
public A getQcloudService() {
return create(A.class, DATA_CONTROL_WEB);
}
@Bean
public B getTaskService() {
return create(B.class, DATA_RESOURCE_MANAGER);
}
/**
* 设置编码解码器为FastJson
*
* @param clazz
* @param serverId
* @param <T>
* @return
*/
protected <T> T create(Class<T> clazz, String serverId) {
InstanceInfo nextServerFromEureka = eurekaClient.getNextServerFromEureka(serverId, false);
String key = nextServerFromEureka.getIPAddr() + ":" + nextServerFromEureka.getPort() + ":"
+ clazz.getName();
Object object = FEIGN_CLIENTS.get(key);
if (Objects.isNull(object)) {
object = Feign.builder()
// encoder指定对象编码方式
.encoder(this.feignEncoder())
// decoder指定对象解码方式
.decoder(this.feignDecoder())
// .client(feignClient)
// options方法指定连接超时时长及响应超时时长
.options(new Request.Options(5000, 5000))
// retryer方法指定重试策略
// .retryer(new Retryer.Default(5000, 5000, 3))
.contract(feignContext.getInstance(serverId, Contract.class))
// 自己写的类,传输token,不需要的话可以去掉
.requestInterceptor(new TokenConfig())
// target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
.target(clazz, nextServerFromEureka.getHomePageUrl());
FEIGN_CLIENTS.put(key, object);
}
return (T) object;
}
protected Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
protected Decoder feignDecoder() {
return new SpringDecoder(feignHttpMessageConverter());
}
/**
* 设置解码器为fastjson
*
* @return
*/
private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters =
new HttpMessageConverters(this.getFastJsonConverter());
return () -> httpMessageConverters;
}
private FastJsonHttpMessageConverter getFastJsonConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_UTF8_VALUE);
supportedMediaTypes.add(mediaTypeJson);
converter.setSupportedMediaTypes(supportedMediaTypes);
return converter;
}
}
访问方式:
类中直接引用接口即可
@@Autowired
private A a;
@@Autowired
private B b;