Spring Cloud 中 Feign 不捆绑远程应用名称, 实现服务应用名称传入,调用通用自动化

Feign 版本10.1.0

Spring 版本 5.1.5.RELEASE

SpringBoot 版本 2.1.5.RELEASE

SpringCloud 版本 2.1.1.RELEASE

大家知道,使用Feigen调用远端微服务的代码大致如下:

定义接口参数:

@FeignClient(name = "remote-service-name")
public interface XxxServiceClient{

    @PostMapping(value = "/xxx/xxx", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    JSONObject callXxx(@RequestBody Xxx param);
}

然后如下调用:

@Slf4j
@Service
public class MyLogic {

    @Autowired
    private XxxServiceClient xxxServiceClient;
    
    public void sendMqMessage() {
        Xxx param = new Xxx();
        JSONObject rsp = xxxServiceClient.callXxx(param);
    }
}

但一般的常规应用、业务如此调用没什么问题。但如果该应用是一个转发代理、或者路由网关服务。那么如此调用就很不合理,而且硬编码的化代码臃肿且不可配置化。

网上找了很多资料,发现如下用法可行。

扫描二维码关注公众号,回复: 8363161 查看本文章

方案:

先编写一个组件类:

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.wechat.proxy.dto.mp.msg.notify.NotifyEventDto;
import com.yunplus.bpg.wechat.proxy.dto.mp.msg.reply.ReplyDiyMsgDto;
import com.yunplus.commons.misc.dto.ApiResultDto;
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.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;


/**
 * 自定义feigen客户端配置
 *
 * @author phpdragon
 */
@Component
public class RemoteService {

    /**
     * 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)
        JSONObject doSomething(URI uri, Xxx  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;
    }

    private Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    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;
    }
}

然后如下调用:

@Slf4j
@Service
public class MyLogic {

    @Autowired
    private RemoteClientService  remoteClientService;
    
    public void callDownstreamService() {
        //这里的url直接使用SpringCloud应用名作为host
        String = "http://server-name/xxx/xxxx";
        URI uri = UrlUtil.newUri(url);
        Xxx param = new Xxx();
        JSONObject rsp = remoteService.getClient(uri.getHost()).doSomething(uri, param);
    }
}

如此,便实现了自动转发、调用代理等路由功能。然后再接口数据库,一个自动转发的服务遍完成。

PS:

https://www.jianshu.com/p/2a3965049f77

https://www.jianshu.com/p/3d597e9d2d67/

猜你喜欢

转载自www.cnblogs.com/phpdragon/p/12119065.html