dubbo:reference解析成一个ReferenceBean,它是一个FactoryBean,消费者的初始化在它的init方法中执行,这个方法在两种情况下会被调用:
(1)消费者设置了立即初始(init属性设置成true),那么bean加载时会立即调用消费者初始化。
(2)消费者bean被使用者调用时,调用getObject->get->init。
消费者初始化是主要做的事情就是引用对应的远程服务,执行以步骤:
(1)监听注册中心
(2)连接服务提供端
(3)创建消费端服务代理
监听注册中心
消费者在初始化时也会生成一个registryUrl,消费端的信息:side=consumer、dubbo版本、时间戳、应用名等拼成query串放在registryUrl的refer参数中,消费者初始化时引用远程服务也由Protocol组件来处理,加载自适应的Protocol实现。
com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map<String, String>)
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (int i = 0 ; i < urls.size(); i++) {
URL curUrl = urls.get(i);
try {
invokers.add(refprotocol.refer(interfaceClass, curUrl));
} catch (Exception e) {
if(i == urls.size() - 1 && registryURL == null){
throw new IllegalStateException("Unable to connect to zookeeper server, please check socket connection or zookeeper machine.");
}
// 尝试连接下一个地址
continue;
}
if (Constants.REGISTRY_PROTOCOL.equals(curUrl.getProtocol())) {
registryURL = curUrl; // 优先使用第一个可用的registry url
break;
}
}
if (registryURL != null) { // 有注册中心协议的URL
// 对有注册中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注册中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
}
传入的url参数是注册中心URL,然后依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol实现的refer方法。
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if(logger.isDebugEnabled()){
logger.debug("start RegistryProtocol refer ");
}
//生成消费者URL
//从url中registry中获得注册时使用的协议,默认是dubbo协议,设置url的协议,并移去这个registry参数
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);//根据协议获得注册中心
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0 ) {
if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1
|| "*".equals( group ) ) {
return doRefer( getMergeableCluster(), registry, type, url );
}
}
return doRefer(cluster, registry, type, url);
registry协议的url两个wrapper不会进行任何直接进入RegistryProtocol.refer,在这个方法中先把消费者注册到注册中心,zookeeper注册中心由ZookeeperRegistry来处理,在zookeeper服务器上生成一个节点,节点路径是/dubbo/interfaceName/consumers/{consumerUrl},存储了服务消费方ip、group、接口名称、版本、应用名称等。
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
//生成消费者URL
// String ipAddr = NetUtils.getLocalHost();
// if(!ConfUtil.getParamInEnv("HOST").isEmpty()){
// ipAddr = ConfUtil.getParamInEnv("HOST");
// }
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
// URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, ipAddr, 0, type.getName(), directory.getUrl().getParameters());
if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
/** 订阅此url*/
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));//category (providers,configurators,routers)
return cluster.join(directory);
}
消费端本地会缓存远程服务提供者(每个提供者对应一个Invoker对象)、注册中心配置、路由配置信息。监听注册中心路径是/dubbo/interfaceClass/providers和/dubbo/interfaceClass/configurators、/dubbo/interfaceClass/routers的节点,当提供者、配置、路由信息发生变化之后注册中心会通知消费者刷新本地缓存 。Dubbo框架通过在消费端缓存提供者的信息消除对注册中心的强依赖,即使注册中心挂了服务依然可用。
框架为每个接口消费者创建一个RegistryDirectory对象,缓存接口的有的提供端Invoker以及注册中心接口相关的接口配置configurators,服务提供者Invoker保存在RegistryDirectory的methodInvokerMap中,key是方法名称或者,因为大系统中同一个服务一般会由多台服务器提供,所以value是一个Invoker列表存储该服务接口的所有提供者。服务调用时会根据方法名称从这个map中查找提供者对应的Invoker,当providers节点发生变化时通知消费端更新缓存 providers对应的Invoker列表。
private void refreshInvoker(List<URL> invokerUrls){
if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // 禁止访问
this.methodInvokerMap = null; // 置空列表
destroyAllInvokers(); // 关闭所有Invoker
} else {
this.forbidden = false; // 允许访问
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<URL>();
this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
}
if (invokerUrls.size() ==0 ){
return;
}
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
// state change
// 如果计算错误,则不进行处理.
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
this.available = true;
return ;
}
this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
this.urlInvokerMap = newUrlInvokerMap;
try{
destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
}catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
接下来要组每一个提供者创建一个对应的Invoker,refreshInvoker方法调用toInvokers,在toInovkers中加载自适应Protocol实现,并且调用它的refer方法,此时url参数是人注册中心获取到的携带提供者信息的providerUrl,根据扩展点加载规则,会依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->DubboProtocol的refer方法,在这两个Wrapper中添加对应的InvokerListener并且构建Invoker-Filter链,最后在DubboProtocol.refer中创建一个DubboInvoker对象,该Invoker对象持有服务class、providerUrl、负责和提供端通信的ExchangeClient、Invoker对象保存在DubboProtocol的invokers集合中。
连接服务提供端
构建DubboInvoker时,会构建一个或多个ExchangeClient用来处理和提供端的连接,默认情况下一下url只会创建一个ExchangeClient负责和对应的提供端建立连接,如果应用方配了多个connections会创建多个,如果lazy属性没有设置成true(默认false),此时ExchangeClient会马上和服务端建立连接,此进组件调用顺序:Exchanger.connect->Transporter.connect->Client,最终会调用Netty框架和提供端建立连接,创建NettyClient对象。
和NettyServer一样,NettyClient在也会注册IO事件链NettyCodecAdapter.decoder->NettyCodecAdapter.encoder->NettyHandler到Netty框架,分别负责请求响应解码、响应处理。
创建消费端服务代理
回到RegistryProtocol.doRefer方法的最后,由Cluster组件来创建一个Invoker并返回,此处的Cluster也是自适应的实现,示例配置的容错模式是failfast,返回扩展链MockClusterWrapper->FailfastCluster,经过扩展链处理创建MockClusterInvoker->FailfastClusterInvoker对象链,FailfastClusterInvoker的directory属性引用上面创建 的RegistryDirectory对象。
返回生成的Invoker实例之后,由ProxyFactory生成一个持有该Invoker实例的代理,代理回调时会激活该Invoker的invoke方法。
该代理会被注册到Spring IO容器中,之后业务中从容器中获取 消费者bean时容器会返回这个代理。
转自:http://blog.youkuaiyun.com/tolihaifeng/article/details/60970753
最后欢迎大家访问我的个人网站:1024s