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. 将返回值发送到客户端。