• SpringCloud之Feign实现原理


    一 开启ApacheHttpClient

      如果我们使用Apache的 HttpClient作为客户端的话,其实现逻辑就是这样的

    @Configuration
    @ConditionalOnClass({ApacheHttpClient.class})//classpath下必须得有ApacheHttpClient
    @ConditionalOnProperty(
        value = {"feign.httpclient.enabled"},
        matchIfMissing = true
    )
    class HttpClientFeignLoadBalancedConfiguration {
        //空的构造器
        HttpClientFeignLoadBalancedConfiguration() {
        }
    
        @Bean
        @ConditionalOnMissingBean({Client.class})
    public Client feignClient(
    CachingSpringLoadBalancerFactory cachingFactory,
    SpringClientFactory clientFactory, HttpClient httpClient) 
    {
            ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
            return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); // 进行包装
        }
    //…省略不相干的代码
    }

    pom文件里必须得有

         <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-httpclient</artifactId>
                <version>9.5.1</version>
                <!--<version>${feign-httpclient.version}</version>-->
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
            </dependency>

    观察下LoadBalancerFeignClient的代码

    public class LoadBalancerFeignClient implements Client {
    
        static final Request.Options DEFAULT_OPTIONS = new Request.Options();
    
        private final Client delegate;//实现类就是ApacheHttpClient
        private CachingSpringLoadBalancerFactory lbClientFactory;//获取ILoadBalancer的工厂类,也是Feign和Ribbon关联的纽带
        private SpringClientFactory clientFactory;
    
        public LoadBalancerFeignClient(Client delegate,
                                       CachingSpringLoadBalancerFactory lbClientFactory,
                                       SpringClientFactory clientFactory) {
            this.delegate = delegate;
            this.lbClientFactory = lbClientFactory;
            this.clientFactory = clientFactory;
        }
    @ConditionalOnProperty(
        value = {"feign.httpclient.enabled"},
        matchIfMissing = true 说明其实我们可以不配 feign.httpclient.enabled,只要在classpath下有ApacheHttpClient该配置类就会生效了

    二 Feign是怎么做负载均衡的

      LoadBalancerFeignClient

    public Response execute(Request request, Request.Options options) throws IOException {
            try {
                URI asUri = URI.create(request.url());
                String clientName = asUri.getHost();
                URI uriWithoutHost = cleanUrl(request.url(), clientName);
                FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                        this.delegate, request, uriWithoutHost);
    
                IClientConfig requestConfig = getClientConfig(options, clientName);
                return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
                        requestConfig).toResponse();
            }

      我这里截了个图

      

      clientName就是服务名

      经过转换原始的请求转成了FeignLoadBalancer.RibbonRequest

    private FeignLoadBalancer lbClient(String clientName) {
            return this.lbClientFactory.create(clientName);
        }

      clientName就是实际服务的名字

      

    public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
            LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
    
            try {
                return command.submit(
                    new ServerOperation<T>() {
                        @Override
                        public Observable<T> call(Server server) {
                            URI finalUri = reconstructURIWithServer(server, request.getUri());
                            S requestForServer = (S) request.replaceUri(finalUri);
                            try {
                                return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                            } 
                            catch (Exception e) {
                                return Observable.error(e);
                            }
                        }
                    })
                    .toBlocking()
                    .single();
            } catch (Exception e) {
                Throwable t = e.getCause();
                if (t instanceof ClientException) {
                    throw (ClientException) t;
                } else {
                    throw new ClientException(e);
                }
            }
            
        }
    LoadBalancerCommand.submit
    public Observable<T> submit(final ServerOperation<T> operation) {
            final ExecutionInfoContext context = new ExecutionInfoContext();
            
            if (listenerInvoker != null) {
                try {
                    listenerInvoker.onExecutionStart();
                } catch (AbortExecutionException e) {
                    return Observable.error(e);
                }
            }
    
            final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
            final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();
    
            // Use the load balancer
            Observable<T> o = 
                    (server == null ? selectServer() : Observable.just(server))
                    .concatMap(new Func1<Server, Observable<T>>() {
                        @Override
                        // Called for each server being selected
                        public Observable<T> call(Server server) {
                            context.setServer(server);
                            final ServerStats stats = loadBalancerContext.getServerStats(server);
                            
                            // Called for each attempt and retry
    private Observable<Server> selectServer() {
            return Observable.create(new OnSubscribe<Server>() {
                @Override
                public void call(Subscriber<? super Server> next) {
                    try {
                        Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
                        next.onNext(server);
                        next.onCompleted();
                    } catch (Exception e) {
                        next.onError(e);
                    }
                }
            });
        }
    LoadBalancerContext.getServerFromLoadBalancer
    public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
            String host = null;
            int port = -1;
            if (original != null) {
                host = original.getHost();
            }
            if (original != null) {
                Pair<String, Integer> schemeAndPort = deriveSchemeAndPortFromPartialUri(original);        
                port = schemeAndPort.second();
            }
    
            // Various Supported Cases
            // The loadbalancer to use and the instances it has is based on how it was registered
            // In each of these cases, the client might come in using Full Url or Partial URL
            ILoadBalancer lb = getLoadBalancer();
            if (host == null) {
                // Partial URI or no URI Case
                // well we have to just get the right instances from lb - or we fall back
                if (lb != null){
                    Server svc = lb.chooseServer(loadBalancerKey);

      ILoadBalancer 在介绍Ribbon那节就介绍过了,它是负责根据负载策略选择要发送的server的

  • 相关阅读:
    软件需求阅读笔记二
    寒假小程序开发记录2
    软件需求阅读笔记一
    寒假小程序开发记录1
    软件工程概论课个人总结
    06大道至简阅读笔记
    golang算法——leetcode-46
    实验 5 Spark SQL 编程初级实践
    scala链接数据库Exception in thread "main" java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
    Error:(15, 103) value toDF is not a member of org.apache.spark.rdd.RDD[Employee] .map(attributes => Employee(attributes(0).trim.toInt, attributes(1), attributes(2).trim.toInt)).toDF()
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14426694.html
Copyright © 2020-2023  润新知