美文网首页
(三)Feign之ribbon负载均衡http调用

(三)Feign之ribbon负载均衡http调用

作者: 天草二十六_简村人 | 来源:发表于2019-08-21 16:39 被阅读0次

    写在前面的话:
    http请求方式默认的是java.net包下的HttpURLConnection。
    另外,可以配置支持HttpClient和OkHttp两种调用。

    一、从spring.factories的EnableAutoConfiguration的配置类说起
    spring-cloud-openfeign-core-2.0.0.RELEASE.jar包下的spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
    org.springframework.cloud.openfeign.FeignAutoConfiguration,\
    org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
    org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
    
    image.png

    重点是前两个类FeignRibbonClientAutoConfiguration和FeignAutoConfiguration。后面两个是gzip压缩相关。

    二、FeignRibbonClientAutoConfiguration入口类

    package org.springframework.cloud.openfeign.ribbon;
    
    import com.netflix.loadbalancer.ILoadBalancer;
    import feign.Feign;
    import feign.Request.Options;
    import org.springframework.boot.autoconfigure.AutoConfigureBefore;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
    import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
    import org.springframework.cloud.openfeign.FeignAutoConfiguration;
    import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.Primary;
    
    @ConditionalOnClass({ILoadBalancer.class, Feign.class})
    @Configuration
    // 自动配置先于org.springframework.cloud.openfeign.FeignAutoConfiguration
    @AutoConfigureBefore({FeignAutoConfiguration.class})
    // http连接相关的配置
    @EnableConfigurationProperties({FeignHttpClientProperties.class})
    // HttpClient调用LB,OkHttp调用LB,默认Http调用LB
    @Import({HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class})
    public class FeignRibbonClientAutoConfiguration {
        public FeignRibbonClientAutoConfiguration() {
        }
    
        @Bean
        @Primary
        @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
        public CachingSpringLoadBalancerFactory cachingLBClientFactory(SpringClientFactory factory) {
            return new CachingSpringLoadBalancerFactory(factory);
        }
    
        @Bean
        @Primary
        @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;
        }
    }
    
    

    画外音:当一个接口对应多个实现类,优先选择用@Primary注解的bean。
    feign.Request的内部类

    public static class Options {
            private final int connectTimeoutMillis;
            private final int readTimeoutMillis;
    
            public Options(int connectTimeoutMillis, int readTimeoutMillis) {
                this.connectTimeoutMillis = connectTimeoutMillis;
                this.readTimeoutMillis = readTimeoutMillis;
            }
    
            public Options() {
                this(10000, 60000);
            }
    
            public int connectTimeoutMillis() {
                return this.connectTimeoutMillis;
            }
    
            public int readTimeoutMillis() {
                return this.readTimeoutMillis;
            }
        }
    

    三、具体的HttpClient调用LB的实现,OkHttp的实现就略过,因为和HttpClient差不多的实现。

    package org.springframework.cloud.openfeign.ribbon;
    
    import feign.Client;
    import feign.httpclient.ApacheHttpClient;
    import java.util.Timer;
    import java.util.TimerTask;
    import javax.annotation.PreDestroy;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.config.RegistryBuilder;
    import org.apache.http.conn.HttpClientConnectionManager;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
    import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
    import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
    import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ConditionalOnClass({ApacheHttpClient.class})
    @ConditionalOnProperty(
        value = {"feign.httpclient.enabled"},
        matchIfMissing = true
    )
    class HttpClientFeignLoadBalancedConfiguration {
        HttpClientFeignLoadBalancedConfiguration() {
        }
    
        @Bean
        // 实例化的条件是feign.Client的bean不存在
    // 可以看出,http的调用默认是Client类
        @ConditionalOnMissingBean({Client.class})
        public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory, HttpClient httpClient) {
            ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
            return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
        }
    
        @Configuration
        @ConditionalOnMissingBean({CloseableHttpClient.class})
    // 定义内部类
        protected static class HttpClientFeignConfiguration {
    // 定时器
            private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
            private CloseableHttpClient httpClient;
            @Autowired(
                required = false
            )
            private RegistryBuilder registryBuilder;
    
            protected HttpClientFeignConfiguration() {
            }
    
            @Bean
            @ConditionalOnMissingBean({HttpClientConnectionManager.class})
            public HttpClientConnectionManager connectionManager(ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) {
                final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
    // 关闭过期的连接
                this.connectionManagerTimer.schedule(new TimerTask() {
                    public void run() {
                        connectionManager.closeExpiredConnections();
                    }
                }, 30000L, (long)httpClientProperties.getConnectionTimerRepeat());
                return connectionManager;
            }
    
            @Bean
            @ConditionalOnProperty(
                value = {"feign.compression.response.enabled"},
                havingValue = "true"
            )
            public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
                HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();
                this.httpClient = this.createClient(builder, httpClientConnectionManager, httpClientProperties);
                return this.httpClient;
            }
    
            @Bean
            @ConditionalOnProperty(
                value = {"feign.compression.response.enabled"},
                havingValue = "false",
                matchIfMissing = true
            )
            public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
                this.httpClient = this.createClient(httpClientFactory.createBuilder(), httpClientConnectionManager, httpClientProperties);
                return this.httpClient;
            }
    
            private CloseableHttpClient createClient(HttpClientBuilder builder, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
                RequestConfig defaultRequestConfig = RequestConfig.custom().setConnectTimeout(httpClientProperties.getConnectionTimeout()).setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
                CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig).setConnectionManager(httpClientConnectionManager).build();
                return httpClient;
            }
    
            @PreDestroy
    // 释放资源
            public void destroy() throws Exception {
                this.connectionManagerTimer.cancel();
                if (this.httpClient != null) {
                    this.httpClient.close();
                }
    
            }
        }
    }
    
    

    四、DefaultFeignLoadBalancedConfiguration

    package org.springframework.cloud.openfeign.ribbon;
    
    import feign.Client;
    import feign.Client.Default;
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.SSLSocketFactory;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    class DefaultFeignLoadBalancedConfiguration {
        DefaultFeignLoadBalancedConfiguration() {
        }
    
        @Bean
        @ConditionalOnMissingBean
    // 注意这里返回就是feign.Client
    // 实现feign.Client的类是LoadBalancerFeignClient
        public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
    //Default类作为内部类,是Client接口的默认实现,        
    return new LoadBalancerFeignClient(new Default((SSLSocketFactory)null, (HostnameVerifier)null), cachingFactory, clientFactory);
        }
    }
    

    五、前面都提到了feign.Client,我们有必要看下LoadBalancerFeignClient和Client的具体源码。
    1、接口Client

    public interface Client {
        Response execute(Request var1, Options var2) throws IOException;
    
        // 在接口里嵌套定义了一个内部类
        public static class Default implements Client {
            // 源码这里就不贴出来了
        }
    }
    
    Default内部类的实现采用java.net.HttpURLConnection.png

    六、org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient实现接口feign.Client

    public class LoadBalancerFeignClient implements Client {
    // 见上
        static final Options DEFAULT_OPTIONS = new Options();
        private final Client delegate;
        private CachingSpringLoadBalancerFactory lbClientFactory;
        private SpringClientFactory clientFactory;
    
        public LoadBalancerFeignClient(Client delegate, CachingSpringLoadBalancerFactory lbClientFactory, SpringClientFactory clientFactory) {
            this.delegate = delegate;
            this.lbClientFactory = lbClientFactory;
            this.clientFactory = clientFactory;
        }
    }
    

    七、org.springframework.cloud.openfeign.FeignAutoConfiguration的实现

    package org.springframework.cloud.openfeign;
    
    import feign.Client;
    import feign.Feign;
    import feign.httpclient.ApacheHttpClient;
    import feign.okhttp.OkHttpClient;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.concurrent.TimeUnit;
    import javax.annotation.PreDestroy;
    import okhttp3.ConnectionPool;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.config.RegistryBuilder;
    import org.apache.http.conn.HttpClientConnectionManager;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.cloud.client.actuator.HasFeatures;
    import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
    import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
    import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
    import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
    import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ConditionalOnClass({Feign.class})
    //okhttp和httpclient共用配置FeignHttpClientProperties
    @EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
    public class FeignAutoConfiguration {
        @Autowired(
            required = false
        )
        private List<FeignClientSpecification> configurations = new ArrayList();
    
        public FeignAutoConfiguration() {
        }
    
        @Bean
        public HasFeatures feignFeature() {
            return HasFeatures.namedFeature("Feign", Feign.class);
        }
    
        @Bean
        public FeignContext feignContext() {
            FeignContext context = new FeignContext();
            context.setConfigurations(this.configurations);
            return context;
        }
    
        @Configuration
        @ConditionalOnClass({OkHttpClient.class})
        @ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
        @ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
        @ConditionalOnProperty({"feign.okhttp.enabled"})
        protected static class OkHttpFeignConfiguration {
         //源码略
        }
    
        @Configuration
        @ConditionalOnClass({ApacheHttpClient.class})
        @ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
        @ConditionalOnMissingBean({CloseableHttpClient.class})
        @ConditionalOnProperty(
            value = {"feign.httpclient.enabled"},
            matchIfMissing = true
        )
        protected static class HttpClientFeignConfiguration {
            //源码略
        }
    
        @Configuration
        @ConditionalOnMissingClass({"feign.hystrix.HystrixFeign"})
        protected static class DefaultFeignTargeterConfiguration {
            protected DefaultFeignTargeterConfiguration() {
            }
    
            @Bean
            @ConditionalOnMissingBean
            public Targeter feignTargeter() {
                return new DefaultTargeter();
            }
        }
    
        @Configuration
        @ConditionalOnClass(
            name = {"feign.hystrix.HystrixFeign"}
        )
        protected static class HystrixFeignTargeterConfiguration {
            protected HystrixFeignTargeterConfiguration() {
            }
    
            @Bean
            @ConditionalOnMissingBean
            public Targeter feignTargeter() {
                return new HystrixTargeter();
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:(三)Feign之ribbon负载均衡http调用

          本文链接:https://www.haomeiwen.com/subject/xzfisctx.html