在分析标签解析的时候知道框架会把dubbo:reference解析成一个ReferenceBean,它是一个FactoryBean,消费者的初始化在它的init方法中执行,这个方法在两种情况下会被调用:
消费者初始化时主要做的事情就是引用对应的远程服务,执行了以下步骤:
消费者在初始化时也会生成一个registryUrl,消费端的信息:side=consumer、dubbo版本、时间戳、应用名等拼成query串放在registryUrl的refer参数中,消费者初始化时引用远程服务也由Protocol组件来处理,加载自适应的Protocol实现:
com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map)
主要代码如下:
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List> invokers = new ArrayList>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一个registry url
}
}
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这两个Wrapper扩展点,然后依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol实例的refer方法:
@SuppressWarnings("unchecked")
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
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/customs/interfaceName/customs/{customUrl}
,存储了服务消费方ip、group、接口名称、版本、应用名称等,customUrl是consumer://10.240.176.159/com.netease.haitao.payment.remote.api.coupon.SendCouponRemoteApi?application=…
这种形式的,在注册之前生成,把本机ip、接口名称等添加到URL中。
消费端本地会缓存远程服务提供者(每个提供者对应一个Invoker对象)、注册中心配置、路由配置信息。监听注册中心路径是/dubbo/interfaceClass/providers和/dubbo/interfaceClass/providers和/dubbo/{interfaceClass}/configurators, /dubbo/${interfaceClass}/routers
的节点,当提供者、配置、路由信息发生变化之后注册中心会通知消费者刷新本地缓存。Dubbo框架通过在消费端缓存提供者的信息消除对注册中心的强依赖,即使注册中心挂了服务依然可用。
com.alibaba.dubbo.registry.integration.RegistryProtocol.doRefer(Cluster, Registry, Class, URL)
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);
// REFER_KEY的所有属性
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
//生成消费者url
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
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)));
}
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
return cluster.join(directory);
}
框架为每个接口消费者创建一个RegistryDirectory对象,缓存接口所有的提供端Invoker以及注册中心接口相关的接口配置configurators,服务提供者Invoker保存在RegistryDirectory的methodInvokerMap中,key是方法名称,value是一个Invoker列表存储该服务接口的所有提供者,一般情况下,假如服务接口中有a、b两个接口,那么map中的内容是:{a=invokers,b=invokers,c=invokers},其中invokers列表中的内容相同,服务被调用时会根据方法名称从这个map中查找提供者对应Invoker。当providers节点发生变化时通知消费端更新缓存的providers对应的Invoker列表。
com.alibaba.dubbo.registry.integration.RegistryDirectory.refreshInvoker(List)
/**
* 根据invokerURL列表转换为invoker列表。转换规则如下:
* 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用
* 2.如果传入的invoker列表不为空,则表示最新的invoker列表
* 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。
*
* @param invokerUrls 传入的参数不能为null
*/
// TODO: 2017/8/31 FIXME 使用线程池去刷新地址,否则可能会导致任务堆积
private void refreshInvoker(List 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> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null) {
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet();
this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
}
if (invokerUrls.size() == 0) {
return;
}
Map> newUrlInvokerMap = toInvokers(invokerUrls);// 将URL列表转成Invoker列表
Map>> 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()));
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,在toInvokers中加载自适应Protocol实现,并且调用它的refer方法,此时url参数是从注册中心获取到的携带提供者服务信息的providerUrl,根据扩展点加载规则,会依次调用ProtocolListenerWrapper->ProtocolFilterWrapper-> DubboProtocol的refer方法,在这两个Wrapper中添加对应的InvokerListener并且构建Invoker-Filter链,最后在DubboProtocol.refer中创建一个DubboInvoker对象,该Invoker对象持有服务Class、providerUrl、负责和提供端通信的ExchangeClient,Invoker对象保存在DubboProtocol的invokers集合中。
com.alibaba.dubbo.registry.integration.RegistryDirectory.toInvokers(List)
/**
* 将urls转成invokers,如果url已经被refer过,不再重新引用。
*
* @param urls
* @param overrides
* @param query
* @return invokers
*/
private Map> toInvokers(List urls) {
Map> newUrlInvokerMap = new HashMap>();
if (urls == null || urls.size() == 0) {
return newUrlInvokerMap;
}
Set keys = new HashSet();
String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {
//如果reference端配置了protocol,则只选择匹配的protocol
if (queryProtocols != null && queryProtocols.length() > 0) {
boolean accept = false;
String[] acceptProtocols = queryProtocols.split(",");
for (String acceptProtocol : acceptProtocols) {
if (providerUrl.getProtocol().equals(acceptProtocol)) {
accept = true;
break;
}
}
if (!accept) {
continue;
}
}
if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
+ ", supported protocol: " + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
continue;
}
URL url = mergeUrl(providerUrl);
String key = url.toFullString(); // URL参数是排序的
if (keys.contains(key)) { // 重复URL
continue;
}
keys.add(key);
// 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
Map> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // 缓存中没有,重新refer
try {
boolean enabled = true;
if (url.hasParameter(Constants.DISABLED_KEY)) {
enabled = !url.getParameter(Constants.DISABLED_KEY, false);
} else {
enabled = url.getParameter(Constants.ENABLED_KEY, true);
}
if (enabled) {
invoker = new InvokerDelegete(protocol.refer(serviceType, url), url, providerUrl);
}
} catch (Throwable t) {
logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
}
if (invoker != null) { // 将新的引用放入缓存
newUrlInvokerMap.put(key, invoker);
}
} else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
构建DubboInvoker时,会构建一个或多个ExchangeClient用来处理和提供端的连接,默认情况下一个url只会创建一个ExchangeClient负责和对应的提供端建立连接,如果应用方配了多个connections则会创建多个。如果lazy属性没有设置成true(默认false),则此时ExchangeClient会马上和服务端建立连接,此时组件调用顺序:Exchanger.connect->Transporter.connect ->Client,最终会调用Netty框架和提供端建立连接,创建NettyClient对象。
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
public Invoker refer(Class serviceType, URL url) throws RpcException {
// create rpc invoker.
DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
private ExchangeClient[] getClients(URL url) {
//是否共享连接
boolean service_share_connect = false;
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
//如果connections不配置,则共享连接,否则每服务每连接
if (connections == 0) {
service_share_connect = true;
connections = 1;
}
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
if (service_share_connect) {
clients[i] = getSharedClient(url);
} else {
clients[i] = initClient(url);
}
}
return clients;
}
和NettyServer一样,NettyClient在也会注册IO事件处理链NettyCodecAdapter.decoder->NettyCodecAdapter.encoder->NettyHandler到Netty框架,分别负责请求响应编解码、响应处理。
com.alibaba.dubbo.remoting.transport.netty.NettyClient.doOpen()
@Override
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
bootstrap = new ClientBootstrap(channelFactory);
// config
// @see org.jboss.netty.channel.socket.SocketChannelConfig
bootstrap.setOption("keepAlive", true);
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("connectTimeoutMillis", getTimeout());
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
//io事件处理链
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
}
回到RegistryProtocol .doRefer方法的最后,由Cluster组件来创建一个Invoker并返回,此处的Cluster也是自适应的实现,示例配置的容错模式是failfast,返回扩展链MockClusterWrapper->FailfastCluster(默认是failover),经过扩展链处理创建MockClusterInvoker->FailfastClusterInvoker对象链, FailfastClusterInvoker的directory属性引用上面创建的RegistryDirectory对象。
public class MockClusterWrapper implements Cluster {
private Cluster cluster;
public MockClusterWrapper(Cluster cluster) {
this.cluster = cluster;
}
public Invoker join(Directory directory) throws RpcException {
return new MockClusterInvoker(directory,
this.cluster.join(directory));
}
}
/**
* 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。
*
* Fail-fast
*
* @author william.liangf
*/
public class FailfastCluster implements Cluster {
public final static String NAME = "failfast";
public Invoker join(Directory directory) throws RpcException {
return new FailfastClusterInvoker(directory);
}
}
返回生成的Invoker实例之后,由ProxyFactory生成一个持有该Invoker实例的代理,代理回调时会激活该Invoker(MockClusterWrapper类型)调用它的invoke方法:
com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
@SuppressWarnings("unchecked")
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
/**
* InvokerHandler
*
* @author william.liangf
*/
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker> invoker;
public InvokerInvocationHandler(Invoker> handler) {
this.invoker = handler;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
该代理会被注册到spring IOC容器中,如果想要获取消费者bean时,容器会返回这个代理。
监听注册中心 :根据配置文件中的地址将服务消费者注册到注册中心。服务消费者监听注册中心的变化,如果有变化更新服务提供者的缓存信息(服务消费者会缓存服务提供都的信息)。
连接服务提供端:调用Netty框架和提供端建立连接,创建NettyClient对象。NettyClient在也会注册IO事件处理链NettyCodecAdapter.decoder->NettyCodecAdapter.encoder->NettyHandler到Netty框架,分别负责请求响应编解码、响应处理。
创建消费端服务代理:生成代理对象,该代理会被注册到spring IOC容器中,如果想要获取消费者bean时,容器会返回这个代理。