SpringCloud : 多個 @FeignClient 注解 value 設置為同一個應用的解決方案


Feign 版本10.1.0

Spring 版本 5.1.5.RELEASE

SpringBoot 版本 2.1.5.RELEASE

SpringCloud 版本 2.1.1.RELEASE

 

在微服務架構中,當我們需要進行服務間調用時可以選擇feign組件,

現在遇到的問題是: 當同一個服務,聲明多個feign實例時,啟動時直接報錯。

 

解決辦法,通過 Feign.builder() 手動生成代理類。     另一種見評論區:#允許bean實例同名覆蓋 allow-bean-definition-overriding: true

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);
        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))
                //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);
        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:

feign 同一個服務編寫多個遠程調用實例 解決辦法

@FeignClient同一個name使用多個配置類的解決方案

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM