當前位置:
首頁 > 知識 > dubbo同名方法的問題及思考

dubbo同名方法的問題及思考

背景

今天小夥伴問我一個問題

dubbo同名方法的問題及思考


分析

我們系統中一直存在該種實踐 似乎從來都穩穩的運行,沒有任何問題呢……

比如

*
* 查詢客戶List
* @param customerCarVO
* @param curPage
* @return
* @throws Exception
*/
@Deprecated
PageResult<CustomerCarVO> getPageCustomerList(CustomerCarVO customerCarVO, int curPage) throws Exception;
/**
* 查詢客戶List
* @param customerCarSo
* @return
* @throws Exception
*/
PageResult<CustomerCarVO> getPageCustomerList(CustomerCarSo customerCarSo) throws Exception;

用了這麼久 似乎也沒有出現問題~

那麼是否可以很武斷的認為同名方法沒有問題???

我們想一下以前webservice中似乎有個限制 方法同名將不能使用 也就是說不支持方法的重載 那麼為何dubbo沒有這個問題?

或者說其實存在問題 只不過我們沒注意???

我們首先來看一下對應url如何映射到invoker的!

/**
* 根據invokerURL列錶轉換為invoker列表。轉換規則如下:
* 1.如果url已經被轉換為invoker,則不在重新引用,直接從緩存中獲取,注意如果url中任何一個參數變更也會重新引用
* 2.如果傳入的invoker列表不為空,則表示最新的invoker列表
* 3.如果傳入的invokerUrl列表是空,則表示只是下發的override規則或route規則,需要重新交叉對比,決定是否需要重新引用。
* @param invokerUrls 傳入的參數不能為null
*/
private void refreshInvoker(List<URL> invokerUrls){
if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // 禁止訪問
this.methodInvokerMap = null; // 置空列表
destroyAllInvokers(); // 關閉所有Invoker
} else {
this.forbidden = false; // 允許訪問
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<URL>();
this.cachedInvokerUrls.addAll(invokerUrls);//緩存invokerUrls列表,便於交叉對比
}
if (invokerUrls.size() ==0 ){
return;
}
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 將URL列錶轉成Invoker列表
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 換方法名映射Invoker列表
// state change
//如果計算錯誤,則不進行處理.
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
return ;
}
this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
this.urlInvokerMap = newUrlInvokerMap;
try{
destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 關閉未使用的Invoker
}catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}

我們來看到這邊通過invokerUrls來刷新獲取到對應的遠程執行者 那麼這段核心代碼將會映射直接的調用者!根據method的映射newMethodInvokerMap 也就是根據method名稱獲取到最新的invoker列表

如下方法通過url轉換成為特定的根據method的invoker列表

/**
* 將invokers列錶轉成與方法的映射關係
*
* @param invokersMap Invoker列表
* @return Invoker與方法的映射關係
*/
private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
// 按提供者URL所聲明的methods分類,兼容註冊中心執行路由過濾掉的methods
List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
if (invokersMap != null && invokersMap.size() > 0) {
for (Invoker<T> invoker : invokersMap.values()) {
String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
if (parameter != null && parameter.length() > 0) {
String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
if (methods != null && methods.length > 0) {
for (String method : methods) {
if (method != null && method.length() > 0
&& ! Constants.ANY_VALUE.equals(method)) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null) {
methodInvokers = new ArrayList<Invoker<T>>();
newMethodInvokerMap.put(method, methodInvokers);
}
methodInvokers.add(invoker);
}
}
}
}
invokersList.add(invoker);
}
}
newMethodInvokerMap.put(Constants.ANY_VALUE, invokersList);
if (serviceMethods != null && serviceMethods.length > 0) {
for (String method : serviceMethods) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
if (methodInvokers == null || methodInvokers.size() == 0) {
methodInvokers = invokersList;
}
newMethodInvokerMap.put(method, route(methodInvokers, method));
}
}
// sort and unmodifiable
for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
Collections.sort(methodInvokers, InvokerComparator.getComparator());
newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
}
return Collections.unmodifiableMap(newMethodInvokerMap);
}

由於通過url拼接的時候如下操作

if (generic) {
map.put("generic", String.valueOf(true));
map.put("methods", Constants.ANY_VALUE);
} else {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put("revision", revision);
}

String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if(methods.length == 0) {
logger.warn("NO method found in service interface " + interfaceClass.getName());
map.put("methods", Constants.ANY_VALUE);
}
else {
map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}

很明顯的set將會抹除掉重複的方法名稱 那麼實質上在url上不會出現相同的methodname

那麼為何在使用的時候沒有錯呢???

我們來繼續回顧上一段代碼

由於根據方法名稱放入了特定的invoker 而後根據invoker傳遞給特殊的調用參數【這個就是實際的網路請求 也就是rpc】

此時可以看到真正傳遞過去的是

/**
* Invocation. (API, Prototype, NonThreadSafe)
*
* @serial Don"t change the class name and package name.
* @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
* @see com.alibaba.dubbo.rpc.RpcInvocation
* @author qian.lei
* @author william.liangf
*/
public interface Invocation {
/**
* get method name.
*
* @serial
* @return method name.
*/
String getMethodName();

/**
* get parameter types.
*
* @serial
* @return parameter types.
*/
Class<?>[] getParameterTypes();

/**
* get arguments.
*
* @serial
* @return arguments.
*/
Object[] getArguments();

/**
* get attachments.
*
* @serial
* @return attachments.
*/
Map<String, String> getAttachments();

/**
* get attachment by key.
*
* @serial
* @return attachment value.
*/
String getAttachment(String key);

/**
* get attachment by key with default value.
*
* @serial
* @return attachment value.
*/
String getAttachment(String key, String defaultValue);

/**
* get the invoker in current context.
*
* @transient
* @return invoker.
*/
Invoker<?> getInvoker();
}

很明顯這裡面可以獲取到真實的參數 包括參數類型 自然調用不會出錯了啊!!!


深思

可以認為沒問題了么???對的 大部分場景是沒有問題的!!!

dubbo同名方法的問題及思考

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

mybatis的整體架構
開源定時任務框架Quartz

TAG:程序員小新人學習 |