Dubbo源码解析
https://www.cnblogs.com/sunshine-2015/category/1132847.html
下图展示了dubbo框架的主要接口和类,以及之间的关系和调用方法名。请看官网的详细解释,还有时序图 调用图
http://dubbo.apache.org/zh-cn/docs/dev/design.html

消费者调用
请结合官网调用源码导读:http://dubbo.apache.org/zh-cn/docs/source_code_guide/service-invoking-process.html
下面介绍消费者调用流程中一些重要的方法。
dubbo接口方法调用后,首先使用动态代理方式执行com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler#invoke方法,如果设置Failover容错机制,则向下执行最终走到com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke方法,doInvoke中执行方法调用成功返回Result,失败重试
//com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker#invoke
@Override
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
LoadBalance loadbalance = null;
//根据invocation匹配可被调用的providers
List<Invoker<T>> invokers = list(invocation);
if (invokers != null && !invokers.isEmpty()) {
//取得loadbalance
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(invocation.getMethodName(), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
}
//如果异步则attachment添加InvocationId,注意此id不是requestid
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
此处的invokers 包含zk中当前方法可以调用的所有服务提供者provider信息和dubbo的配置参数。测试时只启动了一个Provider服务,所以下图invokers 中只有一个invoker。

根据配置的retries次数,开始for循环调用,每次循环通过负载均衡机制选出目标provider(invoker)
//com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker#doInvoke
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
//可被调用的providers
List<Invoker<T>> copyinvokers = invokers;
checkInvokers(copyinvokers, invocation);
//重试次数
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.
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);
}
//执行负载均衡 路由provider
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
//记入已调用provider
invoked.add(invoker);
//将已调用provider放入RpcContext
RpcContext.getContext().setInvokers((List) invoked);
try {
//调用provider(执行到com.alibaba.dubbo.rpc.filter.ConsumerContextFilter#invoke)
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + invocation.getMethodName()
+ " 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 != null ? le.getCode() : 0, "Failed to invoke the method "
+ invocation.getMethodName() + " 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 != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
}
负载均衡(以加权Random为例)
官网源码导读 http://dubbo.apache.org/zh-cn/docs/source_code_guide/loadbalance.html
//com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance#doSelect
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size(); // Number of invokers
int totalWeight = 0; // The sum of weights
boolean sameWeight = true; // Every invoker has the same weight?
//计算所有invokers总权重
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
totalWeight += weight; // Sum
if (sameWeight && i > 0
&& weight != getWeight(invokers.get(i - 1), invocation)) {
//如果与前一个invoker权重不同
sameWeight = false;
}
}
//如果权重不同
if (totalWeight > 0 && !sameWeight) {
//取小于总权重的随机数 做偏移量
int offset = random.nextInt(totalWeight);
//遍历invokers,offset-其权重,直到offset小于零返回invoker
for (int i = 0; i < length; i++) {
offset -= getWeight(invokers.get(i), invocation);
if (offset < 0) {
return invokers.get(i);
}
}
}
// If all invokers have the same weight value or totalWeight=0, return evenly.
return invokers.get(random.nextInt(length));
}
RpcContext装配信息,RpcInvocation装配invoker
//com.alibaba.dubbo.rpc.filter.ConsumerContextFilter#invoke
//设置RpcContext
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
RpcContext.getContext()
.setInvoker(invoker)
.setInvocation(invocation)
.setLocalAddress(NetUtils.getLocalHost(), 0)
.setRemoteAddress(invoker.getUrl().getHost(),
invoker.getUrl().getPort());
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(invoker);
}
try {
//向下调用provider
return invoker.invoke(invocation);
} finally {
RpcContext.getContext().clearAttachments();
}
}
检验当前方法并发调用量active(AtomicLong) ,开始计时,累计并调用发量active
//com.alibaba.dubbo.rpc.filter.ActiveLimitFilter#invoke
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
String methodName = invocation.getMethodName();
//dubbo配置ACTIVES
int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
//获取当前调用方法的RpcStatus (com.alibaba.dubbo.rpc.RpcStatus#getStatus)
RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
if (max > 0) { //配置了active
//dubbo配置timeout
long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
//计时
long start = System.currentTimeMillis();
long remain = timeout;
int active = count.getActive();
if (active >= max) {
//当前并发数active已超dubbo配置
//syn 当前方法RpcStatus ,阻塞其他线程
synchronized (count) {
//使用锁为了让线程wait (因为wait必须持锁)
while ((active = count.getActive()) >= max) {
//当前active 超过配置
try {
//当前线程挂起并释放锁,等待被执行完的线程唤醒 或 自然醒
count.wait(remain);
//wait醒来后必须循环 再次判断条件
} catch (InterruptedException e) {
}
long elapsed = System.currentTimeMillis() - start;
remain = timeout - elapsed;
if (remain <= 0) {
throw new RpcException("Waiting concurrent invoke timeout in client-side for service: "
+ invoker.getInterface().getName() + ", method: "
+ invocation.getMethodName() + ", elapsed: " + elapsed
+ ", timeout: " + timeout + ". concurrent invokes: " + active
+ ". max concurrent invoke limit: " + max);
}
}
}
}
}
//执行到这里 1 未配置active 2 未到达active限制
try {
//调用计时开始
long begin = System.currentTimeMillis();
//url 和 method 对应的RpcStatus都active + 1
RpcStatus.beginCount(url, methodName);
try {
//继续向下调用(到com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter#invoke)
Result result = invoker.invoke(invocation);
//RpcStatus active - 1
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);
return result;
} catch (RuntimeException t) {
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);
throw t;
}
} finally {
if (max > 0) {
synchronized (count) {
count.notify();
}
}
}
}
//com.alibaba.dubbo.rpc.RpcStatus#getStatus
//取得当前方法的RpcStatus
public static RpcStatus getStatus(URL url, String methodName) {
String uri = url.toIdentityString();
ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
if (map == null) {
//不存在则放入
METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());
map = METHOD_STATISTICS.get(uri);
}
RpcStatus status = map.get(methodName);
if (status == null) {
//不存在则放入map
map.putIfAbsent(methodName, new RpcStatus());
//再从map中取出,保证并发安全
status = map.get(methodName);
}
return status;
}

针对 无返回、异步、同步调用进行不同处理
//com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke
protected Result doInvoke(final Invocation invocation) throws Throwable {
//装填invocation
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
//选择NettyClient
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);//需要Return?
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout);
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
RpcContext.getContext().setFuture(null);
//request返回ResponseFuture(DefaultFuture),get()挂起当前线程,等待被接收到响应的线程唤醒
return (Result) currentClient.request(inv, timeout).get();
}
} 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);
}
}
attachments中携带配置信息和自定义KV,传递给服务提供者

Netty客户端列表

创建DefaultFuture,Netty发送调用信息,返回DefaultFuture。
//com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeChannel#request(java.lang.Object, int)
public ResponseFuture request(Object request, int timeout) throws RemotingException {
if (closed) {
throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
}
// create request.
//Request构造器中分配请求ID(static AtomicLong INVOKE_ID)
Request req = new Request();
req.setVersion("2.0.0");
req.setTwoWay(true);
req.setData(request);
//为请求request创建future
DefaultFuture future = new DefaultFuture(channel, req, timeout);
try {
//Netty发送
channel.send(req);
} catch (RemotingException e) {
future.cancel();
throw e;
}
return future;
}
DefaultFuture
每个请求/线程对应一个DefaultFuture,这个类对象是挂起/唤醒请求线程的关键。请求线程发出请求后,调用自己对应的defaultFuture.get()方法挂起等待响应。NettyIO线程接收到响应信息后,根据请求id在FUTURES缓存中找到对应请求的defaultFuture,唤醒其请求线程。
下面是几个主要方法
/**
==================================DefaultFuture类静态内容=========================================
类中包含两个静态容器,一个守护线程扫描响应超时的请求。
*/
//缓存已发出请求的channel <请求ID, Channel>
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
//缓存已发出请求的Future <请求ID, DefaultFuture>
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
static {
//从Thread名可以推断,这个守护线程工作是死循环扫描FUTURES,对响应超时的请求doReceived(timeOutResponse)
Thread th = new Thread(new RemotingInvocationTimeoutScan(), "DubboResponseTimeoutScanTimer");
th.setDaemon(true);
th.start();
}
/**
==================================DefaultFuture非静态内容=========================================
*/
//一个线程一次请求实例化一个defaultFuture
//成员变量
private final long id; // invoke id.
private final Channel channel;
private final Request request;
private final int timeout;
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
private final long start = System.currentTimeMillis();
private volatile long sent;
private volatile Response response;
private volatile ResponseCallback callback;
//DefaultFuture构造器
public DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
//取得请求ID
this.id = request.getId();
//消费端没有设置超时,则取得ZK中提供者配置
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
// put into waiting map.
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
//获取结果/挂起当前线程
public Object get(int timeout) throws RemotingException {
if (timeout <= 0) {
timeout = Constants.DEFAULT_TIMEOUT;
}
if (!isDone()) {
long start = System.currentTimeMillis();
//=================================Dubbo中实现请求线程挂起的方式begin==================================================
/**
主要利用的是lock.newCondition().await(timeout)实现挂起。这种思路在dubbo多处源码可见。
此处因为defaultFuture对象是每个请求相互独立,且此处lock也是defaultFuture的非静态成员变量。
理论上一个线程一次请求一个defaultFuture,只会有请求/响应线程持有lock,所以不会出现大量线程竞争lock的现象。所以性能上不会有影响。
new Countdownlacth(1)可以作为替换方案
*/
lock.lock();
try {
while (!isDone()) {
//done = lock.newCondition();
//释放lock + 请求线程挂起timeout时间,等待唤醒 (必须持有lock ,才能执行done.await)
done.await(timeout, TimeUnit.MILLISECONDS);
/*
醒来后 判断超时/响应 ? 继续while
情况1:被唤醒,向下执行break。提前被响应线程唤醒
情况2:自然醒,向下执行break。timeout超时
*/
if (isDone() || System.currentTimeMillis() - start > timeout) {
break;
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
//=================================Dubbo中实现请求线程挂起的方式end==================================================
if (!isDone()) {
throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
}
}
return returnFromResponse();
}
//NettyIO线程输入流中会调用收到方法
//在IDEA中向上搜索方法引用 ,最终找到dubbo的NettyIOHandler
//com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleResponse
private void doReceived(Response res) {
//=================================Dubbo中实现请求线程唤醒的方式begin==================================================
lock.lock();
try {
response = res;
if (done != null) {
//接收响应的线程在此唤醒请求线程
done.signal();
}
} finally {
lock.unlock();
}
//=================================Dubbo中实现请求线程唤醒的方式end==================================================
if (callback != null) {
//若设置了回调对象 执行回调
invokeCallback(callback);
}
}


浙公网安备 33010602011771号