源码分析基于dubbo 2.7.1
ReferenceBean继承了ReferenceConfig, 还实现了FactoryBean。
spring启动时,会通过FactoryBean.getObject创建bean。这里会调用到ReferenceConfig.createProxy
private T createProxy(Map map) {
if (shouldJvmRefer(map)) {
...
} else {
...
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; // use last registry url
}
}
if (registryURL != null) { // registry url is available
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, RegistryAwareCluster.NAME);
// 集群支持
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // not a registry url, must be direct invoke.
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
...
// 生成代理类
return (T) proxyFactory.getProxy(invoker);
}
服务订阅
refprotocol.refer会调用到RegistryProtocol.doRefer方法
private Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
RegistryDirectory directory = new RegistryDirectory(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
...
directory.buildRouterChain(subscribeUrl);
// 订阅
directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
directory.subscribe:
public void subscribe(URL url) {
...
registry.subscribe(url, this);
}
directory通过registry完成订阅操作,同时RegistryDirectory还实现了NotifyListener接口,把自身作为监听者。
RegistryDirectory.notify会调用refreshOverrideAndInvoker,再调用refreshInvoker创新Invoker。
refreshInvoker通过toInvokers创建Invoker
private Map> toInvokers(List urls) {
...
Invoker invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // Not in the cache, refer again
try {
boolean enabled = true;
...
if (enabled) {
// 创建invoker
invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
}
} catch (Throwable t) {
...
}
if (invoker != null) { // Put new invoker in cache
newUrlInvokerMap.put(key, invoker);
}
} else {
newUrlInvokerMap.put(key, invoker);
}
return newUrlInvokerMap;
}
RegistryDirectory订阅服务,监听服务端的变化,创建Invoker,然后存放在urlInvokerMap属性中,以便后续使用。
启动客户端
protocol.refer
会调用DubboProtocol.refer。
public Invoker refer(Class serviceType, URL url) throws RpcException {
...
// 创建Invoker
DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
getClients会检查是否能共用Client,如果可以,使用shareClient,否则initClient。
initClient会调用Exchangers.connect方法。
这里server端一样,经过Exchangers(HeaderExchanger), Transporter(NettyTransporter), 然后生成NettyClient。这里不再复述。
动态代理
proxyFactory.getProxy默认通过JavassistProxyFactory实现。
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
Proxy根据需代理的interfaces生成代码字符串,通过Javassist生成动态类。
这里直接看生成动态类,生成动态类的方法体为
Object[] args = new Object[1];
args[0] = ($w)$1;
Object ret = handler.invoke(this, methods[0], args);
return (java.lang.String)ret;
handler就是JavassistProxyFactory newInstance时传入的参数。
集群容错
回顾一下Invoker invoker = cluster.join(directory)
,它通过directory获取了invoker。
cluster默认为FailoverCluster
public Invoker join(Directory directory) throws RpcException {
return new FailoverClusterInvoker(directory);
}
但FailoverCluster处理前,要经过装饰者MockClusterWrapper
public Invoker join(Directory directory) throws RpcException {
return new MockClusterInvoker(directory,
this.cluster.join(directory));
}
可以看到,这里Invoker也有多层装饰:InvokerInvocationHandler ---> MockClusterInvoker ---> FailoverClusterInvoker。
InvokerInvocationHandler用于toString/hashCode/equals等特殊方法。
FailoverClusterInvoker实现重试机制,还有其他重试策略FailoverClusterInvoker、FailfastClusterInvoker、FailbackClusterInvoker、AvailableClusterInvoker等。
看看默认的FailoverClusterInvoker.doInvoke
public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException {
// 重试次数
int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// retry loop.
RpcException le = null; // last exception.
// 已调用的invoked
List> invoked = new ArrayList>(copyinvokers.size()); // invoked invokers.
Set providers = new HashSet(len);
for (int i = 0; i < len; i++) {
...
// 选择invoker
Invoker invoker = select(loadbalance, invocation, copyinvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List) invoked);
try {
// 调用invoker,发送网络请求
Result result = invoker.invoke(invocation);
return result;
} catch (RpcException e) {
if (e.isBiz()) { // biz exception.
throw e;
}
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
throw new RpcException(...);
}
注意,如果是server端抛出的业务异常,不会重发请求,只有rpc异常(如连接超时),才会重发请求。
FailoverClusterInvoker的父类AbstractClusterInvoker实现了服务路由,负载均衡等机制
public Result invoke(final Invocation invocation) throws RpcException {
...
// 服务路由
List> invokers = list(invocation);
// 负载均衡
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
服务路由
服务路由机制可以筛选出符合路由规则的服务提供者。
Dubbo 目前提供了三种服务路由实现,分别为条件路由 ConditionRouter、脚本路由 ScriptRouter 和标签路由 TagRouter。
list
方法通过directory获取Invoker,通过路由规则过滤Invoker。这里不再深入解析了。有需要在自行查看。
负载均衡
Dubbo 提供了4种负载均衡实现,分别是基于权重随机算法的 RandomLoadBalance、基于最少活跃调用数算法的 LeastActiveLoadBalance、基于 hash 一致性的 ConsistentHashLoadBalance,以及基于加权轮询算法的 RoundRobinLoadBalance。
这里也不再深入解析了