温故知新(四)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就执行完了。