Feign 版本10.1.0
Spring 版本 5.1.5.RELEASE
SpringBoot 版本 2.1.5.RELEASE
SpringCloud 版本 2.1.1.RELEASE
在微服务架构中,当我们需要进行服务间调用时可以选择feign组件,
现在遇到的问题是: 当同一个服务,声明多个feign实例时,启动时直接报错。
解决办法,通过 Feign.builder() 手动生成代理类。
1.定义接口:
public interface AbcClient{
@ResponseBody
@PostMapping("/abc")
JSONObject doSomething(@RequestBody Req request);
}
public interface DefClient{
@ResponseBody
@PostMapping("/def")
JSONObject doSomething(@RequestBody Req request);
}
2.配置接口代理
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import com.yunplus.bpg.cloud.file.proxy.client.QcloudClient;
import com.yunplus.bpg.cloud.file.proxy.client.TaskClient;
import feign.Contract;
import feign.Feign;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
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;
/**
* Feign配置类,负责实例化所需要的伪客户端
*/
@Component
public class RemoteXxxClient {
public final static String SERVICE_ID = "XXX-SERVICE-ID";
/**
* 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 AbcClient getQcloudService() {
return create(AbcClient.class, SERVICE_ID);
}
@Bean
public DefClient getTaskService() {
return create(DefClient.class, SERVICE_ID);
}
/**
* 设置编码解码器为FastJson
*
* @param clazz
* @param serverId
* @param <T>
* @return
*/
protected <T> T create(Class<T> clazz, String serverId) {
InstanceInfo nextServerFromEureka = eurekaClient.getNextServerFromEureka(serverId, false);
Object object = FEIGN_CLIENTS.get(nextServerFromEureka.getIPAddr());
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))
//target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
.target(clazz, nextServerFromEureka.getHomePageUrl());
FEIGN_CLIENTS.put(nextServerFromEureka.getIPAddr(), 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);
FastJsonConfig config = new FastJsonConfig();
config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
return converter;
}
}
3.调用
@Slf4j
@Service
public class MyLogic {
@Autowired
private AbcClient abcClient;
public void callDownstreamService() {
Req req = new Req();
JSONObject rsp = abcClient.doSomething(req);
}
}
PS:
@FeignClient同一个name使用多个配置类的解决方案
来源:oschina
链接:https://my.oschina.net/u/4406182/blog/3236249