一.参考
dubbo的spi和生产端的服务导出的源码分析参考之前的帖子.
参考dubbo-2.7.x版本的源码
二.架构
三.测试用例
使用dubbo-demo包下dubbo-demo-xml-consumer包下的org.apache.dubbo.demo.consumer.Application#main测试方法.
测试用例如下:
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
context.start();
// DemoService demoService = context.getBean("demoService", DemoService.class);
GreetingService greetingService = context.getBean("greetingService", GreetingService.class);
new Thread(() -> {
while (true) {
String greetings = greetingService.hello();
System.out.println(greetings + " from separated thread.");
try {
//Thread.sleep(100);
TimeUnit.MINUTES.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
四.源码分析
(一)初始化代理类
进入方法AbstractApplicationContext#getBean().调用栈如下图
1.依次调用AbstractApplicationContext#getBean()->FactoryBeanRegistrySupport#doGetObjectFromFactoryBean->ReferenceBean#getObject->ReferenceConfig#init,代码如下图:方法逻辑和服务端导出服务类似.
public synchronized void init() {
if (initialized) {
return;
}
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
// compatible with api call.
if (null != this.getRegistries()) {
bootstrap.registries(this.getRegistries());
}
// 初始化配置中心,元数据中心
bootstrap.initialize();
}
checkAndUpdateSubConfigs();
checkStubAndLocal(interfaceClass);
ConfigValidationUtils.checkMock(interfaceClass, this);
//map中隐含了应用,模块,接口,方法的上下级关系的覆盖.
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, CONSUMER_SIDE);
ReferenceConfigBase.appendRuntimeParameters(map);
if (!ProtocolUtils.isGeneric(generic)) {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), COMMA_SEPARATOR));
}
}
map.put(INTERFACE_KEY, interfaceName);
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
// remove 'default.' prefix for configs from ConsumerConfig
// appendParameters(map, consumer, Constants.DEFAULT_KEY);
AbstractConfig.appendParameters(map, consumer);
AbstractConfig.appendParameters(map, this);
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
Map<String, AsyncMethodInfo> attributes = null;
if (CollectionUtils.isNotEmpty(getMethods())) {
attributes = new HashMap<>();
for (MethodConfig methodConfig : getMethods()) {
AbstractConfig.appendParameters(map, methodConfig, methodConfig.getName());
String retryKey = methodConfig.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(methodConfig.getName() + ".retries", "0");
}
}
AsyncMethodInfo asyncMethodInfo = AbstractConfig.convertMethodConfig2AsyncInfo(methodConfig);
if (asyncMethodInfo != null) {
// consumerModel.getMethodModel(methodConfig.getName()).addAttribute(ASYNC_KEY, asyncMethodInfo);
attributes.put(methodConfig.getName(), asyncMethodInfo);
}
}
}
String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(hostToRegistry)) {
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException(
"Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(REGISTER_IP_KEY, hostToRegistry);
serviceMetadata.getAttachments().putAll(map);
//核心,创建接口的代理
ref = createProxy(map);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
ConsumerModel consumerModel = repository.lookupReferredService(serviceMetadata.getServiceKey());
consumerModel.setProxyObject(ref);
consumerModel.init(attributes);
initialized = true;
checkInvokerAvailable();
// dispatch a ReferenceConfigInitializedEvent since 2.7.4
dispatch(new ReferenceConfigInitializedEvent(this, invoker));
然后依次进入ReferenceConfig#createProxy->包装类ProtocolFilterWrapper#refer->包装类ProtocolListenerWrapper#refer->RegistryProtocol#refer->DubboProtocol#protocolBindingRefer->RegistryProtocol#interceptInvoker->MigrationRuleListener#onRefer->MigrationRuleHandler#doMigrate->MigrationInvoker#refreshInterfaceInvoker->RegistryProtocol#doCreateInvoker->AbstractRegistry#notify().在这个方法中,遍历所有接口url列表,过滤出来当前测试接口的三个provide的url.进入RegistryDirectory#refreshInvoker.然后进入RegistryDirectory#toInvokers,遍历三个url,每个url创建一个Invoker对象。如下图所示:
private Map<URL, Invoker<T>> toInvokers(List<URL> urls) {
Map<URL, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<>();
if (CollectionUtils.isEmpty(urls)) {
return newUrlInvokerMap;
}
Set<URL> keys = new HashSet<>();
String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
//遍历zookeeper上提供的当前接口的生产者的url
for (URL providerUrl : urls) {
//xxxx 省略代码
URL url = mergeUrl(providerUrl);
if (keys.contains(url)) { // Repeated url
continue;
}
keys.add(url);
// Cache key is url that does not merge with consumer side parameters,
//regardless of how the consumer combines parameters, if the server url changes, then refer again
Map<URL, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(url);
if (invoker == null) { // Not in the cache, refer again
try {
//xxx 省略代码
if (enabled) {
//创建一个生产者的invoker对象,后面赋值到RegistryDirectory#urlInvokerMap里面
//里面的prefer方法进入ProtocolFilterWrapper#refer,在下面代码2处分析
invoker = new InvokerDelegate<>(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) { // Put new invoker in cache
newUrlInvokerMap.put(url, invoker);
}
} else {
newUrlInvokerMap.put(url, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
三个invoker对象在RegistryDirectory#refreshInvoker方法中,写入RegistryDirectory#urlInvokerMap成员中.
2.在ProtocolFilterWrapper#refer方法中.继续构造调用链.如下图代码.
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (UrlUtils.isRegistry(url)) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
}
//默认有三个Filter,ConsumeContextFilter,FutureFilter,MonitorFilter
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
last = new FilterNode<T>(invoker, last, filter);
}
}
return last;
}
3.在ReferenceConfig#createProxy方法中,最后调用ProxyFactory#getProxy()生成invoker的代理对象返回,作为接口的实际代理对象。
(二).发起接口调用
调用接口的方式时,进入org.apache.dubbo.rpc.proxy.InvokerInvocationHandler#invoke调用栈如下图所示:
依次调用InvokerInvocationHandler#invoke->MigrationInvoker#invoke->MockClusterInvoker#invoke,代码如下图:
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
String value = getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
//no mock
//正常调用进入这里
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
}
//force:direct mock
//直连调用,开发测试用
result = doMockInvoke(invocation, null);
} else {
//fail-mock
try {
result = this.invoker.invoke(invocation);
//fix:#4585
if(result.getException() instanceof RpcException){
RpcException rpcException= (RpcException)result.getException();
if(rpcException.isBiz()){
throw rpcException;
}else {
result = doMockInvoke(invocation, rpcException);
}
}
} catch (RpcException e) {
if (e.isBiz()) {
throw e;
}
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + getUrl(), e);
}
//调用异常时,返回mock降级结果.
result = doMockInvoke(invocation, e);
}
}
return result;
}
进入InterceptorInvokerNode#invoke,依次调用ClusterInterceptor#intercept->AbstractClusterInvoker#invoke,代码如下:
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
// binding attachments into invocation.
Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
}
//根据路由策略选择可以调用的地址
List<Invoker<T>> invokers = list(invocation);
//初始化负载均衡策略,加载cluster.LoadBalance的spi的所有实现类,默认走random策略
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
2.路由策略
从AbstractClusterInvoker#invoke方法进入DynamicDirectory#doList,如下图:
public List<Invoker<T>> doList(Invocation invocation) {
if (forbidden && shouldFailFast) {
// 1. No service provider 2. Service providers are disabled
throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +
getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +
NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +
", please check status of providers(disabled, not registered or in blacklist).");
}
if (multiGroup) {
return this.invokers == null ? Collections.emptyList() : this.invokers;
}
List<Invoker<T>> invokers = null;
try {
// Get invokers from cache, only runtime routers will be executed.
//过滤路由策略
invokers = routerChain.route(getConsumerUrl(), invocation);
} catch (Throwable t) {
logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
}
return invokers == null ? Collections.emptyList() : invokers;
}
进入下图代码,
依次调用MockInvokersSelector,TagRouter,ServiceRouter,AppRouter的route方法过滤路由.
3.从AbstractClusterInvoker#invoke方法依次调用FailoverClusterInvoker#doInvoke,代码如下图,
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyInvokers = invokers;
checkInvokers(copyInvokers, invocation);
String methodName = RpcUtils.getMethodName(invocation);
int len = calculateInvokeTimes(methodName);
// retry loop.
RpcException le = null; // last exception.
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
//len是生产者的个数
for (int i = 0; i < len; i++) {
//Reselect before retry to avoid a change of candidate `invokers`.
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
if (i > 0) {
checkWhetherDestroyed();
copyInvokers = list(invocation);
// check again
checkInvokers(copyInvokers, invocation);
}
//进行负载均衡的选择,下面的4处分析.
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List) invoked);
try {
// 发起调用,下面的5处分析
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + methodName
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
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(le.getCode(), "Failed to invoke the method "
+ methodName + " in the service " + getInterface().getName()
+ ". Tried " + len + " times of the providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Last error is: "
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
}
4.FailoverClusterInvoker#doInvoke方法进入负载均衡AbstractClusterInvoker#select.代码如下:
private Invoker<T> doSelect(LoadBalance loadbalance, Invocation invocation,
List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
if (CollectionUtils.isEmpty(invokers)) {
return null;
}
//只有一个provider,不需要负载均衡.
if (invokers.size() == 1) {
return invokers.get(0);
}
//进入具体的负载均衡策略,选择合适的invoker
Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);
//If the `invoker` is in the `selected` or invoker is unavailable && availablecheck is true, reselect.
if ((selected != null && selected.contains(invoker))
|| (!invoker.isAvailable() && getUrl() != null && availablecheck)) {
try {
//不同的集群策略,可能重复进入doSelect方法,已选择的provider,排除掉,重新从其他的provider中进行选择
Invoker<T> rInvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);
if (rInvoker != null) {
invoker = rInvoker;
} else {
//Check the index of current selected invoker, if it's not the last one, choose the one at index+1.
int index = invokers.indexOf(invoker);
try {
//Avoid collision
invoker = invokers.get((index + 1) % invokers.size());
} catch (Exception e) {
logger.warn(e.getMessage() + " may because invokers list dynamic change, ignore.", e);
}
}
} catch (Throwable t) {
logger.error("cluster reselect fail reason is :" + t.getMessage() + " if can not solve, you can set cluster.availablecheck=false in url", t);
}
}
return invoker;
}
默认是随机算法。其他还有一致性哈希,最少调用,最短响应时间,Round robin策略.
5.从3中的FailoverClusterInvoker#doInvoke依次进入InvokerWrapper#invoke->(一).2中的三个filter,ConsumeContextFilter,FutureFilter,MonitorFilter的invoke方法->ListenerInvokerWrapper#invoke->AbstractInvoker#invoke->DubboInvoker#doInvoke(添加dubbo写的attachment)->ReferenceCountExchangeClient#request()->HeaderExchangeClient#request()->HeaderExchangeChannel#request()->AbstractPeer#send->AbstractClient#send,调用网络层发送数据出去.