总结/朱季谦
先前在测试环境遇到过一个问题,即Dubbo广播机制,在对各个提供者节点进行广播操作过程中,存在最前面的两个节点出现异常的情况,但后边的其他节点仍能正常同步的情况。我以前就知道Dubbo的Broadcast机制,先前概念里总以为这是一个当广播到某个节点若出现异常时,就会直接停止广播操作,但在Dubbo的广播机制里,却不是这样。它会先遍历所有的Invokers调用,若过程出现异常时,只会先将异常先类似日志一样记录下来,等到Invokers遍历完成后,最后才会将最后保留的异常进行抛出。
这就能解释了,为啥存在两个节点出现异常的情况下,后面的节点仍能正常被广播通知到。
接下来,我们简单看下Dubbo的Broadcast机制源码,这里的代码很好看懂——
接下来,我们简单看下Dubbo的broadcast机制源码——
代码语言:javascript复制public class BroadcastClusterInvoker<T> extends AbstractClusterInvoker<T> {
private static final Logger logger = LoggerFactory.getLogger(BroadcastClusterInvoker.class);
public BroadcastClusterInvoker(Directory<T> directory) {
super(directory);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
//通过CollectionUtils.isEmpty(invokers)检查invokers集合是否为空,若为空,抛出异常
checkInvokers(invokers, invocation);
//类似上下文操作保存invokers
RpcContext.getContext().setInvokers((List) invokers);
RpcException exception = null;
Result result = null;
//遍历invoker远程调用接口服务
for (Invoker<T> invoker : invokers) {
try {
result = invoker.invoke(invocation);
} catch (RpcException e) {
//若出现异常,将异常信息进行保存
exception = e;
logger.warn(e.getMessage(), e);
} catch (Throwable e) {
exception = new RpcException(e.getMessage(), e);
logger.warn(e.getMessage(), e);
}
}
//等invokers遍历完成后,若存在异常,再对异常进行抛出
if (exception != null) {
throw exception;
}
return result;
}
}
这里的遍历操作是单线程进行的,存在一个问题,若invokers数量很庞大,那么,将会出现广播耗时的情况,我觉得这里若invokers数据量过大时,可以通过选择有返回值的线程池并发执行。