dubbo原始碼解析(三十一)遠端呼叫——rmi協議

CrazyHzm發表於2019-01-19

遠端呼叫——rmi協議

目標:介紹rmi協議的設計和實現,介紹dubbo-rpc-rmi的原始碼。

前言

dubbo支援rmi協議,主要基於spring封裝的org.springframework.remoting.rmi包來實現,當然最原始還是依賴 JDK 標準的java.rmi.*包,採用阻塞式短連線和 JDK 標準序列化方式。關於rmi協議的介紹可以參考dubbo官方文件。

地址:http://dubbo.apache.org/zh-cn…

原始碼分析

(一)RmiRemoteInvocation

該類繼承了RemoteInvocation,主要是在RemoteInvocation的基礎上新增dubbo自身所需的附加值,避免這些附加值沒有被傳遞,為了做一些驗證處理。

public class RmiRemoteInvocation extends RemoteInvocation {
    private static final long serialVersionUID = 1L;
    private static final String dubboAttachmentsAttrName = "dubbo.attachments";

    /**
     * executed on consumer side
     */
    public RmiRemoteInvocation(MethodInvocation methodInvocation) {
        super(methodInvocation);
        // 新增dubbo附加值的屬性
        addAttribute(dubboAttachmentsAttrName, new HashMap<String, String>(RpcContext.getContext().getAttachments()));
    }

    /**
     * Need to restore context on provider side (Though context will be overridden by Invocation`s attachment
     * when ContextFilter gets executed, we will restore the attachment when Invocation is constructed, check more
     * 需要在提供者端恢復上下文(儘管上下文將被Invocation的附件覆蓋
     * 當ContextFilter執行時,我們將在構造Invocation時恢復附件,檢查更多
     * from {@link com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler}
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object invoke(Object targetObject) throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException {
        // 獲得上下文
        RpcContext context = RpcContext.getContext();
        // 設定引數
        context.setAttachments((Map<String, String>) getAttribute(dubboAttachmentsAttrName));
        try {
            return super.invoke(targetObject);
        } finally {
            // 清空引數
            context.setAttachments(null);
        }
    }
}

(二)RmiProtocol

該類繼承了AbstractProxyProtocol類,是rmi協議實現的核心,跟其他協議一樣,也實現了自己的服務暴露和服務引用方法。

1.doExport

@Override
protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
    // rmi暴露者
    final RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
    // 設定埠
    rmiServiceExporter.setRegistryPort(url.getPort());
    // 設定服務名稱
    rmiServiceExporter.setServiceName(url.getPath());
    // 設定介面
    rmiServiceExporter.setServiceInterface(type);
    // 設定服務實現
    rmiServiceExporter.setService(impl);
    try {
        // 初始化bean的時候執行
        rmiServiceExporter.afterPropertiesSet();
    } catch (RemoteException e) {
        throw new RpcException(e.getMessage(), e);
    }
    return new Runnable() {
        @Override
        public void run() {
            try {
                // 銷燬
                rmiServiceExporter.destroy();
            } catch (Throwable e) {
                logger.warn(e.getMessage(), e);
            }
        }
    };
}

該方法是服務暴露的邏輯實現。

2.doRefer

@Override
@SuppressWarnings("unchecked")
protected <T> T doRefer(final Class<T> serviceType, final URL url) throws RpcException {
    // FactoryBean對於RMI代理,支援傳統的RMI服務和RMI呼叫者,建立RmiProxyFactoryBean物件
    final RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
    // RMI needs extra parameter since it uses customized remote invocation object
    // 檢測版本
    if (url.getParameter(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion()).equals(Version.getProtocolVersion())) {
        // Check dubbo version on provider, this feature only support
        // 設定RemoteInvocationFactory以用於此訪問器
        rmiProxyFactoryBean.setRemoteInvocationFactory(new RemoteInvocationFactory() {
            @Override
            public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
                // 自定義呼叫工廠可以向呼叫新增更多上下文資訊
                return new RmiRemoteInvocation(methodInvocation);
            }
        });
    }
    // 設定此遠端訪問者的目標服務的URL。URL必須與特定遠端處理提供程式的規則相容。
    rmiProxyFactoryBean.setServiceUrl(url.toIdentityString());
    // 設定要訪問的服務的介面。介面必須適合特定的服務和遠端處理策略
    rmiProxyFactoryBean.setServiceInterface(serviceType);
    // 設定是否在找到RMI存根後快取它
    rmiProxyFactoryBean.setCacheStub(true);
    // 設定是否在啟動時查詢RMI存根
    rmiProxyFactoryBean.setLookupStubOnStartup(true);
    // 設定是否在連線失敗時重新整理RMI存根
    rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
    // // 初始化bean的時候執行
    rmiProxyFactoryBean.afterPropertiesSet();
    return (T) rmiProxyFactoryBean.getObject();
}

該方法是服務引用的邏輯實現。

後記

該部分相關的原始碼解析地址:https://github.com/CrazyHZM/i…

該文章講解了遠端呼叫中關於rmi協議實現的部分,邏輯比較簡單。接下來我將開始對rpc模組關於thrift協議部分進行講解。

相關文章