Dubbo源码阅读(一)-服务调用流程

4 篇文章 0 订阅

在消费端基于Dubbo API的方式调用服务:

public class ApiConsumerApplication {
    public static void main(String[] args) {
        // 1. 创建服务引用对象实例
        ReferenceConfig<IGreetingService> referenceConfig = new ReferenceConfig<IGreetingService>();
        // 2. 设置应用程序信息
        referenceConfig.setApplication(new ApplicationConfig("deep-in-dubbo-first-consumer"));
        // 3. 设置注册中心
        referenceConfig.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181/"));
        // 4. 设置服务接口和超时时间
        referenceConfig.setInterface(IGreetingService.class);
        // 默认重试3次
        referenceConfig.setTimeout(5000);
        // 5 设置服务分组和版本
        referenceConfig.setGroup("dubbo-sxzhongf-group");
        referenceConfig.setVersion("1.0.0");
        // 6. 引用服务
        IGreetingService greetingService = referenceConfig.get();
        // 7. 设置隐式参数
        RpcContext.getContext().setAttachment("company", "sxzhongf");
        // 获取provider端传递的隐式参数(FIXME: 需要后续追踪)
//        System.out.println("年龄是:" + RpcContext.getContext().getAttachment("age"));
        //8. 调用服务
        System.out.println(greetingService.sayHello("pan"));
    }
}

一、生成透明代理

我们知道消费端依赖的只是服务端的一个接口,具体服务实现类在服务端,那么上面代码的第6步:引用服务一定生成的是类似透明代理的东西。接下来就从referenceConfig.get()开始,看下调用链:

ReferenceConfig#get
	->ReferenceConfig#init
		->ReferenceConfig#createProxy
			->DubboProtocol#protocolBindingRefer —— 生成Invoker
				->AbstractProxyFactory#getProxy(org.apache.dubbo.rpc.Invoker<T>) —— 根据Invoker生成透明代理
					->JavassistProxyFactory#getProxy

借用网上的一张图片,也可以直观的看到透明代理的生成过程:
在这里插入图片描述

再看下ReferenceConfig#createProxy的核心源码:
据是否存在registry注册中心和url个数,决定是否创建cluster invoker,cluster invoker会在服务调用的时候用到。另外,这个透明代理一定封装了远端服务的IP端口、地址信息和服务类名、方法名等信息。

private T createProxy(Map<String, String> map) {
	// JVM内部引用
	if (shouldJvmRefer(map)) {
		URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
		invoker = REF_PROTOCOL.refer(interfaceClass, url);
		if (logger.isInfoEnabled()) {
			logger.info("Using injvm service " + interfaceClass.getName());
		}
	} else {
		// P2P 直连url
		urls.clear(); // reference retry init will add url to urls, lead to OOM
		if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
			String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
			if (us != null && us.length > 0) {
				for (String u : us) {
					URL url = URL.valueOf(u);
					if (StringUtils.isEmpty(url.getPath())) {
						url = url.setPath(interfaceName);
					}
					if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
						urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
					} else {
						urls.add(ClusterUtils.mergeUrl(url, map));
					}
				}
			}
			// 注册中心模式
		} else { // assemble URL from register center's configuration
			// if protocols not injvm checkRegistry
			if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())){
				checkRegistry();
				List<URL> us = loadRegistries(false);
				if (CollectionUtils.isNotEmpty(us)) {
					for (URL u : us) {
						URL monitorUrl = loadMonitor(u);
						if (monitorUrl != null) {
							map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
						}
						urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
					}
				}
				if (urls.isEmpty()) {
					throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
				}
			}
		}

		// 如果只有一个
		if (urls.size() == 1) {
			invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
		} else {
			List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
			URL registryURL = null;
			for (URL url : urls) {
			
				// Dubbo SPI自适应拓展点,根据url的协议类型registry指定处理类RegistryProtocol
				invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
				if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
					registryURL = url; // use last registry url
				}
			}
			if (registryURL != null) { // registry url is available
				// use RegistryAwareCluster only when register's CLUSTER is available
				URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
				
				// 通过英文注释可知,这个是集群模式,需要进行路由负载均衡
				// The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
				invoker = CLUSTER.join(new StaticDirectory(u, invokers));
			} else { // not a registry url, must be direct invoke.
				invoker = CLUSTER.join(new StaticDirectory(invokers));
			}
		}
	}

	// 省略部分代码

	// 通过JavassistProxyFactory创建透明代理
	// create service proxy
	return (T) PROXY_FACTORY.getProxy(invoker);
}

JavassistProxyFactory#getProxy

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
	return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

看下 InvokerInvocationHandler类的继承关系:
在这里插入图片描述
联想到JDK的动态代理,这个InvokerInvocationHandler的invoke()方法是执行代理方法的地方,也就是说Dubbo服务调用就是从此开始的!

二、服务调用流程

先看下调用链:

InvokerInvocationHandler#invoke 
	->MockClusterInvoker#invoke —— 服务降级相关
		->AbstractClusterInvoker#invoke —— 集群负载均衡
			->FailoverClusterInvoker#doInvoke
				->AbstractInvoker#invoke
					->DubboInvoker#doInvoke —— dubbo协议调用

AbstractClusterInvoker#invoke:

public Result invoke(final Invocation invocation) throws RpcException {
	checkWhetherDestroyed();

	// 隐式传参
	// binding attachments into invocation.
	Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
	if (contextAttachments != null && contextAttachments.size() != 0) {
		((RpcInvocation) invocation).addAttachments(contextAttachments);
	}

	List<Invoker<T>> invokers = list(invocation);
	// 根据Dubbo SPI 自适应拓展点指定负载均衡策略,模式是random随机
	LoadBalance loadbalance = initLoadBalance(invokers, invocation);
	RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
	return doInvoke(invocation, invokers, loadbalance);
}

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 = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
	if (len <= 0) {
		len = 1;
	}
	// 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);
	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);
		}
		
		// 执行负载均衡
		Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
		invoked.add(invoker);
		RpcContext.getContext().setInvokers((List) invoked);
		
		// 执行协议调用
		Result result = invoker.invoke(invocation);
		return result;
	}
}

DubboInvoker#doInvoke

protected Result doInvoke(final Invocation invocation) throws Throwable {
	RpcInvocation inv = (RpcInvocation) invocation;
	final String methodName = RpcUtils.getMethodName(invocation);
	inv.setAttachment(PATH_KEY, getUrl().getPath());
	inv.setAttachment(VERSION_KEY, version);

	ExchangeClient currentClient;
	if (clients.length == 1) {
		currentClient = clients[0];
	} else {
		currentClient = clients[index.getAndIncrement() % clients.length];
	}
	try {
		boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
		int timeout = getUrl().getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
		
		// 异步无返回结果
		if (isOneway) {
			boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
			currentClient.send(inv, isSent);
			return AsyncRpcResult.newDefaultAsyncResult(invocation);
			
		// 异步转同步
		} else {
			AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
			CompletableFuture<Object> responseFuture = currentClient.request(inv, timeout);
			asyncRpcResult.subscribeTo(responseFuture);
			// save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
			FutureContext.getContext().setCompatibleFuture(responseFuture);
			return asyncRpcResult;
		}
	} catch (TimeoutException e) {
		throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
	} catch (RemotingException e) {
		throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
	}
}

注意这里的所谓的异步是指,调用线程、网络IO线程这两个线程默认是解耦的,如果是同步调用,则调用线程在调用完网络IO线程之后,会阻塞等待网络IO线程的返回结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值