1. 程式人生 > 程式設計 >Dubbo原始碼解析(三十一)遠端呼叫——rmi協議

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

遠端呼叫——rmi協議

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

前言

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

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

原始碼分析

(一)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();
}
複製程式碼

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

後記

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

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