Spring Cloud——Feign設(shè)計(jì)原理

什么是Feign?

Feign 的英文表意為“假裝,偽裝,變形”, 是一個(gè)http請(qǐng)求調(diào)用的輕量級(jí)框架,可以以Java接口注解的方式調(diào)用Http請(qǐng)求,而不用像Java中通過封裝HTTP請(qǐng)求報(bào)文的方式直接調(diào)用。Feign通過處理注解,將請(qǐng)求模板化,當(dāng)實(shí)際調(diào)用的時(shí)候,傳入?yún)?shù),根據(jù)參數(shù)再應(yīng)用到請(qǐng)求上,進(jìn)而轉(zhuǎn)化成真正的請(qǐng)求,這種請(qǐng)求相對(duì)而言比較直觀。

Feign被廣泛應(yīng)用在Spring Cloud 的解決方案中,是學(xué)習(xí)基于Spring Cloud 微服務(wù)架構(gòu)不可或缺的重要組件。

開源項(xiàng)目地址:https://github.com/OpenFeign/feign

Feign解決了什么問題?

封裝了Http調(diào)用流程,更適合面向接口化的編程習(xí)慣
在服務(wù)調(diào)用的場(chǎng)景中,我們經(jīng)常調(diào)用基于Http協(xié)議的服務(wù),而我們經(jīng)常使用到的框架可能有HttpURLConnection、Apache HttpComponnets、OkHttp3 、Netty等等,這些框架在基于自身的專注點(diǎn)提供了自身特性。而從角色劃分上來看,他們的職能是一致的提供Http調(diào)用服務(wù)。具體流程如下:


Feign是如何設(shè)計(jì)的?

工作原理

  • 主程序入口添加了@EnableFeignClients注解開啟對(duì)FeignClient掃描加載處理。根據(jù)Feign Client的開發(fā)規(guī)范,定義接口并加@FeignClientd注解。

  • 當(dāng)程序啟動(dòng)時(shí),會(huì)進(jìn)行包掃描,掃描所有@FeignClients的注解的類,并且將這些信息注入Spring IOC容器中,當(dāng)定義的的Feign接口中的方法被調(diào)用時(shí),通過JDK動(dòng)態(tài)代理方式,來生成具體的RequestTemplate。當(dāng)生成代理時(shí),F(xiàn)eign會(huì)為每個(gè)接口方法創(chuàng)建一個(gè)RequestTemplate對(duì)象,該對(duì)象封裝可HTTP請(qǐng)求需要的全部信息,如請(qǐng)求參數(shù)名,請(qǐng)求方法等信息都是在這個(gè)過程中確定的。

  • 然后RequestTemplate生成Request,然后把Request交給Client去處理,這里指的Client可以是JDK原生的URLConnection、Apache的HttpClient、也可以是OKhttp,最后Client被封裝到LoadBalanceClient類,這個(gè)類結(jié)合Ribbon負(fù)載均衡發(fā)起服務(wù)之間的調(diào)用。

1、基于面向接口的JDK動(dòng)態(tài)代理方式生成實(shí)現(xiàn)類

在使用feign 時(shí),會(huì)定義對(duì)應(yīng)的接口類,在接口類上使用Http相關(guān)的注解,標(biāo)識(shí)HTTP請(qǐng)求參數(shù)信息,如下所示:

interface GitHub {
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

public static class Contributor {
    String login;
    int contributions;
}

public class MyApp {
    public static void main(String... args) {
        GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");
  
        // Fetch and print a list of the contributors to this library.
        List<Contributor> contributors = github.contributors("OpenFeign", "feign");
        for (Contributor contributor : contributors) {
            System.out.println(contributor.login + " (" + contributor.contributions + ")");
        }
    }
}

在Feign 底層,通過基于面向接口的動(dòng)態(tài)代理方式生成實(shí)現(xiàn)類,將請(qǐng)求調(diào)用委托到動(dòng)態(tài)代理實(shí)現(xiàn)類,基本原理如下所示:


public class ReflectiveFeign extends Feign {

    @Override
    public <T> T newInstance(Target<T> target) {
        //根據(jù)接口類和Contract協(xié)議解析方式,解析接口類上的方法和注解,轉(zhuǎn)換成內(nèi)部的MethodHandler處理方式
        Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

        for (Method method : target.type().getMethods()) {
            if (method.getDeclaringClass() == Object.class) {
                continue;
            } else if (Util.isDefault(method)) {
                DefaultMethodHandler handler = new DefaultMethodHandler(method);
                defaultMethodHandlers.add(handler);
                methodToHandler.put(method, handler);
            } else {
                methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
            }
        }
        InvocationHandler handler = factory.create(target, methodToHandler);
        // 基于Proxy.newProxyInstance 為接口類創(chuàng)建動(dòng)態(tài)實(shí)現(xiàn),將所有的請(qǐng)求轉(zhuǎn)換給InvocationHandler 處理。
        T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);

        for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
            defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
    }
}

2、根據(jù)Contract協(xié)議規(guī)則,解析接口類的注解信息,解析成內(nèi)部表現(xiàn)

Feign 定義了轉(zhuǎn)換協(xié)議,定義如下:

public interface Contract {

    //傳入接口定義,解析成相應(yīng)的方法內(nèi)部元數(shù)據(jù)表示
    List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType);
}
默認(rèn)Contract 實(shí)現(xiàn)

Feign 默認(rèn)有一套自己的協(xié)議規(guī)范,規(guī)定了一些注解,可以映射成對(duì)應(yīng)的Http請(qǐng)求,如官方的一個(gè)例子:

public interface GitHub {
  
    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> getContributors(@Param("owner") String owner, @Param("repo") String repository);
  
    class Contributor {
        String login;
        int contributions;
    }
}

上述的例子中,嘗試調(diào)用GitHub.getContributors(“foo”,“myrepo”)的的時(shí)候,會(huì)轉(zhuǎn)換成如下的HTTP請(qǐng)求:

GET /repos/foo/myrepo/contributors
HOST XXXX.XXX.XXX
Feign 默認(rèn)的協(xié)議規(guī)范
注解 接口Target 使用說明
@RequestLine 方法上 定義HttpMethod 和 UriTemplate. UriTemplate 中使用{} 包裹的表達(dá)式,可以通過在方法參數(shù)上使用@Param 自動(dòng)注入
@Param 方法參數(shù) 定義模板變量,模板變量的值可以使用名稱的方式使用模板注入解析
@Headers 類上或者方法上 定義頭部模板變量,使用@Param 注解提供參數(shù)值的注入。如果該注解添加在接口類上,則所有的請(qǐng)求都會(huì)攜帶對(duì)應(yīng)的Header信息;如果在方法上,則只會(huì)添加到對(duì)應(yīng)的方法請(qǐng)求上
@QueryMap 方法上 定義一個(gè)鍵值對(duì)或者 pojo,參數(shù)值將會(huì)被轉(zhuǎn)換成URL上的 query 字符串上
@HeaderMap 方法上 定義一個(gè)HeaderMap, 與 UrlTemplate 和HeaderTemplate 類型,可以使用@Param 注解提供參數(shù)值

具體FeignContract 是如何解析的,詳情請(qǐng)參考代碼:
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Contract.java

2.1、基于Spring MVC的協(xié)議規(guī)范SpringMvcContract

OpenFeign是Spring Cloud 在Feign的基礎(chǔ)上支持了Spring MVC的注解
當(dāng)前Spring Cloud 微服務(wù)解決方案中spring-cloud-starter-openfeign,在Feign的基礎(chǔ)上支持了Spring MVC的注解,OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,也就是說 ,寫客戶端請(qǐng)求接口和像寫服務(wù)端代碼一樣:客戶端和服務(wù)端可以通過SDK的方式進(jìn)行約定,客戶端只需要引入服務(wù)端發(fā)布的SDK API,就可以使用面向接口的編碼方式對(duì)接服務(wù):

3、基于RequestBean動(dòng)態(tài)生成Request

根據(jù)傳入的Bean對(duì)象和注解信息,從中提取出相應(yīng)的值,來構(gòu)造Http Request 對(duì)象:


4、使用Encoder 將Bean轉(zhuǎn)換成 Http報(bào)文正文(消息解析和轉(zhuǎn)碼邏輯)

Feign 最終會(huì)將請(qǐng)求轉(zhuǎn)換成Http 消息發(fā)送出去,傳入的請(qǐng)求對(duì)象最終會(huì)解析成消息體,如下所示:


在接口定義上Feign做的比較簡單,抽象出了Encoder 和decoder 接口:

public interface Encoder {
    Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD;

    //將實(shí)體對(duì)象轉(zhuǎn)換成Http請(qǐng)求的消息正文中
    void encode(Object var1, Type var2, RequestTemplate var3) throws EncodeException;

    public static class Default implements Encoder {
        public Default() {
        }

        public void encode(Object object, Type bodyType, RequestTemplate template) {
            if (bodyType == String.class) {
                template.body(object.toString());
            } else if (bodyType == byte[].class) {
                template.body((byte[])((byte[])object), (Charset)null);
            } else if (object != null) {
                throw new EncodeException(String.format("%s is not a type supported by this encoder.", object.getClass()));
            }

        }
    }
}
public interface Decoder {

    //從Response 中提取Http消息正文,通過接口類聲明的返回類型,消息自動(dòng)裝配
    Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;
    
    public class Default extends StringDecoder {

        @Override
        public Object decode(Response response, Type type) throws IOException {
            if (response.status() == 404 || response.status() == 204)
                return Util.emptyValueOf(type);
            if (response.body() == null)
                return null;
            if (byte[].class.equals(type)) {
                return Util.toByteArray(response.body().asInputStream());
            }
            return super.decode(response, type);
        }
    }
}   

目前Feign 有以下實(shí)現(xiàn):

Encoder/ Decoder 實(shí)現(xiàn) 說明
JacksonEncoder,JacksonDecoder 基于 Jackson 格式的持久化轉(zhuǎn)換協(xié)議
GsonEncoder,GsonDecoder 基于Google GSON 格式的持久化轉(zhuǎn)換協(xié)議
SaxEncoder,SaxDecoder 基于XML 格式的Sax 庫持久化轉(zhuǎn)換協(xié)議
JAXBEncoder,JAXBDecoder 基于XML 格式的JAXB 庫持久化轉(zhuǎn)換協(xié)議
ResponseEntityEncoder,ResponseEntityDecoder Spring MVC 基于ResponseEntity< T > 返回格式的轉(zhuǎn)換協(xié)議
SpringEncoder,SpringDecoder 基于Spring MVC HttpMessageConverters 一套機(jī)制實(shí)現(xiàn)的轉(zhuǎn)換協(xié)議 ,應(yīng)用于Spring Cloud 體系中

5、攔截器負(fù)責(zé)對(duì)請(qǐng)求和返回進(jìn)行裝飾處理

在請(qǐng)求轉(zhuǎn)換的過程中,F(xiàn)eign 抽象出來了攔截器接口,用于用戶自定義對(duì)請(qǐng)求的操作:

public interface RequestInterceptor {

    /**
     * 可以在構(gòu)造RequestTemplate 請(qǐng)求時(shí),增加或者修改Header, Method, Body 等信息
     */
    void apply(RequestTemplate template);
}

比如,如果希望Http消息傳遞過程中被壓縮,可以定義一個(gè)請(qǐng)求攔截器:

public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {

    /**
     * Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
     * @param properties the encoding properties
     */
    protected FeignAcceptGzipEncodingInterceptor(
            FeignClientEncodingProperties properties) {
        super(properties);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void apply(RequestTemplate template) {
        //  在Header 頭部添加相應(yīng)的數(shù)據(jù)信息
        addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER,
                HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
    }

}

6、日志記錄

在發(fā)送和接收請(qǐng)求的時(shí)候,F(xiàn)eign定義了統(tǒng)一的日志門面來輸出日志信息 , 并且將日志的輸出定義了四個(gè)等級(jí):

級(jí)別 說明
NONE 不做任何記錄
BASIC 只記錄輸出Http 方法名稱、請(qǐng)求URL、返回狀態(tài)碼和執(zhí)行時(shí)間
HEADERS 記錄輸出Http 方法名稱、請(qǐng)求URL、返回狀態(tài)碼和執(zhí)行時(shí)間 和 Header 信息
FULL 記錄Request 和Response的Header,Body和一些請(qǐng)求元數(shù)據(jù)
public abstract class Logger {

    /**
     * Controls the level of logging.
     */
    public enum Level {
        /**
         * No logging.
         */
        NONE,
        /**
         * Log only the request method and URL and the response status code and execution time.
         */
        BASIC,
        /**
         * Log the basic information along with request and response headers.
         */
        HEADERS,
        /**
         * Log the headers, body, and metadata for both requests and responses.
         */
        FULL
    }
}

7、基于重試器發(fā)送HTTP請(qǐng)求

Feign 內(nèi)置了一個(gè)重試器,當(dāng)HTTP請(qǐng)求出現(xiàn)IO異常時(shí),F(xiàn)eign會(huì)有一個(gè)最大嘗試次數(shù)發(fā)送請(qǐng)求,以下是Feign核心
代碼邏輯:

final class SynchronousMethodHandler implements MethodHandler {

    @Override
    public Object invoke(Object[] argv) throws Throwable {
        //根據(jù)輸入?yún)?shù),構(gòu)造Http 請(qǐng)求。
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        Options options = findOptions(argv);
        // 克隆出一份重試器
        Retryer retryer = this.retryer.clone();
        // 嘗試最大次數(shù),如果中間有結(jié)果,直接返回
        while (true) {
            try {
                return executeAndDecode(template, options);
            } catch (RetryableException e) {
                try {
                    retryer.continueOrPropagate(e);
                } catch (RetryableException th) {
                    Throwable cause = th.getCause();
                    if (propagationPolicy == UNWRAP && cause != null) {
                        throw cause;
                    } else {
                        throw th;
                    }
                }
                if (logLevel != Logger.Level.NONE) {
                    logger.logRetry(metadata.configKey(), logLevel);
                }
                continue;
            }
        }
    }
}

重試器有如下幾個(gè)控制參數(shù):

重試參數(shù) 說明 默認(rèn)值
period 初始重試時(shí)間間隔,當(dāng)請(qǐng)求失敗后,重試器將會(huì)暫停 初始時(shí)間間隔(線程 sleep 的方式)后再開始,避免強(qiáng)刷請(qǐng)求,浪費(fèi)性能 100ms
maxPeriod 當(dāng)請(qǐng)求連續(xù)失敗時(shí),重試的時(shí)間間隔將按照:long interval = (long) (period * Math.pow(1.5, attempt - 1)); 計(jì)算,按照等比例方式延長,但是最大間隔時(shí)間為 maxPeriod, 設(shè)置此值能夠避免 重試次數(shù)過多的情況下執(zhí)行周期太長 1000ms
maxAttempts 最大重試次數(shù) 5

具體的代碼實(shí)現(xiàn)可參考:
https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Retryer.java

8、發(fā)送Http請(qǐng)求

Feign 真正發(fā)送HTTP請(qǐng)求是委托給 feign.Client 來做的:

public interface Client {
    
    //執(zhí)行Http請(qǐng)求,并返回Response
    Response execute(Request request, Options options) throws IOException;
    
}

Feign 默認(rèn)底層通過JDK 的 java.net.HttpURLConnection 實(shí)現(xiàn)了feign.Client接口類,在每次發(fā)送請(qǐng)求的時(shí)候,都會(huì)創(chuàng)建新的HttpURLConnection 鏈接,這也就是為什么默認(rèn)情況下Feign的性能很差的原因??梢酝ㄟ^拓展該接口,使用Apache HttpClient 或者OkHttp3等基于連接池的高性能Http客戶端,我們項(xiàng)目內(nèi)部使用的就是OkHttp3作為Http 客戶端。

三、Feign性能優(yōu)化

Feign 整體框架非常小巧,在處理請(qǐng)求轉(zhuǎn)換和消息解析的過程中,基本上沒什么時(shí)間消耗。真正影響性能的,是處理Http請(qǐng)求的環(huán)節(jié)。由于默認(rèn)情況下,F(xiàn)eign采用的是JDK的HttpURLConnection,所以整體性能并不高。需要進(jìn)行性能優(yōu)化,通常采用ApacheHttpClient或者OKHttp,加入連接池技術(shù)。

3.1、使用ApacheHttpClient

相關(guān)類:

org.springframework.cloud.openfeign.ribbon.HttpClientFeignLoadBalancedConfiguration
org.springframework.cloud.openfeign.support.FeignHttpClientProperties

引入依賴:

<!-- Http Client 支持 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

<!-- Apache Http Client 對(duì) Feign 支持 -->
<dependency>
    <groupId>com.netflix.feign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>${feign-httpclient.version}</version>
</dependency>

配置文件:

### Feign 配置
feign:
  httpclient:
    # 開啟 Http Client
    enabled: true
    # 最大連接數(shù),默認(rèn):200
    max-connections: 200
    # 最大路由,默認(rèn):50
    max-connections-per-route: 50
    # 連接超時(shí),默認(rèn):2000/毫秒
    connection-timeout: 2000
    # 生存時(shí)間,默認(rèn):900L
    time-to-live: 900
    # 響應(yīng)超時(shí)的時(shí)間單位,默認(rèn):TimeUnit.SECONDS
    #timeToLiveUnit: SECONDS

注意:
ApacheHttpClient的請(qǐng)求鏈接數(shù)是由最大連接數(shù)和最大路由數(shù)共同決定,最大路由數(shù)默認(rèn)是2,這里一定要進(jìn)行設(shè)置。不然會(huì)出現(xiàn)大量線程阻塞,等待獲取http鏈接,直到超時(shí)的異常(wait timeout)。

3.2、使用OKHttp

OKHttp 是現(xiàn)在比較常用的一個(gè) HTTP 客戶端訪問工具,具有以下特點(diǎn):

  • 支持 SPDY,可以合并多個(gè)到同一個(gè)主機(jī)的請(qǐng)求。
  • 使用連接池技術(shù)減少請(qǐng)求的延遲(如果SPDY是可用的話)。
  • 使用 GZIP 壓縮減少傳輸?shù)臄?shù)據(jù)量。
  • 緩存響應(yīng)避免重復(fù)的網(wǎng)絡(luò)請(qǐng)求。

相關(guān)類:

org.springframework.cloud.openfeign.FeignAutoConfiguration.OkHttpFeignConfiguration

引入依賴:

<!-- OKHttp 對(duì) Feign 支持 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

配置文件:

### Feign 配置
feign:
  httpclient:
    # 是否開啟 Http Client
    enabled: false
#    # 最大連接數(shù),默認(rèn):200
#    max-connections: 200
#    # 最大路由,默認(rèn):50
#    max-connections-per-route: 50
#    # 連接超時(shí),默認(rèn):2000/毫秒
#    connection-timeout: 2000
#    # 生存時(shí)間,默認(rèn):900L
#    time-to-live: 900
#    # 響應(yīng)超時(shí)的時(shí)間單位,默認(rèn):TimeUnit.SECONDS
##    timeToLiveUnit: SECONDS
  okhttp:
    enabled: true

配置類:

/**
 * @Description:Feign 底層使用 OKHttp 訪問配置
 */
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignClientOkHttpConfiguration {

    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
                // 連接超時(shí)
                .connectTimeout(20, TimeUnit.SECONDS)
                // 響應(yīng)超時(shí)
                .readTimeout(20, TimeUnit.SECONDS)
                // 寫超時(shí)
                .writeTimeout(20, TimeUnit.SECONDS)
                // 是否自動(dòng)重連
                .retryOnConnectionFailure(true)
                // 連接池
                .connectionPool(new ConnectionPool())
                .build();
    }

}

注意:
如果發(fā)現(xiàn)配置的超時(shí)時(shí)間無效,可以添加以下配置,因?yàn)樽x取超時(shí)配置的時(shí)候沒有讀取上面的okhttp的配置參數(shù),而是從Request中讀取。具體配置如下所示:

@Bean
public Request.Options options(){
    return new Request.Options(60000,60000);
}

3.3、undertow替換tomcat

如果將tomcat 換成 undertow,這個(gè)性能在 Jmeter 的壓測(cè)下,undertow 比 tomcat 高一倍。

pom文件:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

配置項(xiàng):

server: 
  undertow: 
    max-http-post-size: 0 
# 設(shè)置IO線程數(shù), 它主要執(zhí)行非阻塞的任務(wù),它們會(huì)負(fù)責(zé)多個(gè)連接, 默認(rèn)設(shè)置每個(gè)CPU核心一個(gè)線程,數(shù)量和CPU 內(nèi)核數(shù)目一樣即可
    io-threads: 4
# 阻塞任務(wù)線程池, 當(dāng)執(zhí)行類似servlet請(qǐng)求阻塞操作, undertow會(huì)從這個(gè)線程池中取得線程,它的值設(shè)置取決于系統(tǒng)的負(fù)載  io-threads*8
    worker-threads: 32
# 以下的配置會(huì)影響buffer,這些buffer會(huì)用于服務(wù)器連接的IO操作,有點(diǎn)類似netty的池化內(nèi)存管理
# 每塊buffer的空間大小,越小的空間被利用越充分
    buffer-size: 1024
# 每個(gè)區(qū)分配的buffer數(shù)量 , 所以pool的大小是buffer-size * buffers-per-region
#   buffers-per-region: 1024 # 這個(gè)參數(shù)不需要寫了
# 是否分配的直接內(nèi)存
    direct-buffers: true

參考:
https://www.icode9.com/content-4-628558.html

https://blog.csdn.net/luanlouis/article/details/82821294

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、服務(wù)發(fā)現(xiàn)(Eureka/Consul/ZooKeeper) 1、Eureka介紹 Netflix Eureka...
    c_gentle閱讀 1,114評(píng)論 0 2
  • 本文作者:sytyale,另外一個(gè)聰明好學(xué)的同事 一、原理 Feign 是一個(gè) Java 到 HTTP 的客戶端綁...
    hackingForest閱讀 3,226評(píng)論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 一、@EnableFeignClients詳解 1、作用 掃描和注冊(cè)所有使用注解@FeignClient定義的fe...
    楊健kimyeung閱讀 3,094評(píng)論 1 0
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn),但是人生放棄了冒險(xiǎn),也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 7,573評(píng)論 0 4

友情鏈接更多精彩內(nèi)容