1.客户端执行RPC.getProxy
Queryquery=(Query) RPC.getProxy(Query.class, MyServer.IPC_VER, addr, newConfiguration());
即调用方法:
publicstatic VersionedProtocol getProxy(
Class<<spanlang="ZH-CN">? extends VersionedProtocol>protocol,
long clientVersion,InetSocketAddress addr, Configuration conf)
会用反射调用以下方法:
VersionedProtocolproxy =
(VersionedProtocol) Proxy.newProxyInstance(
protocol.getClassLoader(),new Class[] { protocol },
new Invoker(protocol, addr,ticket, conf, factory, rpcTimeout));
再直接调用以下的InvocationHandler:
private staticclass Invoker implements InvocationHandler {
private Client.ConnectionId remoteId;
private Client client;
private boolean isClosed = false;
public Invoker(Class<?extends VersionedProtocol> protocol,
InetSocketAddress address, UserGroupInformation ticket,
Configuration conf, SocketFactory factory,
intrpcTimeout) throws IOException {
this.remoteId= Client.ConnectionId.getConnectionId(address,protocol,
ticket, rpcTimeout, conf);
this.client =CLIENTS.getClient(conf, factory);
}
}
privatesynchronized Client getClient(Configurationconf,
SocketFactory factory) {
// Construct & cacheclient. The configuration is only used fortimeout,
// and Clients haveconnection pools. So we can either (a) losesome
// connection pooling andleak sockets, or (b) use the same timeout for all
// configurations. Since the IPC is usually intended globally,not
// per-job, we choose(a).
Client client =clients.get(factory);
if (client == null){
client =new Client(ObjectWritable.class, conf, factory);
clients.put(factory, client);
} else {
client.incCount();
}
return client;
}
检查客户端版本,调用
longserverVersion =proxy.getProtocolVersion(protocol.getName(),
clientVersion);
这时候开始真正的Socket连接
publicObject invoke(Object proxy, Method method, Object[]args)
throws Throwable {
final boolean logDebug =LOG.isDebugEnabled();
long startTime =0;
if (logDebug) {
startTime= System.currentTimeMillis();
}
ObjectWritablevalue = (ObjectWritable)
client.call(new Invocation(method, args), remoteId);
if (logDebug) {
longcallTime = System.currentTimeMillis() - startTime;
LOG.debug("Call: " + method.getName() + " " + callTime);
}
returnvalue.get();
}
publicWritable call(Writableparam, ConnectionId remoteId)
throws InterruptedException,IOException {
Call call = new Call(param);
Connection connection = getConnection(remoteId,call);
connection.sendParam(call); // send theparameter
boolean interrupted = false;
synchronized (call) {
while (!call.done){
try{
call.wait(); // waitfor the result
} catch(InterruptedException ie) {
// save the fact that we wereinterrupted
interrupted = true;
}
}
if (interrupted) {
// set theinterrupt flag now that we are done waiting
Thread.currentThread().interrupt();
}
if (call.error != null){
if(call.error instanceof RemoteException) {
call.error.fillInStackTrace();
throw call.error;
} else {// local exception
// use the connection because it will reflect anip change, unlike
// the remoteId
throwwrapException(connection.getRemoteAddress(),call.error);
}
} else {
returncall.value;
}
}
}
private Connection getConnection(ConnectionIdremoteId,
Call call)
throws IOException,InterruptedException {
if (!running.get()) {
// the client isstopped
throw new IOException("Theclient is stopped");
}
Connection connection;
do {
synchronized (connections){
connection= connections.get(remoteId);
if(connection == null) {
connection = new Connection(remoteId);
connections.put(remoteId,connection);
}
}
} while (!connection.addCall(call));
//we don't invoke the method below inside"synchronized (connections)"
//block above. The reason for that is if theserver happens to be slow,
//it will take longer to establish a connectionand that will slow the
//entire system down.
connection.setupIOstreams();
return connection;
}
新建一个org.apache.hadoop.ipc.Client.Connection线程
private synchronized void setupIOstreams()throws InterruptedException {
if (socket != null ||shouldCloseConnection.get()) {
return;
}
try {
if(LOG.isDebugEnabled()) {
LOG.debug("Connecting to "+server);
}
shortnumRetries = 0;
finalshort maxRetries = 15;
Randomrand = null;
while(true) {
setupConnection();
InputStream inStream =NetUtils.getInputStream(socket);
OutputStream outStream =NetUtils.getOutputStream(socket);
writeRpcHeader(outStream);
if (useSasl) {
final InputStream in2 =inStream;
final OutputStream out2 =outStream;
UserGroupInformation ticket =remoteId.getTicket();
if (authMethod ==AuthMethod.KERBEROS) {
if(ticket.getRealUser() != null) {
ticket = ticket.getRealUser();
}
}
boolean continueSasl =false;
try{
continueSasl =
ticket.doAs(new PrivilegedExceptionAction(){
@Override
public Boolean run() throwsIOException {
returnsetupSaslConnection(in2, out2);
}
});
} catch (Exception ex){
if (rand== null) {
rand = new Random();
}
handleSaslConnectionFailure(numRetries++, maxRetries, ex,rand,
ticket);
continue;
}
if (continueSasl){
// Saslconnect is successful. Let's set up Sasl i/o streams.
inStream =saslRpcClient.getInputStream(inStream);
outStream= saslRpcClient.getOutputStream(outStream);
} else {
// fallback to simple auth because server told us so.
authMethod= AuthMethod.SIMPLE;
header =new ConnectionHeader(header.getProtocol(),
header.getUgi(),authMethod);
useSasl =false;
}
}
this.in = new DataInputStream(newBufferedInputStream
(newPingInputStream(inStream)));
this.out = new DataOutputStream
(newBufferedOutputStream(outStream));
writeHeader();
// update last activity time
touch();
//start the receiver thread after the socket connection has been setup
// 开启org.apache.hadoop.ipc.Client.Connection线程
start();
return;
}
} catch (IOException e){
markClosed(e);
close();
}
}
privatesynchronized void setupConnection()throws IOException {
short ioFailures =0;
short timeoutFailures =0;
while (true) {
try{
this.socket =socketFactory.createSocket();
this.socket.setTcpNoDelay(tcpNoDelay);
if (UserGroupInformation.isSecurityEnabled()){
KerberosInfo krbInfo=
remoteId.getProtocol().getAnnotation(KerberosInfo.class);
if (krbInfo != null&& krbInfo.clientPrincipal() != null) {
Stringhost =
SecurityUtil.getHostFromPrincipal(remoteId.getTicket().getUserName());
// If hostname is a valid local address then bind socket to it
InetAddress localAddr =NetUtils.getLocalInetAddress(host);
if(localAddr != null) {
this.socket.bind(newInetSocketAddress(localAddr, 0));
}
}
}
// connection time out is 20s
NetUtils.connect(this.socket,server, 20000);
if (rpcTimeout > 0) {
pingInterval = rpcTimeout; // rpcTimeout overwrites pingInterval
}
this.socket.setSoTimeout(pingInterval);
return;
} catch(SocketTimeoutException toe) {
if (updateAddress()) {
timeoutFailures = ioFailures= 0;
}
handleConnectionFailure(timeoutFailures++, 45,toe);
} catch(IOException ie) {
if (updateAddress()) {
timeoutFailures = ioFailures= 0;
}
handleConnectionFailure(ioFailures++,maxRetries, ie);
}
}
}
publicstatic void connect(Socketsocket,
SocketAddressendpoint,
int timeout) throwsIOException {
if (socket == null || endpoint == null ||timeout < 0) {
throw newIllegalArgumentException("Illegal argument forconnect()");
}
SocketChannel ch =socket.getChannel();
if (ch == null) {
// let the defaultimplementation handle it.
socket.connect(endpoint,timeout);
} else {
SocketIOWithTimeout.connect(ch, endpoint, timeout);
}
// There is a very rare case allowed by the TCPspecification, such that
// if we are trying to connect to an endpoint onthe local machine,
// and we end up choosing an ephemeral portequal to the destination port,
// we will actually end up getting connected toourself (ie any data we
// send just comes right back). This is onlypossible if the target
// daemon is down, so we'll treat it likeconnection refused.
if (socket.getLocalPort() == socket.getPort()&&
socket.getLocalAddress().equals(socket.getInetAddress())){
LOG.info("Detected a loopbackTCP socket, disconnecting it");
socket.close();
throw newConnectException(
"Localhosttargeted connection resulted in a loopback. " +
"No daemonis listening on the target port.");
}
}
org.apache.hadoop.ipc.Client.Connection线程
publicvoid run(){
if(LOG.isDebugEnabled())
LOG.debug(getName() + ": starting, having connections"
+connections.size());
while (waitForWork()) {//waithere for work - read or close connection
receiveResponse();
}
close();
if(LOG.isDebugEnabled())
LOG.debug(getName() + ": stopped, remaining connections"
+connections.size());
}
private void receiveResponse(){
if(shouldCloseConnection.get()) {
return;
}
touch();
try {
int id= in.readInt(); // try toread an id
if(LOG.isDebugEnabled())
LOG.debug(getName() + " got value #" +id);
Call call= calls.get(id);
int state= in.readInt(); // read call status
if (state== Status.SUCCESS.state) {
Writablevalue = ReflectionUtils.newInstance(valueClass,conf);
value.readFields(in); // readvalue
call.setValue(value);
calls.remove(id);
} else if(state == Status.ERROR.state) {
call.setException(newRemoteException(WritableUtils.readString(in),
WritableUtils.readString(in)));
calls.remove(id);
} else if(state == Status.FATAL.state) {
// Close the connection
markClosed(newRemoteException(WritableUtils.readString(in),
WritableUtils.readString(in)));
}
} catch (IOException e){
markClosed(e);
}
}
将结果放在call.value里面。
取得结果以后,线程org.apache.hadoop.ipc.Client.Connection运行完成,自动退出。
2.执行 query.getFileStatus("/tmp/testIPC");
实际上跟执行getProtocolVersion是一样的。
3.执行RPC.stopProxy(query);
privatevoid stopClient(Clientclient) {
synchronized (this){
client.decCount();
if(client.isZeroReference()) {
clients.remove(client.getSocketFactory());
}
}
if (client.isZeroReference()){
client.stop();
}
}
}
public void stop(){
if (LOG.isDebugEnabled()) {
LOG.debug("Stoppingclient");
}
if (!running.compareAndSet(true, false)){
return;
}
// wake up all connections
synchronized (connections) {
for (Connection conn :connections.values()) {
conn.interrupt();
}
}
// wait until all connections areclosed
while (!connections.isEmpty()) {
try {
Thread.sleep(100);
} catch (InterruptedExceptione) {
}
}
}
4.执行server.stop()
public synchronized void stop() {
LOG.info("Stopping server on " +port);
running = false;
if (handlers != null) {
for (int i = 0; i <handlerCount; i++) {
if(handlers[i] != null) {
handlers[i].interrupt();
}
}
}
listener.interrupt();
listener.doStop();
responder.interrupt();
notifyAll();
if (this.rpcMetrics != null) {
this.rpcMetrics.shutdown();
}
}
synchronized void doStop(){
if (selector != null){
selector.wakeup();
Thread.yield();
}
if (acceptChannel != null){
try{
acceptChannel.socket().close();
} catch(IOException e) {
LOG.info(getName() + ":Exception in closinglistener socket. " + e);
}
}
readPool.shutdown();
}