hadoop作为分布式的系统, 集群机器之间的通信是最基本,最常见的需求。
这种需求本质上是IPC, 即进程间通信。 按照传统的UINX编程模型,进程间通信无非是如下的几种方式:
管道, FIFO, 消息队列, 信号量, 共享存储, 套接字。只有套接字是可以跨机器的网络通信, 能满足hadoop的需求。
通常情况下, 网络通信的程序使用显式网络编程(即直接使用java.net包)。比如Web浏览器, Web服务器等。
但也有另一部分程序使用隐式网络编程, 比如利用hadoop RPC这种封装了底层通信细节的工具包。
这样做使得底层的网络通信对于程序员透明。一则减轻了程序员的负担, 二则抽象了功能模块, 使得模块之间职责更清晰, 便于维护。
首先展示一个hadoop RPC功能demo, 了解hadoop RPC的用法。
step 1: 在pom.xml文件中添加依赖项
1
2
3
4
5
|
<
dependency
>
<
groupId
>org.apache.hadoop</
groupId
>
<
artifactId
>hadoop-common</
artifactId
>
<
version
>2.6.0</
version
>
</
dependency
>
|
step 2: 在src/main/resources中添加log4j.properties
1
2
3
4
5
6
7
8
9
|
log4j.rootLogger=DEBUG,console
log4j.additivity.org.apache=true
# (console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=INFO
log4j.appender.console.ImmediateFlush=true
log4j.appender.console.Target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
|
step 3: 定义RPC协议
1
2
3
4
5
6
7
8
9
10
|
package
hadooprpc.demo;
import
java.io.IOException;
interface
ClientProtocol
extends
org.apache.hadoop.ipc.VersionedProtocol {
// 版本号,默认情况下,不同版本号的 RPC Client 和 Server 之间不能相互通信
public
static
final
long
versionID = 1L;
String echo(String value)
throws
IOException;
int
add(
int
v1,
int
v2)
throws
IOException;
}
|
step 4: 实现RPC协议
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
|
package
hadooprpc.demo;
import
java.io.IOException;
import
org.apache.hadoop.ipc.ProtocolSignature;
public
class
ClientProtocolImpl
implements
ClientProtocol {
// 重载的方法,用于获取自定义的协议版本号,
public
long
getProtocolVersion(String protocol,
long
clientVersion) {
return
ClientProtocol.versionID;
}
// 重载的方法,用于获取协议签名
public
ProtocolSignature getProtocolSignature(String protocol,
long
clientVersion,
int
hashcode) {
return
new
ProtocolSignature(ClientProtocol.versionID,
null
);
}
public
String echo(String value)
throws
IOException {
return
value;
}
public
int
add(
int
v1,
int
v2)
throws
IOException {
return
v1 + v2;
}
}
|
step 5; 构造并启动 RPC Server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package
hadooprpc.demo;
import
java.io.IOException;
import
org.apache.hadoop.HadoopIllegalArgumentException;
import
org.apache.hadoop.conf.Configuration;
import
org.apache.hadoop.ipc.RPC;
import
org.apache.hadoop.ipc.Server;
public
class
RPCServer {
public
static
void
main(String[] args)
throws
HadoopIllegalArgumentException, IOException {
Configuration conf =
new
Configuration();
Server server =
new
RPC.Builder(conf).setProtocol(ClientProtocol.
class
)
.setInstance(
new
ClientProtocolImpl()).setBindAddress(
"localhost"
).setPort(
8097
)
.setNumHandlers(
5
).build();
server.start();
}
}
|
step 6: 构造 RPC Client 并发送 RPC 请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package
hadooprpc.demo;
import
java.io.IOException;
import
java.net.InetSocketAddress;
import
org.apache.hadoop.conf.Configuration;
import
org.apache.hadoop.ipc.RPC;
public
class
RPCClient {
public
static
void
main(String[] args)
throws
IOException {
ClientProtocol client = (ClientProtocol)RPC.getProxy(ClientProtocol.
class
, ClientProtocol.versionID,
new
InetSocketAddress(
8097
),
new
Configuration());
int
result = client.add(
5
,
6
);
System.out.println(result);
String echoResult = client.echo(
"result"
);
System.out.println(echoResult);
RPC.stopProxy(client); -- 关闭连接
}
}
|
step 7: 启动RPC Server, 然后执行 RPC Client.
通过上面的例子可以发现, 通过这种编程方式,不用考虑网络层的细节,只需要编写接口和接口实现即可。
hadoop RPC的实现原理很简单。
Client:
1. 通过动态代理,获取到调用接口的方法,参数类型。
2. 将调用信息编码,发送到服务器
3. 获取服务器的返回值, 并解码。
4. 返回调用方法的返回值。
Server:
1. 启动服务器, 并监听客户端。
2. 获取客户端发送过来的调用方法, 参数。
3. 执行实现类中相关的方法。
4. 将返回值发送到客户端。