5、Spring Cloud-声明式调用 Feign(下)
5.5、在Feign中使用HttpClient和OkhHttp
Feign 中、Client 是一个非常重要的组件,
Feign 最终发送 Request 请求以及接收 Response响应都是由 Client 组件完成的。
Client在Feign 源码中是一 个接口,
默认的情况下:
Client的实现类是 Client.Default
Client.Default 是由 HttpURLConnnection 来实现网络请求的。
Client 支持 HttpClient、OkhHttp 来进行网络请求。
FeignRibbonClient 的自动配置类 FeignRibbonClientAutoConfiguration
FeignRibbonClientAutoConfiguration.java
@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; } }
1、若要使用HttpClient
在pom添加依赖:
<dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
配置文件中:
feign.httpclient.enabled=true
从新启动即可(具体看源码进行分析)
2、同理若要使用OkHttp
添加依赖:
配置文件中:
feign.okhttp.enabled=true
再次启动即可....
5.6、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执行请求的方法
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()方法:该方法就是选择服务进行负载均衡的方法
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来处理
5.7、总结
1、首先通过@EnableFeignClients 注解开启 FeignClient 的功能。只有这个注解存在,才
会在程序启动时开启对@FeignClient 注解的包扫描
2、根据 Feign的规则实现接口,并在接口上面加上@FeignClient注解
3、程序启动后,会进行包扫描,扫描所有的@ FeignClient 注解 ,并将这些信息注入 IoC 容器中。
4、当接口的方法被调用时 通过 JDK 的代理来生成 体的 RequestTemplate模板对象
5、根据 RequestTemplate 再生成 Http 请求的 Request 对象
6、Request 对象交给 Client 去处理 其中 Client 的网络请求框架可以是 HttpURLConnect on、HttpClient、OkHttp
7、最后 Client 被封装到 LoadBalanceClient 类,这个类结合类 Ribbon 做到了负载均衡