zoukankan      html  css  js  c++  java
  • 一、ribbon如何集成在openfeign中使用

    所有文章

    https://www.cnblogs.com/lay2017/p/11908715.html

    正文

    ribbon是springcloud封装的一个基于http客户端负载均衡的组件。springcloud的openfeign集成使用了ribbon。所以如果你使用openfeign,那么也会很轻易得使用到ribbon。本文将从openfeign切入,看看它是怎么来使用到ribbon这个客户端负载均衡组件的。

    LoadBalancerFeignClient提供openfeign的负载均衡实现

    在讲openfeign的时候我们说到,最后代理类其实就是发起http请求,并解码返回的字节码内容。回顾一下代码

    Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        Request request = targetRequest(template);
    
        Response response;
    
        try {
          response = client.execute(request, options);
        } catch (IOException e) {
        }
    
    
        boolean shouldClose = true;
        try {
          if (Response.class == metadata.returnType()) {
            // 
          }
          if (response.status() >= 200 && response.status() < 300) {
            // 返回空
            if (void.class == metadata.returnType()) {
              return null;
            } else {
              // 返回解码结果对象
              Object result = decode(response);
    
              return result;
            }
          } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
            //
          } else {
            //
          }
        } catch (IOException e) {
          // 
        } finally {
          // 
        }
      }

    可以看到,client提交了一个http的request,然后获得了response响应对象,处理后并返回。ribbon的接入将从这里开始,我们看看client接口

    public interface Client {
      
      Response execute(Request request, Options options) throws IOException;  
    }

    接口很简单,就是一个请求响应模型。那么,我们再向下看看Client关于负载均衡这个方面有什么实现呢?

    LoadBalancerFeignClient作为Client在负载均衡方面的实现类,我们跟进它的execute方法,看看做了些啥

    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);
          // 构造ribbon的request对象
          FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(this.delegate, request, uriWithoutHost);
    
          IClientConfig requestConfig = getClientConfig(options, clientName);
          // 执行ribbon的request对象
          return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
        } catch (ClientException e) {
          //
        }
      }

    可以看到,关于负载均衡方面openfeign直接构造了ribbon的请求,并执行。

    构造ribbon的IClient

    lbClient(clientName)构造了一个ribbon的http客户端实现,打开该方法

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

    一个简单工厂模式,跟进create方法

    public FeignLoadBalancer create(String clientName) {
        FeignLoadBalancer client = this.cache.get(clientName);
        if (client != null) {
          return client;
        }
        IClientConfig config = this.factory.getClientConfig(clientName);
        ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
        ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
        // 默认返回FeignLoadBalancer
        client = this.loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector, this.loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
        this.cache.put(clientName, client);
        return client;
      }

    这里瞄一眼FeignLoadBalancer的类图吧

    FeignLoadBalancer实现了IClient接口,所以它会负责提交并执行Ribbon的request请求

    提交执行ribbon的request请求

    lbClient方法构建了FeignLoadBalancer,下面该调用它的executeWithLoadBalancer方法了,跟进方法(方法在父类AbstractLoadBalancerAwareClient中)

    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) {
                        // 回调返回选择好的Server对象,并重新构造uri地址
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            // 执行ribbon的request请求
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
            // 
        }
        
    }

    如果不关心负载均衡的情况,这里其实就是直接执行ribbon的request请求了。也就是IClient这个接口类定义的内容。不过FeignLoadBalancer需要进行一次负载选择Server,然后才回调这里的call来发起请求。

    跟进submit方法看看,submit方法先是进行了一次选择获得了一个Server对象,然后回调了上面说的ribbon的request执行

    public Observable<T> submit(final ServerOperation<T> operation) {
            // ...
    
            // Use the load balancer
            Observable<T> o = 
                    // 选择Server
                    (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);
                            //
                            
                            // Called for each attempt and retry
                            Observable<T> o = Observable
                                    .just(server)
                                    .concatMap(new Func1<Server, Observable<T>>() {
                                        @Override
                                        public Observable<T> call(final Server server) {
                                            // ...
                                            // 回调ribbon的request请求
                                            return operation.call(server).doOnEach(
                                                // ...
                                            );
                                        }
                                    });
                            
                            if (maxRetrysSame > 0) 
                                o = o.retry(retryPolicy(maxRetrysSame, true));
                            return o;
                        }
                    });
                
            // ...
        }

    ILoadBalancer负载均衡器

    继续跟进selectServer,看看如何选择服务的

    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);
                }
            }
        });
    }

    托付给了getServerFromLoadBalancer来实现,继续跟进

    public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
        // ...
    
        ILoadBalancer lb = getLoadBalancer();
        if (host == null) {
            if (lb != null){
                Server svc = lb.chooseServer(loadBalancerKey);
                // ...
    
                return svc;
            } else {
                // ...
            }
        } else {
            // ...
        }
        
        // ...
    }

    getLoadBalancer方法先是获取了一个ILoadBalancer接口的实现,然后调用了chooseServer来选择一个Server。

    先跟进getLoadBalancer方法,直接返回了上下文中的设置的ILoadBalancer负载均衡器

    private ILoadBalancer lb;
    
    public ILoadBalancer getLoadBalancer() {
        return lb;    
    }

    我们看一下ILoadBalancer的的类图,RibbonClientConfiguration配置ILoadBalancer的时候配置的是ZoneAwareLoadBalancer的Bean

    getLoadBalancer返回的ILoadBalancer负载均衡器也将是ZoneAwareLoadBalancer的实例对象

    IRule负载均衡算法

    有了ILoadBalancer负载均衡器,再看看chooseServer方法。这里忽略一些细节,直接看BaseLoadBalancer的chooseServer这个核心的实现

    public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

    可以看看,直接调用了IRule接口的choose方法。IRule接口则负责相关的负载均衡算法实现,我们看看IRule接口有哪些实现吧

    常见的随机算法、轮询算法...等

    总结

    到这里,本文就结束了。我们再回顾一下文章的接口和流程

    1、先是openfeign开放了一个Client接口用于http请求,并且LoadBalancerFeignClient作为负载均衡的实现类

    2、LoadBalancerFeignClient则直接构造了一个ribbon的IClient接口的实现FeignLoadBalancer

    3、执行ribbon的request之前,先委托ILoadBalancer负载均衡器选择一个Server,然后回调执行request请求

    4、ILoadBalancer会选择IRule实现的负载均衡算法来获取一个Server,并返回。

    总体逻辑比较简单,本文忽略了一些细节内容,比如一些自动配置的东西、如果从Eureka中获取服务列表等,有兴趣可以自己看看。

  • 相关阅读:
    深入探讨this指针
    杂谈:你选择coco 还是unity3d?
    一分钟制作U盘版BT3
    【转载】ShowWindow函数
    hadoop备记
    OA系统权限管理设计(转载)
    二叉树的存储与遍历
    这难道是CSDN的BUG? 大家帮忙看看哪里有问题
    Cookie 操作工具类
    Java实现 蓝桥杯VIP 算法训练 数的统计
  • 原文地址:https://www.cnblogs.com/lay2017/p/11954707.html
Copyright © 2011-2022 走看看