zoukankan      html  css  js  c++  java
  • 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的

  • 相关阅读:
    Powered by .NET Core 进展:验证高并发性能问题嫌疑犯 docker swarm团队
    暴风雨中的 online : .NET Core 版博客站点遭遇的高并发问题进展团队
    【网站公告】.NET Core 版博客站点第二次发布尝试团队
    【故障公告】发布 .NET Core 版博客站点引起大量 500 错误团队
    上周热点回顾(7.29-8.4)团队
    上周热点回顾(7.22-7.28)团队
    上周热点回顾(7.15-7.21)团队
    上周热点回顾(7.8-7.14)团队
    VMware虚拟机克隆Linux(CentOS)系统后找不到eth0网卡的问题(图文详解)
    Word在转PDF的过程中如何创建标签快速方便阅读(图文详解)
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14426694.html
Copyright © 2011-2022 走看看