Dubbo原始碼解析(三十一)遠端呼叫——rmi協議
阿新 • • 發佈:2020-06-24
遠端呼叫——rmi協議
目標:介紹rmi協議的設計和實現,介紹dubbo-rpc-rmi的原始碼。
前言
dubbo支援rmi協議,主要基於spring封裝的org.springframework.remoting.rmi包來實現,當然最原始還是依賴 JDK 標準的java.rmi.*包,採用阻塞式短連線和 JDK 標準序列化方式。關於rmi協議的介紹可以參考dubbo官方檔案。
原始碼分析
(一)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協議部分進行講解。