服务调用(七)之远程调用(rmi)
服务调用(七)之远程调用(rmi)
本文基于 Dubbo 2.6.1 版本,望知悉。
1. 概述
本文,我们分享 rmi:// 协议的远程调用,主要分成三个部分:
- 服务暴露
- 服务引用
- 服务调用
对应项目为 dubbo-rpc-rmi 。
对应文档为 《Dubbo 用户指南 —— rmi://》 。定义如下:
RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式。
本文涉及类图(红圈部分)如下:
旁白君:整体实现和 dubbo-rpc-http 一致,所以内容上和 《精尽 Dubbo 源码分析 —— 服务调用(三)之远程调用(HTTP)》 差不多。
2. RmiRemoteInvocation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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);
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
* from {@link com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler}
*
* 服务端执行时,重新放入上下文(虽然这时上下文在ContextFilter执行时将被Invocation的attachments覆盖,我们在Invocation构造时还原attachments, see 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);
}
}
}
3. RmiProtocol
com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol ,实现 AbstractProxyProtocol 抽象类,rmi:// 协议实现类。
3.1 构造方法
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 默认端口
*/
public static final int DEFAULT_PORT = 1099;
public RmiProtocol() {
super(RemoteAccessException.class, RemoteException.class);
}
public int getDefaultPort() {
return DEFAULT_PORT;
}
rpcExceptions = RemoteAccessException.class, RemoteException.class。- 艿艿对 RMI了解不多,所以本文更多梳理好整体脉络。
3.2 doExport
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
// 创建 RmiServiceExporter 对象
final RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
rmiServiceExporter.setRegistryPort(url.getPort());
rmiServiceExporter.setServiceName(url.getPath());
rmiServiceExporter.setServiceInterface(type);
rmiServiceExporter.setService(impl);
try {
rmiServiceExporter.afterPropertiesSet();
} catch (RemoteException e) {
throw new RpcException(e.getMessage(), e);
}
// 返回取消暴露的回调 Runnable
return new Runnable() {
public void run() {
try {
rmiServiceExporter.destroy();
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
}
};
}
- 第 3 至 13 行:创建 RmiServiceExporter 对象。
- 第 14 至 23 行:返回取消暴露的回调 Runnable。
3.3 doRefer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
@SuppressWarnings("unchecked")
protected <T> T doRefer(final Class<T> serviceType, final URL url) throws RpcException {
// 创建 RmiProxyFactoryBean 对象
final RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
// RMI needs extra parameter since it uses customized remote invocation object
// RMI传输时使用自定义的远程执行对象,从而传递额外的参数
if (url.getParameter(Constants.DUBBO_VERSION_KEY, Version.getVersion()).equals(Version.getVersion())) {
// Check dubbo version on provider, this feature only support
rmiProxyFactoryBean.setRemoteInvocationFactory(new RemoteInvocationFactory() {
public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
return new RmiRemoteInvocation(methodInvocation);
}
});
}
// 设置相关参数
rmiProxyFactoryBean.setServiceUrl(url.toIdentityString());
rmiProxyFactoryBean.setServiceInterface(serviceType);
rmiProxyFactoryBean.setCacheStub(true);
rmiProxyFactoryBean.setLookupStubOnStartup(true);
rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
rmiProxyFactoryBean.afterPropertiesSet();
// 创建 Service Proxy 对象
return (T) rmiProxyFactoryBean.getObject();
}
- 第 5 行:创建 RmiProxyFactoryBean 对象。
- 第 8 至 15 行:若远程服务是 Dubbo RMI 服务时,RMI 传输时使用自定义的远程执行对象,从而传递额外的参数。
- 第 16 至 22 行:设置相关参数。另外,dubbo 配置中的超时时间对 RMI 无效,需使用 java 启动参数设置
-Dsun.rmi.transport.tcp.responseTimeout=3000,参见下面的 RMI 配置 - 第 24 行:创建 Service Proxy 对象。
3.3.1 getErrorCode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected int getErrorCode(Throwable e) {
if (e instanceof RemoteAccessException) {
e = e.getCause();
}
if (e != null && e.getCause() != null) {
Class<?> cls = e.getCause().getClass();
if (SocketTimeoutException.class.equals(cls)) {
return RpcException.TIMEOUT_EXCEPTION;
} else if (IOException.class.isAssignableFrom(cls)) {
return RpcException.NETWORK_EXCEPTION;
} else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
return RpcException.SERIALIZATION_EXCEPTION;
}
}
return super.getErrorCode(e);
}
- 将异常,翻译成 Dubbo 异常码。
666. 彩蛋
水水的一篇更新,嘿嘿嘿。
本文由作者按照 CC BY 4.0 进行授权

