SpringCloud : 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);
    }
}

 

但一般的常規應用、業務如此調用沒什么問題。但如果該應用是一個轉發代理、或者路由網關服務。那么如此調用就很不合理,而且硬編碼的化代碼臃腫且不可配置化。

網上找了很多資料,發現如下用法可行。

 

方案:

先編寫一個組件類:

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/

 


免責聲明!

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



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