dubbo源碼解析(二十七)遠程調用——injvm本地調用

遠程調用——injvm本地調用

目標:介紹injvm本地調用的設計和實現,介紹dubbo-rpc-injvm的源碼。

前言

dubbo是一個遠程調用的框架,可是它沒有理由不支持本地調用,本文就要講解dubbo關於本地調用的實現。本地調用要比遠程調用簡單的多。java

源碼分析

(一)InjvmExporter

該類繼承了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);
    }

}

(二)InjvmInvoker

該類繼承了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方法。框架

(三)InjvmProtocol

該類是本地調用的協議實現,其中實現了服務調用和服務暴露方法,而且封裝了一個判斷是不是本地調用的方法。jvm

1.屬性

/**
 * 本地調用  Protocol的實現類key
 */
public static final String NAME = Constants.LOCAL_PROTOCOL;

/**
 * 默認端口
 */
public static final int DEFAULT_PORT = 0;
/**
 * 單例
 */
private static InjvmProtocol INSTANCE;

2.getExporter

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

3.export

@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // 建立InjvmExporter 而且返回
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}

4.refer

@Override
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    // 建立InjvmInvoker 而且返回
    return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}

5.isInjvmRefer

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協議部分進行講解。源碼分析

相關文章
相關標籤/搜索