1. 程式人生 > 實用技巧 >Dubbo 叢集容錯模式詳解

Dubbo 叢集容錯模式詳解

參考:

https://blog.csdn.net/u011642663/article/details/81913325

Dubbo 的叢集容錯模式:Failover Cluster

失敗自動切換,當出現失敗,重試其它伺服器,通常用於讀操作(推薦使用),缺點:重試會帶來更長延遲

本文簡單介紹 Dubbo 中的 Failover Cluster(失敗自動切換)。

1簡介

呼叫例項失敗後,繼續呼叫其他例項。假如有 3 個例項:A, B, C,當呼叫 A 失敗後,再呼叫 B,如果還是失敗,則呼叫 C。

2如何使用

<dubbo:service cluster="failover" retries="2"/>

<dubbo:reference cluster="failover" retries="2"/>

3.實現邏輯

  1. 根據負載均衡演算法選中被呼叫例項

  2. 執行選中的例項;將例項儲存到已經呼叫的列表中

  3. 執行成功則返回;執行不成功則選下個呼叫例項(排除已經呼叫的例項)

4原始碼

public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {

private static final Logger logger = LoggerFactory.getLogger(FailoverClusterInvoker.class);

public FailoverClusterInvoker(Directory<T> directory) {
super(directory);
}

@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyinvokers = invokers;
checkInvokers(copyinvokers, invocation);
// 獲取呼叫次數的配置 retries
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);
}
// 選擇這次要呼叫的例項
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
// 新增到已經呼叫的例項列表中
invoked.add(invoker);
// 儲存到上下文
RpcContext.getContext().setInvokers((List) invoked);
try {
// 執行呼叫
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 {
// 儲存已經呼叫的例項的 url 地址
providers.add(invoker.getUrl().getAddress());
}
}
// 重試了 retries 次後如果沒有呼叫成功,則報錯
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);
}

}