五、Spring Cloud-聲明式調用 Feign(下)

5.五、在Feign中使用HttpClient和OkhHttp

Feign 中、Client 是一個很是重要的組件,
Feign 最終發送 Request 請求以及接收 Response響應都是由 Client 組件完成的。
 
Client在Feign 源碼中是一 個接口,
默認的狀況下:
Client的實現類是 Client.Default
Client.Default 是由 HttpURLConnnection 來實現網絡請求的。
Client 支持 HttpClient、OkhHttp 來進行網絡請求。
 
FeignRibbonClient 的自動配置類 FeignRibbonClientAutoConfiguration

FeignRibbonClientAutoConfiguration.javajava

@ConditionalOnClass({ILoadBalancer.class, Feign.class})
@Configuration
@AutoConfigureBefore({FeignAutoConfiguration.class})
@EnableConfigurationProperties({FeignHttpClientProperties.class})
@Import({HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class})
public class FeignRibbonClientAutoConfiguration {
    public FeignRibbonClientAutoConfiguration() {
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean
    @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    public CachingSpringLoadBalancerFactory cachingLBClientFactory(SpringClientFactory factory) {
        return new CachingSpringLoadBalancerFactory(factory);
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean
    @ConditionalOnClass(
        name = {"org.springframework.retry.support.RetryTemplate"}
    )
    public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
        return new CachingSpringLoadBalancerFactory(factory, retryFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    public Options feignRequestOptions() {
        return LoadBalancerFeignClient.DEFAULT_OPTIONS;
    }
}

 

一、若要使用HttpClient
在pom添加依賴:
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>

配置文件中:spring

feign.httpclient.enabled=true網絡

重新啓動便可(具體看源碼進行分析)負載均衡

 

二、同理若要使用OkHttp
添加依賴:

配置文件中:
feign.okhttp.enabled=true
 
再次啓動便可....

 

 5.六、Feign如何實現負載均衡

LoadBalancerFeignClient.java框架

public Response execute(Request request, Options options) throws IOException {
    try {
        URI asUri = URI.create(request.url());
        String clientName = asUri.getHost();
        URI uriWithoutHost = cleanUrl(request.url(), clientName);
        RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, uriWithoutHost);
        IClientConfig requestConfig = this.getClientConfig(options, clientName);
        return ((RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
    } catch (ClientException var8) {
        IOException io = this.findIOException(var8);
        if (io != null) {
            throw io;
        } else {
            throw new RuntimeException(var8);
        }
    }
}

 

execute執行請求的方法ui

 

executeWithLoadBalancer()方法:經過負載均衡的方式來執行網絡請求
代碼以下:
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    LoadBalancerCommand command = this.buildLoadBalancerCommand(request, requestConfig);

    try {
        return (IResponse)command.submit(new ServerOperation<T>() {
            public Observable<T> call(Server server) {
                URI finalUri = AbstractLoadBalancerAwareClient.this.reconstructURIWithServer(server, request.getUri());
                ClientRequest requestForServer = request.replaceUri(finalUri);

                try {
                    return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                } catch (Exception var5) {
                    return Observable.error(var5);
                }
            }
        }).toBlocking().single();
    } catch (Exception var6) {
        Throwable t = var6.getCause();
        if (t instanceof ClientException) {
            throw (ClientException)t;
        } else {
            throw new ClientException(var6);
        }
    }
}

 

 

submit()方法:
進入方法內部:
能夠看出它是LoadBalancerCommand類的方法
public Observable<T> submit(final ServerOperation<T> operation) {
    final LoadBalancerCommand<T>.ExecutionInfoContext context = new LoadBalancerCommand.ExecutionInfoContext();
    if (this.listenerInvoker != null) {
        try {
            this.listenerInvoker.onExecutionStart();
        } catch (AbortExecutionException var6) {
            return Observable.error(var6);
        }
    }

    final int maxRetrysSame = this.retryHandler.getMaxRetriesOnSameServer();
    final int maxRetrysNext = this.retryHandler.getMaxRetriesOnNextServer();
    Observable<T> o = (this.server == null ? this.selectServer() : Observable.just(this.server)).concatMap(new Func1<Server, Observable<T>>() { public Observable<T> call(Server server) { context.setServer(server); ......

 

上述代碼中有一個selectServe()方法:該方法就是選擇服務進行負載均衡的方法this

private Observable<Server> selectServer() {
    return Observable.create(new OnSubscribe<Server>() {
        public void call(Subscriber<? super Server> next) {
            try {
                Server server = LoadBalancerCommand.this.loadBalancerContext.getServerFromLoadBalancer(LoadBalancerCommand.this.loadBalancerURI, LoadBalancerCommand.this.loadBalancerKey);
                next.onNext(server);
                next.onCompleted();
            } catch (Exception var3) {
                next.onError(var3);
            }

        }
    });
}

 

 由上述可知,最終負載均衡交給loadbalancerContext來處理url

 

 

5.七、總結

一、首先經過@EnableFeignClients 註解開啓 FeignClient 的功能。只有這個註解存在,才
會在程序啓動時開啓對@FeignClient 註解的包掃描
二、根據 Feign的規則實現接口,並在接口上面加上@FeignClient註解
三、程序啓動後,會進行包掃描,掃描全部的@ FeignClient 註解 ,並將這些信息注入 IoC 容器中。
 
四、當接口的方法被調用時 經過 JDK 的代理來生成 體的 RequestTemplate模板對象
五、根據 RequestTemplate 再生成 Http 請求的 Request 對象
六、Request 對象交給 Client 去處理 其中 Client 的網絡請求框架能夠是 HttpURLConnect on、HttpClient、OkHttp
七、最後 Client 被封裝到 LoadBalanceClient 類,這個類結合類 Ribbon 作到了負載均衡
相關文章
相關標籤/搜索