温故知新(四)Ribbon执行流程解析

一、准备

  • ribbon版本2.2.5
  • 运行启动lesson1的Provider工程
    在这里插入图片描述
    端口号为6667的返回“i am provider1”,端口号为6668的返回“i am provider2”。
  • 新建lesson3,只引入ribbon。
     <dependencies>
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon</artifactId>
            <version>2.2.5</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-core</artifactId>
            <version>2.2.5</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-httpclient</artifactId>
            <version>2.2.5</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-loadbalancer</artifactId>
            <version>2.2.5</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.archaius</groupId>
            <artifactId>archaius-core</artifactId>
            <version>0.7.4</version>
        </dependency>
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.8</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>16.0.1</version>
        </dependency>
    </dependencies>

在这里插入图片描述
application.properties中列出我们的服务:
sample-client.ribbon.listOfServers=localhost:6667,localhost:6668
Client代码如下:

public class Client {
    public static void main(String[] args) throws Exception{
        ConfigurationManager.loadPropertiesFromResources("application.properties");//1
        System.out.println(ConfigurationManager.getConfigInstance().getProperty("sample-client.ribbon.listOfServers"));
        RestClient client = (RestClient) ClientFactory.getNamedClient("sample-client");//2
        HttpRequest request = HttpRequest.newBuilder().uri("/provider").build();//3
        for (int i = 0; i < 10; i++)  {
            HttpResponse response = client.executeWithLoadBalancer(request); // 4
            System.out.println("Status code for " + response.getRequestedURI() + "  :" + response.getStatus()+" entity: "+response.getEntity(String.class));
        }
    }
}

执行结果如下:
在这里插入图片描述
实现了负载均衡。

二、执行流程解析

public class Client {
    public static void main(String[] args) throws Exception{
        ConfigurationManager.loadPropertiesFromResources("application.properties");//1
        System.out.println(ConfigurationManager.getConfigInstance().getProperty("sample-client.ribbon.listOfServers"));
        RestClient client = (RestClient) ClientFactory.getNamedClient("sample-client");//2
        HttpRequest request = HttpRequest.newBuilder().uri("/provider").build();//3
        for (int i = 0; i < 10; i++)  {
            HttpResponse response = client.executeWithLoadBalancer(request); // 4
            System.out.println("Status code for " + response.getRequestedURI() + "  :" + response.getStatus()+" entity: "+response.getEntity(String.class));
        }
    }
}
  • 编号为1的加载配置信息。
  • 编号为2的获取RestClient
  • 编号为3的创建一个请求
  • 编号为4的执行请求

2.1loadPropertiesFromResources

ConfigurationManager主要用来做配置管理,loadPropertiesFromResources加载配置文件,并把配置信息写入ConcurrentMapConfiguration,为后续的请求做准备。

 public static void loadPropertiesFromResources(String path) 
            throws IOException {
       .......................
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            InputStream fin = url.openStream();
            Properties props = ConfigurationUtils.loadPropertiesFromInputStream(fin);
            if (instance instanceof AggregatedConfiguration) {
                String name = getConfigName(url);
                ConcurrentMapConfiguration config = new ConcurrentMapConfiguration();
                config.loadProperties(props);
                ((AggregatedConfiguration) instance).addConfiguration(config, name);
            } else {
                ConfigurationUtils.loadProperties(props, instance);
            }
        }
    }

因为properties中设置sample-client.ribbon.listOfServers=localhost:6667,localhost:6668,那么会把key=sample-client.ribbon.listOfServers,value=localhost:6667,localhost:6668的put到ConcurrentMapConfiguration的map,其他变量类似。

2.2RestClient

RestClient client = (RestClient) ClientFactory.getNamedClient(“sample-client”);通过client name来获取RestClient,

    public static synchronized IClient getNamedClient(String name) {
        return getNamedClient(name, DefaultClientConfigImpl.class);
    }
    
    public static synchronized IClient getNamedClient(String name, Class<? extends IClientConfig> configClass) {
    	if (simpleClientMap.get(name) != null) {
    	    return simpleClientMap.get(name);
    	}
        try {
            return createNamedClient(name, configClass);
        } catch (ClientException e) {
            throw new RuntimeException("Unable to create client", e);
        }
    }
    public static synchronized IClient createNamedClient(String name, Class<? extends IClientConfig> configClass) throws ClientException {
    	IClientConfig config = getNamedConfig(name, configClass);
        return registerClientFromProperties(name, config);
    }

其中DefaultClientConfigImpl是默认的client的配置实现类,如默认MaxTotalConnections,ConnectTimeout,ConnectionManagerTimeout,ReadTimeout等。
最终调用ClientFactory的getNamedConfig获取ClientConfig,调用registerClientFromProperties通过反射获取client和loadbalance

2.2.1创建IClientConfig
    public static IClientConfig getNamedConfig(String name, Class<? extends IClientConfig> clientConfigClass) {
        IClientConfig config = namedConfig.get(name);
        if (config != null) {
            return config;
        } else {
            try {
                config = (IClientConfig) clientConfigClass.newInstance();
                config.loadProperties(name);
            } catch (InstantiationException | IllegalAccessException e) {
                logger.error("Unable to create named client config '{}' instance for config class {}", name,
                        clientConfigClass, e);
                return null;
            }
            config.loadProperties(name);
            IClientConfig old = namedConfig.putIfAbsent(name, config);
            if (old != null) {
                config = old;
            }
            return config;
        }
    }

config = (IClientConfig) clientConfigClass.newInstance();通过反射获取DefaultClientConfigImpl实例,
通过config.loadProperties(name);加载name(当前为sample-client)为前缀的配置,然后替换默认配置。

    @Override
	public void loadProperties(String restClientName){
        enableDynamicProperties = true;
        setClientName(restClientName);
        loadDefaultValues();
        Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName);
        for (Iterator<String> keys = props.getKeys(); keys.hasNext(); ){
            String key = keys.next();
            String prop = key;
            try {
                if (prop.startsWith(getNameSpace())){
                    prop = prop.substring(getNameSpace().length() + 1);
                }
                setPropertyInternal(prop, getStringValue(props, key));
            } catch (Exception ex) {
                throw new RuntimeException(String.format("Property %s is invalid", prop));
            }
        }
    }

Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName);获取配置的子集,然后通过setPropertyInternal设置相应属性,
在这里插入图片描述
因为设置properties中设置sample-client.ribbon.listOfServers=localhost:6667,localhost:6668,通过上面的转换后,setPropertyInternal中prop = listOfServers,getStringValue为localhost:6667,localhost:6668,即sample-client的服务列表设置为localhost:6667,localhost:6668。到这里已经创建好了IClientConfig。

2.2.2创建IClient

通过registerClientFromProperties创建IClient.

 public static synchronized IClient<?, ?> registerClientFromProperties(String restClientName, IClientConfig clientConfig) throws ClientException { 
    	IClient<?, ?> client = null;
    	ILoadBalancer loadBalancer = null;
    	if (simpleClientMap.get(restClientName) != null) {
    		throw new ClientException(
    				ClientException.ErrorType.GENERAL,
    				"A Rest Client with this name is already registered. Please use a different name");
    	}
    	try {
    	    //1.获取RestCliet
    		String clientClassName = (String) clientConfig.getProperty(CommonClientConfigKey.ClientClassName);
    		//2.通过反射获取RestCliet实例,通过initWithNiwsConfig设置相关配置项
    		client = (IClient<?, ?>) instantiateInstanceWithClientConfig(clientClassName, clientConfig);
    		//3.通过反射创建负载均衡器
    		boolean initializeNFLoadBalancer = Boolean.parseBoolean(clientConfig.getProperty(
    				CommonClientConfigKey.InitializeNFLoadBalancer, DefaultClientConfigImpl.DEFAULT_ENABLE_LOADBALANCER).toString());
    		if (initializeNFLoadBalancer) {
    			loadBalancer  = registerNamedLoadBalancerFromclientConfig(restClientName, clientConfig);
    		}
    		if (client instanceof AbstractLoadBalancerAwareClient) {
    			((AbstractLoadBalancerAwareClient) client).setLoadBalancer(loadBalancer);
    		}
    	} catch (Throwable e) {
    		String message = "Unable to InitializeAndAssociateNFLoadBalancer set for RestClient:"
    				+ restClientName;
    		logger.warn(message, e);
    		throw new ClientException(ClientException.ErrorType.CONFIGURATION, 
    				message, e);
    	}
    	simpleClientMap.put(restClientName, client);
    	Monitors.registerObject("Client_" + restClientName, client);
    	logger.info("Client Registered:" + client.toString());
    	return client;
    }

通过client = (IClient<?, ?>) instantiateInstanceWithClientConfig(clientClassName, clientConfig);
创建restclient,

 @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        super.initWithNiwsConfig(clientConfig);
        this.ncc = clientConfig;
        this.restClientName = ncc.getClientName();
        this.isSecure = getBooleanFromConfig(ncc, CommonClientConfigKey.IsSecure, this.isSecure);
        this.isHostnameValidationRequired = getBooleanFromConfig(ncc, CommonClientConfigKey.IsHostnameValidationRequired, this.isHostnameValidationRequired);
        this.isClientAuthRequired = getBooleanFromConfig(ncc, CommonClientConfigKey.IsClientAuthRequired, this.isClientAuthRequired);
        this.bFollowRedirects = getBooleanFromConfig(ncc, CommonClientConfigKey.FollowRedirects, true);
        this.ignoreUserToken = getBooleanFromConfig(ncc, CommonClientConfigKey.IgnoreUserTokenInConnectionPoolForSecureClient, this.ignoreUserToken);

        this.config = new DefaultApacheHttpClient4Config();
        this.config.getProperties().put(
                ApacheHttpClient4Config.PROPERTY_CONNECT_TIMEOUT,
                Integer.parseInt(String.valueOf(ncc.getProperty(CommonClientConfigKey.ConnectTimeout))));
        this.config.getProperties().put(
                ApacheHttpClient4Config.PROPERTY_READ_TIMEOUT,
                Integer.parseInt(String.valueOf(ncc.getProperty(CommonClientConfigKey.ReadTimeout))));

        this.restClient = apacheHttpClientSpecificInitialization();
        this.setRetryHandler(new HttpClientLoadBalancerErrorHandler(ncc));
    }

最终的Client是apacheHttpClient。
创建负载均衡
loadBalancer = registerNamedLoadBalancerFromclientConfig(restClientName, clientConfig);
BaseLoadBalancer中

void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping) {
        //略。。。。
        // cross associate with each other
        // i.e. Rule,Ping meet your container LB
        // LB, these are your Ping and Rule guys ...
        setRule(rule);
        setPing(ping);
        setLoadBalancerStats(new LoadBalancerStats(clientName));
        rule.setLoadBalancer(this);
      //略。。。。

    }

这里引入是Ribbon的核心的几个接口,IRule,IPing等,这几个接口在后续结合Spring Cloud的来一起分析。
到这里整个Resclient就创建完成了。

执行

通过调用client.executeWithLoadBalancer(request);执行请求,Http请求过程中,会使用到上面介绍的LoadBalance,RetryHandler等。到这里Ribbon就执行完了。

源码地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值