dubbo源碼解析(三十一)遠程調用——rmi協議

遠程調用——rmi協議

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

前言

dubbo支持rmi協議,主要基於spring封裝的org.springframework.remoting.rmi包來實現,固然最原始仍是依賴 JDK 標準的java.rmi.*包,採用阻塞式短鏈接和 JDK 標準序列化方式。關於rmi協議的介紹能夠參考dubbo官方文檔。html

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

源碼分析

(一)RmiRemoteInvocation

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

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協議實現的核心,跟其餘協議同樣,也實現了本身的服務暴露和服務引用方法。git

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);
            }
        }
    };
}

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

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();
}

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

後記

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

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

相關文章
相關標籤/搜索