zoukankan      html  css  js  c++  java
  • 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使用多个配置类的解决方案

  • 相关阅读:
    Android Init语言
    Android Framework------之Input子系统
    Android Framework------之Keyguard 简单分析
    Android Framework------之ActivityManagerService与Activity之间的通信
    Android Framework------之PowerManagerService的功能
    蓝牙广播数据格式和动态改变
    蓝牙DA14580开发:固件格式、二次引导和烧写
    嵌入式调试器原理和各类调试器集锦(JLINK、STLINK、CCDEBUG)
    微信智能跑鞋技术解密
    国内物联网平台的发展、技术架构演进暨物联网解决方案发布
  • 原文地址:https://www.cnblogs.com/phpdragon/p/12119128.html
Copyright © 2011-2022 走看看