关于SpringCloud的Feign组件的动态调用


微服务SpringCloud中使用越来越多的服务将业务进行隔离,相互之间使用feign进行调用,但是在使用的时候我们遇到一个问题,每次调用一个新的服务的使用,都要手动的使用@FeignClient注解去申明调用一个服务,问题来了,如果需要调用多个服务,那我们是不是就需要手动的配置多个,于是找了很多博客后,加上自己的实际实践,总结了feign组件的动态调用,可以方便我们的程序开发,省去很多不必要的代码,但由此可能会带来一些feign的性能问题,没有详细测试,希望使用的朋友们不要忘了测试一下性能哈

  1. 首先,我们需要添加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> 
  1. 添加一个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;
    }

}
  1. 使用工具类实现动态调用
URI uri = new URI(serviceUrl);
resultMap  = feignUtil.getClient(uri.getHost()).doSomething(uri, map);

关于调用说明:

这里的serviceUrl相当于我们实际调用的服务接口地址
eg: http://BUSS-SERVICE/microPay
map 相当于我们的传递参数封装

RemoteFeignClient 相当于我们日常封装的@FeignClient
doSomething()方法相当于我们实际调用对方服务的接口路径

核心代码就是这样了,希望对小伙伴们有所帮助哦

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值