alluxio.master.AlluxioMaster.java Source code

Java tutorial

Introduction

Here is the source code for alluxio.master.AlluxioMaster.java

Source

/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.master;

import alluxio.AlluxioURI;
import alluxio.Configuration;
import alluxio.Constants;
import alluxio.PropertyKey;
import alluxio.RuntimeConstants;
import alluxio.Server;
import alluxio.master.block.BlockMaster;
import alluxio.master.file.FileSystemMaster;
import alluxio.master.journal.ReadWriteJournal;
import alluxio.master.lineage.LineageMaster;
import alluxio.metrics.MetricsSystem;
import alluxio.metrics.sink.MetricsServlet;
import alluxio.security.authentication.TransportProvider;
import alluxio.underfs.UnderFileSystem;
import alluxio.util.CommonUtils;
import alluxio.util.ConfigurationUtils;
import alluxio.util.LineageUtils;
import alluxio.util.network.NetworkAddressUtils;
import alluxio.util.network.NetworkAddressUtils.ServiceType;
import alluxio.web.MasterUIWebServer;
import alluxio.web.UIWebServer;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import org.apache.thrift.TMultiplexedProcessor;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;

import javax.annotation.concurrent.NotThreadSafe;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Entry point for the Alluxio master program.
 */
@NotThreadSafe
public class AlluxioMaster implements Server {
    private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER_TYPE);

    /**
     * Starts the Alluxio master.
     *
     * @param args command line arguments, should be empty
     */
    public static void main(String[] args) {
        if (args.length != 0) {
            LOG.info("java -cp {} {}", RuntimeConstants.ALLUXIO_JAR, AlluxioMaster.class.getCanonicalName());
            System.exit(-1);
        }

        // validate the configuration
        if (!ConfigurationUtils.validateConf()) {
            LOG.error("Invalid configuration found");
            System.exit(-1);
        }

        AlluxioMaster master = Factory.create();
        try {
            master.start();
        } catch (Exception e) {
            LOG.error("Uncaught exception while running Alluxio master, stopping it and exiting.", e);
            try {
                master.stop();
            } catch (Exception e2) {
                // continue to exit
                LOG.error("Uncaught exception while stopping Alluxio master, simply exiting.", e2);
            }
            System.exit(-1);
        }
    }

    /** Maximum number of threads to serve the rpc server. */
    private final int mMaxWorkerThreads;

    /** Minimum number of threads to serve the rpc server. */
    private final int mMinWorkerThreads;

    /** The port for the RPC server. */
    private final int mPort;

    /** The socket for thrift rpc server. */
    private final TServerSocket mTServerSocket;

    /** The transport provider to create thrift server transport. */
    private final TransportProvider mTransportProvider;

    /** The address for the rpc server. */
    private final InetSocketAddress mMasterAddress;

    private final MetricsServlet mMetricsServlet = new MetricsServlet(MetricsSystem.METRIC_REGISTRY);

    /** The master managing all block metadata. */
    protected BlockMaster mBlockMaster;

    /** The master managing all file system related metadata. */
    protected FileSystemMaster mFileSystemMaster;

    /** The master managing all lineage related metadata. */
    protected LineageMaster mLineageMaster;

    /** A list of extra masters to launch based on service loader. */
    protected List<Master> mAdditionalMasters;

    /** The journal for the block master. */
    protected final ReadWriteJournal mBlockMasterJournal;

    /** The journal for the file system master. */
    protected final ReadWriteJournal mFileSystemMasterJournal;

    /** The journal for the lineage master. */
    protected final ReadWriteJournal mLineageMasterJournal;

    /** The web ui server. */
    private UIWebServer mWebServer = null;

    /** The RPC server. */
    private TServer mMasterServiceServer = null;

    /** is true if the master is serving the RPC server. */
    private boolean mIsServing = false;

    /** The start time for when the master started serving the RPC server. */
    private long mStartTimeMs = -1;

    /** The master services' names. */
    private static List<String> sServiceNames;

    /** The master service loaders. */
    private static ServiceLoader<MasterFactory> sServiceLoader;

    /**
     * @return the (cached) master service loader
     */
    private static ServiceLoader<MasterFactory> getServiceLoader() {
        if (sServiceLoader != null) {
            return sServiceLoader;
        }
        // Discover and register the available factories.
        // NOTE: ClassLoader is explicitly specified so we don't need to set ContextClassLoader.
        sServiceLoader = ServiceLoader.load(MasterFactory.class, MasterFactory.class.getClassLoader());
        return sServiceLoader;
    }

    /**
     * @return the (cached) list of the enabled master services' names
     */
    public static List<String> getServiceNames() {
        if (sServiceNames != null) {
            return sServiceNames;
        }
        sServiceNames = new ArrayList<>();
        sServiceNames.add(Constants.BLOCK_MASTER_NAME);
        sServiceNames.add(Constants.FILE_SYSTEM_MASTER_NAME);
        sServiceNames.add(Constants.LINEAGE_MASTER_NAME);

        for (MasterFactory factory : getServiceLoader()) {
            if (factory.isEnabled()) {
                sServiceNames.add(factory.getName());
            }
        }

        return sServiceNames;
    }

    /**
     * Factory for creating {@link AlluxioMaster} or {@link FaultTolerantAlluxioMaster} based on
     * {@link Configuration}.
     */
    @ThreadSafe
    public static final class Factory {
        /**
         * @return {@link FaultTolerantAlluxioMaster} if Alluxio configuration is set to use zookeeper,
         *         otherwise, return {@link AlluxioMaster}.
         */
        public static AlluxioMaster create() {
            if (Configuration.getBoolean(PropertyKey.ZOOKEEPER_ENABLED)) {
                return new FaultTolerantAlluxioMaster();
            }
            return new AlluxioMaster();
        }

        private Factory() {
        } // prevent instantiation.
    }

    protected AlluxioMaster() {
        mMinWorkerThreads = Configuration.getInt(PropertyKey.MASTER_WORKER_THREADS_MIN);
        mMaxWorkerThreads = Configuration.getInt(PropertyKey.MASTER_WORKER_THREADS_MAX);

        Preconditions.checkArgument(mMaxWorkerThreads >= mMinWorkerThreads, PropertyKey.MASTER_WORKER_THREADS_MAX
                + " can not be less than " + PropertyKey.MASTER_WORKER_THREADS_MIN);

        try {
            // Extract the port from the generated socket.
            // When running tests, it is fine to use port '0' so the system will figure out what port to
            // use (any random free port).
            // In a production or any real deployment setup, port '0' should not be used as it will make
            // deployment more complicated.
            if (!Configuration.getBoolean(PropertyKey.TEST_MODE)) {
                Preconditions.checkState(Configuration.getInt(PropertyKey.MASTER_RPC_PORT) > 0,
                        "Alluxio master rpc port is only allowed to be zero in test mode.");
                Preconditions.checkState(Configuration.getInt(PropertyKey.MASTER_WEB_PORT) > 0,
                        "Alluxio master web port is only allowed to be zero in test mode.");
            }
            mTransportProvider = TransportProvider.Factory.create();
            mTServerSocket = new TServerSocket(NetworkAddressUtils.getBindAddress(ServiceType.MASTER_RPC));
            mPort = NetworkAddressUtils.getThriftPort(mTServerSocket);
            // reset master rpc port
            Configuration.set(PropertyKey.MASTER_RPC_PORT, Integer.toString(mPort));
            mMasterAddress = NetworkAddressUtils.getConnectAddress(ServiceType.MASTER_RPC);

            // Check the journal directory
            String journalDirectory = Configuration.get(PropertyKey.MASTER_JOURNAL_FOLDER);
            if (!journalDirectory.endsWith(AlluxioURI.SEPARATOR)) {
                journalDirectory += AlluxioURI.SEPARATOR;
            }
            Preconditions.checkState(isJournalFormatted(journalDirectory),
                    "Alluxio master was not formatted! The journal folder is " + journalDirectory);

            // Create the journals.
            mBlockMasterJournal = new ReadWriteJournal(BlockMaster.getJournalDirectory(journalDirectory));
            mFileSystemMasterJournal = new ReadWriteJournal(FileSystemMaster.getJournalDirectory(journalDirectory));
            mLineageMasterJournal = new ReadWriteJournal(LineageMaster.getJournalDirectory(journalDirectory));

            mBlockMaster = new BlockMaster(mBlockMasterJournal);
            mFileSystemMaster = new FileSystemMaster(mBlockMaster, mFileSystemMasterJournal);
            if (LineageUtils.isLineageEnabled()) {
                mLineageMaster = new LineageMaster(mFileSystemMaster, mLineageMasterJournal);
            }

            mAdditionalMasters = new ArrayList<>();
            List<? extends Master> masters = Lists.newArrayList(mBlockMaster, mFileSystemMaster);
            for (MasterFactory factory : getServiceLoader()) {
                Master master = factory.create(masters, journalDirectory);
                if (master != null) {
                    mAdditionalMasters.add(master);
                }
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            throw Throwables.propagate(e);
        }
    }

    /**
     * @return the externally resolvable address of this master
     */
    public InetSocketAddress getMasterAddress() {
        return mMasterAddress;
    }

    /**
     * @return the actual bind hostname on RPC service (used by unit test only)
     */
    public String getRPCBindHost() {
        return NetworkAddressUtils.getThriftSocket(mTServerSocket).getInetAddress().getHostAddress();
    }

    /**
     * @return the actual port that the RPC service is listening on (used by unit test only)
     */
    public int getRPCLocalPort() {
        return mPort;
    }

    /**
     * @return the actual bind hostname on web service (used by unit test only)
     */
    public String getWebBindHost() {
        if (mWebServer != null) {
            return mWebServer.getBindHost();
        }
        return "";
    }

    /**
     * @return the actual port that the web service is listening on (used by unit test only)
     */
    public int getWebLocalPort() {
        if (mWebServer != null) {
            return mWebServer.getLocalPort();
        }
        return -1;
    }

    /**
     * @return internal {@link BlockMaster}
     */
    public BlockMaster getBlockMaster() {
        return mBlockMaster;
    }

    /**
     * @return other additional {@link Master}s
     */
    public List<Master> getAdditionalMasters() {
        return Collections.unmodifiableList(mAdditionalMasters);
    }

    /**
     * @return internal {@link FileSystemMaster}
     */
    public FileSystemMaster getFileSystemMaster() {
        return mFileSystemMaster;
    }

    /**
     * @return internal {@link LineageMaster}
     */
    public LineageMaster getLineageMaster() {
        return mLineageMaster;
    }

    /**
     * @return the start time of the master in milliseconds
     */
    public long getStartTimeMs() {
        return mStartTimeMs;
    }

    /**
     * @return the uptime of the master in milliseconds
     */
    public long getUptimeMs() {
        return System.currentTimeMillis() - mStartTimeMs;
    }

    /**
     * @return true if the system is the leader (serving the rpc server), false otherwise
     */
    boolean isServing() {
        return mIsServing;
    }

    @Override
    public void start() throws Exception {
        startMasters(true);
        startServing();
    }

    @Override
    public void stop() throws Exception {
        if (mIsServing) {
            LOG.info("Stopping RPC server on Alluxio master @ {}", mMasterAddress);
            stopServing();
            stopMasters();
            mTServerSocket.close();
            mIsServing = false;
        } else {
            LOG.info("Stopping Alluxio master @ {}", mMasterAddress);
        }
    }

    protected void startMasters(boolean isLeader) {
        try {
            connectToUFS();

            mBlockMaster.start(isLeader);
            mFileSystemMaster.start(isLeader);
            if (LineageUtils.isLineageEnabled()) {
                mLineageMaster.start(isLeader);
            }
            // start additional masters
            for (Master master : mAdditionalMasters) {
                master.start(isLeader);
            }

        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
            throw Throwables.propagate(e);
        }
    }

    protected void stopMasters() {
        try {
            if (LineageUtils.isLineageEnabled()) {
                mLineageMaster.stop();
            }
            // stop additional masters
            for (Master master : mAdditionalMasters) {
                master.stop();
            }
            mBlockMaster.stop();
            mFileSystemMaster.stop();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
            throw Throwables.propagate(e);
        }
    }

    private void startServing() {
        startServing("", "");
    }

    protected void startServing(String startMessage, String stopMessage) {
        MetricsSystem.startSinks();
        startServingWebServer();
        LOG.info("Alluxio master version {} started @ {} {}", RuntimeConstants.VERSION, mMasterAddress,
                startMessage);
        startServingRPCServer();
        LOG.info("Alluxio master version {} ended @ {} {}", RuntimeConstants.VERSION, mMasterAddress, stopMessage);
    }

    protected void startServingWebServer() {
        mWebServer = new MasterUIWebServer(ServiceType.MASTER_WEB.getServiceName(),
                NetworkAddressUtils.getBindAddress(ServiceType.MASTER_WEB), this);
        // reset master web port
        Configuration.set(PropertyKey.MASTER_WEB_PORT, Integer.toString(mWebServer.getLocalPort()));
        // Add the metrics servlet to the web server.
        mWebServer.addHandler(mMetricsServlet.getHandler());
        // start web ui
        mWebServer.startWebServer();
    }

    private void registerServices(TMultiplexedProcessor processor, Map<String, TProcessor> services) {
        for (Map.Entry<String, TProcessor> service : services.entrySet()) {
            processor.registerProcessor(service.getKey(), service.getValue());
        }
    }

    protected void startServingRPCServer() {
        // set up multiplexed thrift processors
        TMultiplexedProcessor processor = new TMultiplexedProcessor();
        registerServices(processor, mBlockMaster.getServices());
        registerServices(processor, mFileSystemMaster.getServices());
        if (LineageUtils.isLineageEnabled()) {
            registerServices(processor, mLineageMaster.getServices());
        }
        // register additional masters for RPC service
        for (Master master : mAdditionalMasters) {
            registerServices(processor, master.getServices());
        }

        // Return a TTransportFactory based on the authentication type
        TTransportFactory transportFactory;
        try {
            transportFactory = mTransportProvider.getServerTransportFactory();
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }

        // create master thrift service with the multiplexed processor.
        Args args = new TThreadPoolServer.Args(mTServerSocket).maxWorkerThreads(mMaxWorkerThreads)
                .minWorkerThreads(mMinWorkerThreads).processor(processor).transportFactory(transportFactory)
                .protocolFactory(new TBinaryProtocol.Factory(true, true));
        if (Configuration.getBoolean(PropertyKey.TEST_MODE)) {
            args.stopTimeoutVal = 0;
        } else {
            args.stopTimeoutVal = Constants.THRIFT_STOP_TIMEOUT_SECONDS;
        }
        mMasterServiceServer = new TThreadPoolServer(args);

        // start thrift rpc server
        mIsServing = true;
        mStartTimeMs = System.currentTimeMillis();
        mMasterServiceServer.serve();
    }

    protected void stopServing() throws Exception {
        if (mMasterServiceServer != null) {
            mMasterServiceServer.stop();
            mMasterServiceServer = null;
        }
        if (mWebServer != null) {
            mWebServer.shutdownWebServer();
            mWebServer = null;
        }
        MetricsSystem.stopSinks();
        mIsServing = false;
    }

    /**
     * Checks to see if the journal directory is formatted.
     *
     * @param journalDirectory the journal directory to check
     * @return true if the journal directory was formatted previously, false otherwise
     * @throws IOException if an I/O error occurs
     */
    private boolean isJournalFormatted(String journalDirectory) throws IOException {
        UnderFileSystem ufs = UnderFileSystem.get(journalDirectory);
        if (!ufs.providesStorage()) {
            // TODO(gene): Should the journal really be allowed on a ufs without storage?
            // This ufs doesn't provide storage. Allow the master to use this ufs for the journal.
            LOG.info("Journal directory doesn't provide storage: {}", journalDirectory);
            return true;
        }
        String[] files = ufs.list(journalDirectory);
        if (files == null) {
            return false;
        }
        // Search for the format file.
        String formatFilePrefix = Configuration.get(PropertyKey.MASTER_FORMAT_FILE_PREFIX);
        for (String file : files) {
            if (file.startsWith(formatFilePrefix)) {
                return true;
            }
        }
        return false;
    }

    private void connectToUFS() throws IOException {
        String ufsAddress = Configuration.get(PropertyKey.UNDERFS_ADDRESS);
        UnderFileSystem ufs = UnderFileSystem.get(ufsAddress);
        ufs.connectFromMaster(NetworkAddressUtils.getConnectHost(ServiceType.MASTER_RPC));
    }

    /**
     * Blocks until the master is ready to serve requests.
     */
    public void waitForReady() {
        while (true) {
            if (mMasterServiceServer != null && mMasterServiceServer.isServing() && mWebServer != null
                    && mWebServer.getServer().isRunning()) {
                return;
            }
            CommonUtils.sleepMs(10);
        }
    }
}