org.apache.hadoop.fs.nfs.rpc.RpcClient.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.fs.nfs.rpc.RpcClient.java

Source

/**
 * Copyright 2014 NetApp Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.apache.hadoop.fs.nfs.rpc;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.oncrpc.RpcAcceptedReply;
import org.apache.hadoop.oncrpc.RpcCall;
import org.apache.hadoop.oncrpc.RpcMessage;
import org.apache.hadoop.oncrpc.RpcReply;
import org.apache.hadoop.oncrpc.XDR;
import org.apache.hadoop.oncrpc.security.Credentials;
import org.apache.hadoop.oncrpc.security.VerifierNone;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.timeout.IdleStateHandler;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.Timer;

public class RpcClient {

    final ClientBootstrap bootstrap;
    final Map<Integer, RpcNetworkTask> tasks;
    final Queue<RpcNetworkTask> pending;
    final AtomicBoolean errored;
    final AtomicBoolean shutdown;
    final AtomicInteger xid;
    final RpcClient client;

    ChannelFuture future;

    public static final int RECONNECT_DELAY_MS = 5;
    public static final int MAX_RETRIES = 10;
    public static final int MAX_RPCWAIT_MS = 10000;

    public static final Timer timer = new HashedWheelTimer();

    public static final Log LOG = LogFactory.getLog(RpcClient.class);

    public RpcClient(String hostname, int port) throws IOException {

        tasks = new ConcurrentHashMap<>();
        pending = new ConcurrentLinkedQueue<>();
        xid = new AtomicInteger(new Random(System.currentTimeMillis()).nextInt(1024) * 1000000);
        errored = new AtomicBoolean(false);
        shutdown = new AtomicBoolean(false);

        ChannelFactory factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool(), 1, 8);

        client = this;
        ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() {
                return Channels.pipeline(new RpcFrameDecoder(),
                        new IdleStateHandler(timer, 0, 1, 0, TimeUnit.MICROSECONDS),
                        new RpcClientHandler(client, bootstrap, timer));
            }
        };
        bootstrap = new ClientBootstrap(factory);
        bootstrap.setPipelineFactory(pipelineFactory);

        bootstrap.setOption("remoteAddress", new InetSocketAddress(hostname, port));
        bootstrap.setOption("tcpNoDelay", true);
        bootstrap.setOption("keepAlive", false);
        bootstrap.setOption("soLinger", 0);
        bootstrap.setOption("receiveBufferSize", 32 * 1024 * 1024);
        bootstrap.setOption("sendBufferSize", 32 * 1024 * 1024);

        future = bootstrap.connect();
        future.awaitUninterruptibly();
        if (future.isDone() && (future.isCancelled() || !future.isSuccess())) {
            throw new IOException("Could not connect to " + hostname + " on port " + port);
        }
    }

    public RpcMessage service(int program, int version, int procedure, XDR in, XDR out, Credentials credentials)
            throws RpcException {
        int callXid = xid.incrementAndGet();

        // Package call into a new task
        XDR request = new XDR();
        RpcCall call = RpcCall.getInstance(callXid, program, version, procedure, credentials, new VerifierNone());
        call.write(request);
        request.writeFixedOpaque(in.getBytes());
        ChannelBuffer buf = XDR.writeMessageTcp(request, true);
        RpcNetworkTask task = new RpcNetworkTask(callXid, buf);

        // Issue the task
        tasks.put(callXid, task);
        pending.add(task);
        sendToChannel();

        // Wait for task to complete
        boolean completed = false;
        for (int i = 0; i < MAX_RETRIES; ++i) {
            if (task.wait(MAX_RPCWAIT_MS)) {
                completed = true;
                break;
            } else {
                LOG.info("RPC: xid=" + callXid + " took too long, so retrying");
                task = new RpcNetworkTask(callXid, buf);
                tasks.put(callXid, task);
                pending.add(task);
                sendToChannel();
            }
        }

        if (!completed || task.getReply() == null) {
            LOG.error("RPC: xid=" + callXid + " timed out");
            throw new RpcException("RPC: xid=" + callXid + " timed out");
        }

        // Process reply and return
        RpcReply reply = task.getReply();
        if (reply.getState() == RpcReply.ReplyState.MSG_DENIED) {
            LOG.error("RPC: xid=" + callXid + " RpcReply request denied: " + reply);
            throw new RpcException("RPC: xid=" + callXid + " RpcReply request denied: " + reply);
        }

        // Call was accepted so process the correct reply
        RpcAcceptedReply acceptedReply = (RpcAcceptedReply) reply;
        LOG.debug("RPC: xid=" + callXid + " completed successfully with acceptstate="
                + acceptedReply.getAcceptState());
        out.writeFixedOpaque(task.getReplyData().getBytes());

        return acceptedReply;

    }

    public void shutdown() {
        long start = System.currentTimeMillis();
        try {
            shutdown.set(true);
            future.getChannel().close();
            future.getChannel().getCloseFuture().awaitUninterruptibly();
            bootstrap.shutdown();
        } finally {
            bootstrap.releaseExternalResources();
            LOG.debug("RpcClient shutdown took " + (System.currentTimeMillis() - start) + " ms");
        }
    }

    public boolean hasShutdown() {
        return shutdown.get();
    }

    protected synchronized void setChannel(ChannelFuture future) {
        this.future = future;
    }

    protected RpcNetworkTask getTask() {
        return pending.poll();
    }

    protected void completeTask(int xid, RpcReply reply, XDR replyData) {
        RpcNetworkTask found = tasks.remove(xid);
        if (found != null) {
            found.setReply(reply, replyData);
            found.signal();
        }
    }

    protected void sendToChannel() {
        try {
            RpcNetworkTask task = getTask();
            if (task != null) {
                future.getChannel().write(task.getCallData());
            }
        } catch (Exception ignore) {

        }
    }

}