目標:介紹injvm本地調用的設計和實現,介紹dubbo-rpc-injvm的源碼。
dubbo是一個遠程調用的框架,可是它沒有理由不支持本地調用,本文就要講解dubbo關於本地調用的實現。本地調用要比遠程調用簡單的多。java
該類繼承了AbstractExporter,是本地服務的暴露者封裝,其中實現比較簡單。只是實現了unexport方法,而且維護了一份保存暴露者的集合。git
class InjvmExporter<T> extends AbstractExporter<T> { /** * 服務key */ private final String key; /** * 暴露者集合 */ private final Map<String, Exporter<?>> exporterMap; InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) { super(invoker); this.key = key; this.exporterMap = exporterMap; exporterMap.put(key, this); } /** * 取消暴露 */ @Override public void unexport() { // 調用父類的取消暴露方法 super.unexport(); // 從集合中移除 exporterMap.remove(key); } }
該類繼承了AbstractInvoker類,是本地調用的invoker實現。github
class InjvmInvoker<T> extends AbstractInvoker<T> { /** * 服務key */ private final String key; /** * 暴露者集合 */ private final Map<String, Exporter<?>> exporterMap; InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap) { super(type, url); this.key = key; this.exporterMap = exporterMap; } /** * 服務是否活躍 * @return */ @Override public boolean isAvailable() { InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key); if (exporter == null) { return false; } else { return super.isAvailable(); } } /** * invoke方法 * @param invocation * @return * @throws Throwable */ @Override public Result doInvoke(Invocation invocation) throws Throwable { // 得到暴露者 Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl()); // 若是爲空,則拋出異常 if (exporter == null) { throw new RpcException("Service [" + key + "] not found."); } // 設置遠程地址爲127.0.0.1 RpcContext.getContext().setRemoteAddress(NetUtils.LOCALHOST, 0); // 調用下一個調用鏈 return exporter.getInvoker().invoke(invocation); } }
其中重寫了isAvailable和doInvoke方法。框架
該類是本地調用的協議實現,其中實現了服務調用和服務暴露方法,而且封裝了一個判斷是不是本地調用的方法。jvm
/** * 本地調用 Protocol的實現類key */ public static final String NAME = Constants.LOCAL_PROTOCOL; /** * 默認端口 */ public static final int DEFAULT_PORT = 0; /** * 單例 */ private static InjvmProtocol INSTANCE;
static Exporter<?> getExporter(Map<String, Exporter<?>> map, URL key) { Exporter<?> result = null; // 若是服務key不是* if (!key.getServiceKey().contains("*")) { // 直接從集合中取出 result = map.get(key.getServiceKey()); } else { // 若是 map不爲空,則遍歷暴露者,來找到對應的exporter if (map != null && !map.isEmpty()) { for (Exporter<?> exporter : map.values()) { // 若是是服務key if (UrlUtils.isServiceKeyMatch(key, exporter.getInvoker().getUrl())) { // 賦值 result = exporter; break; } } } } // 若是沒有找到exporter if (result == null) { // 則返回null return null; } else if (ProtocolUtils.isGeneric( result.getInvoker().getUrl().getParameter(Constants.GENERIC_KEY))) { // 若是是泛化調用,則返回null return null; } else { return result; } }
該方法是得到相關的暴露者。ide
@Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { // 建立InjvmExporter 而且返回 return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap); }
@Override public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException { // 建立InjvmInvoker 而且返回 return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap); }
public boolean isInjvmRefer(URL url) { final boolean isJvmRefer; // 得到scope配置 String scope = url.getParameter(Constants.SCOPE_KEY); // Since injvm protocol is configured explicitly, we don't need to set any extra flag, use normal refer process. if (Constants.LOCAL_PROTOCOL.toString().equals(url.getProtocol())) { // 若是是injvm,則不是本地調用 isJvmRefer = false; } else if (Constants.SCOPE_LOCAL.equals(scope) || (url.getParameter("injvm", false))) { // if it's declared as local reference // 'scope=local' is equivalent to 'injvm=true', injvm will be deprecated in the future release // 若是它被聲明爲本地引用 scope = local'至關於'injvm = true',將在之後的版本中棄用injvm isJvmRefer = true; } else if (Constants.SCOPE_REMOTE.equals(scope)) { // it's declared as remote reference // 若是被聲明爲遠程調用 isJvmRefer = false; } else if (url.getParameter(Constants.GENERIC_KEY, false)) { // generic invocation is not local reference // 泛化的調用不是本地調用 isJvmRefer = false; } else if (getExporter(exporterMap, url) != null) { // by default, go through local reference if there's the service exposed locally // 默認狀況下,若是本地暴露服務,請經過本地引用 isJvmRefer = true; } else { isJvmRefer = false; } return isJvmRefer; }
該方法是判斷是否爲本地調用。memcached
該部分相關的源碼解析地址: https://github.com/CrazyHZM/i...
該文章講解了遠程調用中關於injvm本地調用的部分,三種抽象的角色仍是比較鮮明的,服務暴露相關的exporter、服務引用相關的invoker、以及協議相關的protocol,關鍵仍是弄清楚再設計上的意圖,以及他們分別表明的是什麼。那麼看這些不一樣的協議實現會很容易看懂。接下來我將開始對rpc模塊關於memcached協議部分進行講解。源碼分析