Hadoop RPC学习笔记
首先RpcEngine。这个接口中了Server端的getServer和Call方法。Client端的getProxy和stopProxy的方法。
从Server端看起
RPC.Server getServer(Class<?> protocol, Object instance, String bindAddress,
int port, int numHandlers, int numReaders,
int queueSizePerHandler, boolean verbose,
Configuration conf,
SecretManager<? extends TokenIdentifier> secretManager
) throws IOException;
这个接口这个参数的函数可以从一个调用的例子来看
this.serviceRpcServer = RPC.getServer(NamenodeProtocols.class, this,
dnSocketAddr.getHostName(), dnSocketAddr.getPort(), serviceHandlerCount,
false, conf, namesystem.getDelegationTokenSecretManager());
this.serviceRPCAddress = this.serviceRpcServer.getListenerAddress();
nn.setRpcServiceServerAddress(conf, serviceRPCAddress);
这是Namenode初始化时的创建RPC Server实例的调用。实际调用的实现,是每个Protocol对应的RpcEngine的实例的实现。RpcEngine的实例怎么来的呢?是根据配置的类名(如rpc.engine.NamenodeProtocols)反射得到的。默认RpcEngine是WritableRpcEngine(hadoop还提供了一种AvroRpcEngine,跳过没看)。WritableRpcEngine的getServer没有特别,一直追溯到顶层的Server,即ipc包中的抽象Server类。到这个类中,就可以窥视到所有Server的重要元素(静态内部类),如Call,Listener,Responder,Connection,Handler。不表先,回到Server的构造函数中来。
前面无非是设置一些Server属性,地址端口,各种队列长度,read线程数等等(paramClass这个单独说)。重点是这里。
listener = new Listener();
this.port = listener.getAddress().getPort();
this.rpcMetrics = RpcMetrics.create(this);
this.rpcDetailedMetrics = RpcDetailedMetrics.create(this.port);
this.tcpNoDelay = conf.getBoolean("ipc.server.tcpnodelay", false);
然后是一个Responder的实例。
responder = new Responder();
一个Server实例就这样初始化完成了。在各种Server端的Init中就会调用他start以启动服务。start分别start responder,listener,handler。分别看。
Listener的Run循环select,对于每个select到的key,DoAccept。DoAccept主要逻辑代码如下:
Reader reader = getReader();//轮流唤醒reader线程
reader.startAdd();
SelectionKey readKey = reader.registerChannel(channel);//将reader的selector注册到channel中
c = new Connection(readKey, channel, System.currentTimeMillis());
readKey.attach(c);
synchronized (connectionList) {
connectionList.add(numConnections, c);
numConnections++;
}
reader线程唤醒后,根据attach再seletkey上得connection提供的readAndProcess方法,读取到数据后,根据server注册的paramClass类,生成方法和参数对象,根据这些对象实例化一个Call实例,放入Call队列中。
Call类不复杂,就是维护一个调用的属性。参数,连接对象,结果数据和处理时长相关的信息。
private int id; // the client's call id
private Writable param; // the parameter passed
private Connection connection; // connection to client
private long timestamp; // the time received when response is null
// the time served when response is not null
private ByteBuffer response; // the response for this call
Responder在Server实例化时实例化,在Server Start时启动,是一个daemon线程。暴露的外部方法。
void doRespond(Call call) throws IOException {
synchronized (call.connection.responseQueue) {
call.connection.responseQueue.addLast(call);
if (call.connection.responseQueue.size() == 1) {
processResponse(call.connection.responseQueue, true);
}
}
}
大致就是这样。Server的几个重要组成部分就是Listener,Handler,Responder,Connection和Call。主要涉及到的知识点有两部分:Java NIO和反射。