ribbon源码之客户端
客户端模块的核心功能是提供统一的用户请求操作接口。
接口定义
客户端模块的核心是IClient接口,定义了客户端网络请求的方法。
public interface IClient<S extends ClientRequest, T extends IResponse> {
    public T execute(S request, IClientConfig requestConfig) throws Exception;
}
ClientRequest为客户端定义的请求体,存储了请求uri、loadbalancer的key,是否重试、配置。
public class ClientRequest implements Cloneable {
    protected URI uri;
    protected Object loadBalancerKey = null;
    protected Boolean isRetriable = null;
    protected IClientConfig overrideConfig;
}
IResponse为客户端定义的响应内容的接口。
public interface IResponse extends Closeable
{
public Object getPayload() throws ClientException;
public boolean hasPayload();
public boolean isSuccess();
public URI getRequestedURI();
public Map<String, ?> getHeaders();
}
IClientConfigAware定义了需要使用IClientConfig初始化IClient的方法。
public interface IClientConfigAware {
    public abstract void initWithNiwsConfig(IClientConfig clientConfig);
}
ribbon基于http请求,相关类和接口,HttpRequest为http请求;HttpResponse为http请求返回结果;Restclient是基于jesery的IClient实现。
类图

客户端工厂类
客户端模块提供了一个客户端工厂类(ClientFactory)用于通过配置文件来创建IClient实例和负载均衡器(ILoadBalancer)实例。
public static synchronized IClient getNamedClient(String name) {//根据配置获取iclient实例,默认使用DefaultClientConfigImpl配置类。
        return getNamedClient(name, DefaultClientConfigImpl.class);
    }
public static synchronized IClient getNamedClient(String name, Class<? extends IClientConfig> configClass) {
   ...
        return createNamedClient(name, configClass);
   ...
}
public static synchronized IClient createNamedClient(String name, Class<? extends IClientConfig> configClass) throws ClientException {
   IClientConfig config = getNamedConfig(name, configClass);//实例化配置类
    return registerClientFromProperties(name, config);//通过配置文件创建iclient
}
public static synchronized ILoadBalancer getNamedLoadBalancer(String name) {
   return getNamedLoadBalancer(name, DefaultClientConfigImpl.class);
}
public static synchronized ILoadBalancer getNamedLoadBalancer(String name, Class<? extends IClientConfig> configClass) {
   ...
            lb = registerNamedLoadBalancerFromProperties(name, configClass);
   ...
}
public static synchronized IClient<?, ?> registerClientFromProperties(String restClientName, IClientConfig clientConfig) throws ClientException {
        IClient<?, ?> client = null;
        ILoadBalancer loadBalancer = null;
        ...
            String clientClassName = (String) clientConfig.getProperty(CommonClientConfigKey.ClientClassName);//通过配置文件获取client的实现类
            client = (IClient<?, ?>) instantiateInstanceWithClientConfig(clientClassName, clientConfig); //通过配置文件创建client实例
            boolean initializeNFLoadBalancer = Boolean.parseBoolean(clientConfig.getProperty(
                    CommonClientConfigKey.InitializeNFLoadBalancer, DefaultClientConfigImpl.DEFAULT_ENABLE_LOADBALANCER).toString());
            if (initializeNFLoadBalancer) {//如果需要初始化负载均衡器,则通过配置文件创建一个负载均衡器
                loadBalancer  = registerNamedLoadBalancerFromclientConfig(restClientName, clientConfig);
            }
            if (client instanceof AbstractLoadBalancerAwareClient) {//如果client实现AbstractLoadBalancerAwareClient,则注入负载均衡器
                ((AbstractLoadBalancerAwareClient) client).setLoadBalancer(loadBalancer);
            }
        ...return client;
    }
public static ILoadBalancer registerNamedLoadBalancerFromclientConfig(String name, IClientConfig clientConfig) throws ClientException {
   ...
   ILoadBalancer lb = null;
   ...
        String loadBalancerClassName = (String) clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerClassName);//
        lb = (ILoadBalancer) ClientFactory.instantiateInstanceWithClientConfig(loadBalancerClassName, clientConfig);                                    
        ...
        return lb;
   ...      
}
//初始化指定的class类
public static Object instantiateInstanceWithClientConfig(String className, IClientConfig clientConfig) 
          throws InstantiationException, IllegalAccessException, ClassNotFoundException {
       Class clazz = Class.forName(className);
       if (IClientConfigAware.class.isAssignableFrom(clazz)) {//如果指定的iclient实现了IClientConfigAware,ClientFactory在创建时会使用IClientConfig进行初始化。
          IClientConfigAware obj = (IClientConfigAware) clazz.newInstance();
          obj.initWithNiwsConfig(clientConfig);
          return obj;
       } else {
          try {
              if (clazz.getConstructor(IClientConfig.class) != null) {
                 return clazz.getConstructor(IClientConfig.class).newInstance(clientConfig);
              }
          } catch (Throwable e) { // NOPMD             
          }         
       }
       return clazz.newInstance();
   }
使用客户端工厂类(ClientFactory)涉及的配置:
| 属性 | 实现 | 默认值 | 
| clientname.ribbon.ClientClassName | client使用的IClient实现类 | com.netflix.niws.client.http.RestClient | 
| clientname.ribbon.InitializeNFLoadBalancer | 是否初始化负载均衡器 | true | 
| clientname.ribbon.NFLoadBalancerClassName | 负载均衡器的实现类 | com.netflix.loadbalancer.ZoneAwareLoadBalancer | 
类图

客户端实现类
AbstractLoadBalancerAwareClient实现了通过负载均衡器进行请求调用。LoadBalancerCommand对负载均衡器操作进行了模版,对请求调用提供了回调函数。
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
     ...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);//设置最终的调用uri。
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } //调用execute方法执行请求调用。
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        ...
    }
LoadBalancerCommand调用了负载均衡器获得了一个server,然后调用回调函数执行请求。此外还提供了各个关键节点的监听器和异常重试机制。
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();
        Observable<T> o =
                (server == null ? selectServer() : Observable.just(server))//调用负载均衡器获得目标server
                .concatMap(new Func1<Server, Observable<T>>() {
                    ...return operation.call(server).doOnEach(new Observer<T>() {
                                            ....
                                        });
                    ...
                        if (maxRetrysSame > 0)
                            o = o.retry(retryPolicy(maxRetrysSame, true));
                        return o;
                    }
                });
        if (maxRetrysNext > 0 && server == null)
            o = o.retry(retryPolicy(maxRetrysNext, false));
        return o.onErrorResumeNext(new Func1<Throwable, Observable<T>>() {
            @Override
            public Observable<T> call(Throwable e) {
                if (context.getAttemptCount() > 0) {
                    if (maxRetrysNext > 0 && context.getServerAttemptCount() == (maxRetrysNext + 1)) {
                        e = new ClientException(ClientException.ErrorType.NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED,
                                "Number of retries on next server exceeded max " + maxRetrysNext
                                + " retries, while making a call for: " + context.getServer(), e);
                    }
                    else if (maxRetrysSame > 0 && context.getAttemptCount() == (maxRetrysSame + 1)) {
                        e = new ClientException(ClientException.ErrorType.NUMBEROF_RETRIES_EXEEDED,
                                "Number of retries exceeded max " + maxRetrysSame
                                + " retries, while making a call for: " + context.getServer(), e);
                    }
                }
                if (listenerInvoker != null) {
                    listenerInvoker.onExecutionFailed(e, context.toFinalExecutionInfo());
                }
                return Observable.error(e);
            }
        });
    }
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);
                }
            }
        });
    }
子类 HttpRequest用于http请求,内部定义了http请求的各个内容,并且使用了builder模式。
public class HttpRequest extends ClientRequest {protected CaseInsensitiveMultiMap httpHeaders = new CaseInsensitiveMultiMap();//head参数
    protected Multimap<String, String> queryParams = ArrayListMultimap.create();//query参数
    private Object entity;//消息体
    protected Verb verb;//http请求的method:post get head delete等。默认get
}
类图
ribbon源码之客户端的更多相关文章
- ribbon源码(1) 概述
		ribbon的核心功能是提供客户端在进行网络请求时负载均衡的能力.主要有以下几个模块: 负载均衡器模块 负载均衡器模块提供了负载均衡能力,详细参见ribbon源码之负载均衡器. 配置模块 配置模块管理 ... 
- 【一起学源码-微服务】Ribbon源码五:Ribbon源码解读汇总篇~
		前言 想说的话 [一起学源码-微服务-Ribbon]专栏到这里就已经全部结束了,共更新四篇文章. Ribbon比较小巧,这里是直接 读的spring cloud 内嵌封装的版本,里面的各种config ... 
- ribbon源码(2) 负载均衡器
		负载均衡器对外提供负载均衡的功能,本质上是是维护当前服务的服务器列表和服务器状态,通过负载均衡算法选取合适的服务器地址. 用户可以通过实现ILoadBalancer来实现自己的负载均衡器,ribbon ... 
- centos精简系统 源码安装客户端git
		CentOS的yum源中git版本比较低,需要最新版本git,只能自己编译安装,现在记录下编译安装的内容,留给自己备忘. 对于精简型的centos系统,会缺少很多依赖包和插件,要源码安装客户端git, ... 
- Netty源码解析—客户端启动
		Netty源码解析-客户端启动 Bootstrap示例 public final class EchoClient { static final boolean SSL = System.getPro ... 
- Netty 源码学习——客户端流程分析
		Netty 源码学习--客户端流程分析 友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白. 使用版本依赖 <dependency> <groupId&g ... 
- Ribbon源码分析(二)-- 服务列表的获取和负载均衡算法分析
		上一篇博客(https://www.cnblogs.com/yangxiaohui227/p/12614343.html)分享了ribbon如何实现对http://product/info/这个链接重 ... 
- 读书笔记-Ribbon源码分析
		@LoadBalanced注解用来给RestTemplate做标记,以使用负载均衡的客户端来配置. 通过搜索LoadBalancerClient可以发现,LoadBalancerClient是Spri ... 
- 【一起学源码-微服务】Ribbon 源码一:Ribbon概念理解及Demo调试
		前言 前情回顾 前面文章已经梳理清楚了Eureka相关的概念及源码,接下来开始研究下Ribbon的实现原理. 我们都知道Ribbon在spring cloud中担当负载均衡的角色, 当两个Eureka ... 
随机推荐
- SpringCloude简记_part3
			18. SpringCloud Alibaba Sentinel实现熔断与限流 18.1 Sentiel 官网 https://github.com/alibaba/Sentinel 中文 https ... 
- javaWeb项目之图书管理系统(附视频讲解)
			视频播放地址:javaWeb图书系统 本系统为"Swing项目之图书管理系统"(此源码已共享)的Web版,网页框架用采用EasyUI 数据库为MysqL,写Web项目摒弃了火狐浏览 ... 
- hbase运行流程图
			hbase运行流程图 
- Mysql锁【转】
			一.概述 数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则.对于任何一种数据库来说都需要有相应的锁定机制,所以MySQL自然也不能例外. M ... 
- vue自定义下拉框组件
			创建下拉框组件 Select.vue <template> <div class="selects"> <div :class="{sele ... 
- js对象数组新增、修改时的验证是否重复的逻辑
			JS代码: // 定义数据集合 const persons = [ { id: 1, name: '张三' }, { id: 2, name: '李四' } ] console.log('') con ... 
- Java面试题(Hibernate篇)
			Hibernate 113.为什么要使用 hibernate? 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码. Hibernate是一个基于JDBC的主流持久化框架,是一个 ... 
- 水滴app
			在选择了软件工程专业之后,指导教师也让我们参加到了学长学姐的作业之中来,使用学长学姐们的软件并写出自己的使用评价以及自己的一些小评价. 这体验的是第三十二组学长们的软件,他们的队名是自然选择,他们做的 ... 
- 放眼SEM现状及发展历程
			http://www.wocaoseo.com/thread-187-1-1.html 由于近年来移动应用的基本普及,搜索引擎营销随之进入高速发展时代,应用层次的提升已经成为企业营销策略的一个重要组成 ... 
- vue-x和axios的学习
			axios的使用 使用原因:因为vue本身就带有处理dom的功能,不希望再有其他操作dom的插件,所以用axios代替了jquery 功能:发送xhr请求 下载: $ npm install axio ... 
