微服务SpringCloud中使用越来越多的服务将业务进行隔离,相互之间使用feign进行调用,但是在使用的时候我们遇到一个问题,每次调用一个新的服务的使用,都要手动的使用@FeignClient注解去申明调用一个服务,问题来了,如果需要调用多个服务,那我们是不是就需要手动的配置多个,于是找了很多博客后,加上自己的实际实践,总结了feign组件的动态调用,可以方便我们的程序开发,省去很多不必要的代码,但由此可能会带来一些feign的性能问题,没有详细测试,希望使用的朋友们不要忘了测试一下性能哈
- 首先,我们需要添加pom依赖,注意版本号
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.2.3</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.2.3</version>
</dependency>
- 添加一个FeignUtil调用组件工具类
package com.icss.instrutry.product.nap.centre.buss.utils;
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 feign.Client;
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.FeignContext;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class FeignUtil {
/**
* FeignClientFactoryBean 该工厂类中 设置builder属性时就是通过该对象,源码中可看到
*/
@Autowired
protected FeignContext feignContext;
/**
* FeignClient 默认LoadBalancerFeignClient
*/
@Autowired
private Client feignClient;
/**
* 通过注入Eureka实例对象,就不用手动指定url,只需要指定服务名即可
*/
@Autowired
protected EurekaClient eurekaClient;
private static final Map<String, Object> FEIGN_CLIENTS = new ConcurrentHashMap<>();
/**
* 定义远程通用接口
*/
public interface RemoteFeignClient {
@ResponseBody
@PostMapping(consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
Map doSomething(URI uri, Map param);
}
/**
* @param serverId
* @return
*/
public RemoteFeignClient getClient(String serverId) {
return this.create(RemoteFeignClient.class, serverId);
}
/**
* 设置编码解码器为FastJson
* @param clazz
* @param serverId
* @param <T>
* @return
*/
private <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;
}
@Bean
private Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
@Bean
private 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;
}
}
- 使用工具类实现动态调用
URI uri = new URI(serviceUrl);
resultMap = feignUtil.getClient(uri.getHost()).doSomething(uri, map);
关于调用说明:
这里的serviceUrl相当于我们实际调用的服务接口地址
eg: http://BUSS-SERVICE/microPay
map 相当于我们的传递参数封装
RemoteFeignClient 相当于我们日常封装的@FeignClient
doSomething()方法相当于我们实际调用对方服务的接口路径
核心代码就是这样了,希望对小伙伴们有所帮助哦