tachyon.worker.block.BlockWorker.java Source code

Java tutorial

Introduction

Here is the source code for tachyon.worker.block.BlockWorker.java

Source

/*
 * Licensed to the University of California, Berkeley under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You 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 tachyon.worker.block;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Throwables;

import tachyon.Constants;
import tachyon.Sessions;
import tachyon.client.WorkerBlockMasterClient;
import tachyon.client.WorkerFileSystemMasterClient;
import tachyon.conf.TachyonConf;
import tachyon.metrics.MetricsSystem;
import tachyon.thrift.NetAddress;
import tachyon.thrift.WorkerService;
import tachyon.util.CommonUtils;
import tachyon.util.ThreadFactoryUtils;
import tachyon.util.io.PathUtils;
import tachyon.util.network.NetworkAddressUtils;
import tachyon.util.network.NetworkAddressUtils.ServiceType;
import tachyon.web.UIWebServer;
import tachyon.web.WorkerUIWebServer;
import tachyon.worker.DataServer;
import tachyon.worker.WorkerContext;
import tachyon.worker.WorkerSource;

/**
 * The class is responsible for managing all top level components of the Block Worker, including:
 *
 * Servers: BlockServiceHandler (RPC Server), BlockDataServer (Data Server)
 *
 * Periodic Threads: BlockMasterSync (Worker to Master continuous communication)
 *
 * Logic: BlockDataManager (Logic for all block related storage operations)
 */
public final class BlockWorker {
    private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER_TYPE);

    /** Runnable responsible for heartbeating and registration with master. */
    private final BlockMasterSync mBlockMasterSync;
    /** Runnable responsible for fetching pinlist from master. */
    private final PinListSync mPinListSync;
    /** Runnable responsible for clean up potential zombie sessions. */
    private final SessionCleaner mSessionCleanerThread;
    /** Logic for handling RPC requests. */
    private final BlockServiceHandler mServiceHandler;
    /** Logic for managing block store and under file system store. */
    private final BlockDataManager mBlockDataManager;
    /** Server for data requests and responses. */
    private final DataServer mDataServer;
    /** Client for all block master communication */
    private final WorkerBlockMasterClient mBlockMasterClient;
    /** Client for all file system master communication */
    private final WorkerFileSystemMasterClient mFileSystemMasterClient;
    /** The executor service for the master client thread */
    private final ExecutorService mMasterClientExecutorService;
    /** Threadpool for the master sync */
    private final ExecutorService mSyncExecutorService;
    /** Net address of this worker */
    private final NetAddress mWorkerNetAddress;
    /** Configuration object */
    private final TachyonConf mTachyonConf;
    /** Server socket for thrift */
    private final TServerSocket mThriftServerSocket;
    /** RPC local port for thrift */
    private final int mPort;
    /** Thread pool for thrift */
    private final TThreadPoolServer mThriftServer;
    /** Worker start time in milliseconds */
    private final long mStartTimeMs;
    /** Worker Web UI server */
    private final UIWebServer mWebServer;
    /** Worker metrics system */
    private MetricsSystem mWorkerMetricsSystem;
    /** Space reserver for the block data manager */
    private SpaceReserver mSpaceReserver = null;

    /**
     * @return the worker service handler
     */
    public BlockServiceHandler getWorkerServiceHandler() {
        return mServiceHandler;
    }

    /**
     * @return the worker RPC service bind host
     */
    public String getRPCBindHost() {
        return NetworkAddressUtils.getThriftSocket(mThriftServerSocket).getLocalSocketAddress().toString();
    }

    /**
     * @return the worker RPC service port
     */
    public int getRPCLocalPort() {
        return mPort;
    }

    /**
     * @return the worker data service bind host
     */
    public String getDataBindHost() {
        return mDataServer.getBindHost();
    }

    /**
     * @return the worker data service port
     */
    public int getDataLocalPort() {
        return mDataServer.getPort();
    }

    /**
     * @return the worker web service bind host
     */
    public String getWebBindHost() {
        return mWebServer.getBindHost();
    }

    /**
     * @return the worker web service port
     */
    public int getWebLocalPort() {
        return mWebServer.getLocalPort();
    }

    /**
     * Creates a Tachyon Block Worker.
     *
     * @throws IOException for other exceptions
     */
    public BlockWorker() throws IOException {
        mTachyonConf = WorkerContext.getConf();
        mStartTimeMs = System.currentTimeMillis();

        // Setup MasterClientBase along with its heartbeat ExecutorService
        mMasterClientExecutorService = Executors.newFixedThreadPool(1,
                ThreadFactoryUtils.build("worker-client-heartbeat-%d", true));
        mBlockMasterClient = new WorkerBlockMasterClient(
                NetworkAddressUtils.getConnectAddress(ServiceType.MASTER_RPC, mTachyonConf),
                mMasterClientExecutorService, mTachyonConf);

        mFileSystemMasterClient = new WorkerFileSystemMasterClient(
                NetworkAddressUtils.getConnectAddress(ServiceType.MASTER_RPC, mTachyonConf),
                mMasterClientExecutorService, mTachyonConf);

        // Set up BlockDataManager
        WorkerSource workerSource = new WorkerSource();
        mBlockDataManager = new BlockDataManager(workerSource, mBlockMasterClient, mFileSystemMasterClient,
                new TieredBlockStore());

        // Setup metrics collection
        mWorkerMetricsSystem = new MetricsSystem("worker", mTachyonConf);
        workerSource.registerGauges(mBlockDataManager);
        mWorkerMetricsSystem.registerSource(workerSource);

        // Set up DataServer
        mDataServer = DataServer.Factory.createDataServer(
                NetworkAddressUtils.getBindAddress(ServiceType.WORKER_DATA, mTachyonConf), mBlockDataManager,
                mTachyonConf);
        // reset data server port
        mTachyonConf.set(Constants.WORKER_DATA_PORT, Integer.toString(mDataServer.getPort()));

        // Setup RPC Server
        mServiceHandler = new BlockServiceHandler(mBlockDataManager);
        mThriftServerSocket = createThriftServerSocket();
        mPort = NetworkAddressUtils.getThriftPort(mThriftServerSocket);
        // reset worker RPC port
        mTachyonConf.set(Constants.WORKER_PORT, Integer.toString(mPort));
        mThriftServer = createThriftServer();
        mWorkerNetAddress = new NetAddress(NetworkAddressUtils.getConnectHost(ServiceType.WORKER_RPC, mTachyonConf),
                mPort, mDataServer.getPort());

        // Set up web server
        mWebServer = new WorkerUIWebServer(ServiceType.WORKER_WEB,
                NetworkAddressUtils.getBindAddress(ServiceType.WORKER_WEB, mTachyonConf), mBlockDataManager,
                NetworkAddressUtils.getConnectAddress(ServiceType.WORKER_RPC, mTachyonConf), mStartTimeMs,
                mTachyonConf);

        // Setup Worker to Master Syncer
        // We create four threads for two syncers, one cleaner and one asynchronous evictor:
        // mBlockMasterSync, mPinListSync, mSessionCleanerThread, mSpaceReserver
        mSyncExecutorService = Executors.newFixedThreadPool(4,
                ThreadFactoryUtils.build("worker-heartbeat-%d", true));

        mBlockMasterSync = new BlockMasterSync(mBlockDataManager, mWorkerNetAddress, mBlockMasterClient);
        // Get the worker id
        // TODO(calvin): Do this at TachyonWorker.
        mBlockMasterSync.setWorkerId();

        // Setup PinListSyncer
        mPinListSync = new PinListSync(mBlockDataManager, mFileSystemMasterClient);

        // Setup session cleaner
        mSessionCleanerThread = new SessionCleaner(mBlockDataManager);

        // Setup space reserver
        if (mTachyonConf.getBoolean(Constants.WORKER_SPACE_RESERVER_ENABLE)) {
            mSpaceReserver = new SpaceReserver(mBlockDataManager);
        }

        // Setup session metadata mapping
        // TODO(calvin): Have a top level register that gets the worker id.
        long workerId = mBlockMasterSync.getWorkerId();
        String ufsWorkerFolder = mTachyonConf.get(Constants.UNDERFS_WORKERS_FOLDER);
        Sessions sessions = new Sessions(PathUtils.concatPath(ufsWorkerFolder, workerId), mTachyonConf);

        // Give BlockDataManager a pointer to the session metadata mapping
        // TODO(calvin): Fix this hack when we have a top level register.
        mBlockDataManager.setSessions(sessions);
        mBlockDataManager.setWorkerId(workerId);
    }

    /**
     * Gets this worker's {@link tachyon.thrift.NetAddress}, which is the worker's hostname, rpc
     * server port, and data server port
     *
     * @return the worker's net address
     */
    public NetAddress getWorkerNetAddress() {
        return mWorkerNetAddress;
    }

    /**
     * Runs the block worker. The thread calling this will be blocked until the thrift server shuts
     * down.
     */
    public void process() {
        mWorkerMetricsSystem.start();

        // Add the metrics servlet to the web server, this must be done after the metrics system starts
        mWebServer.addHandler(mWorkerMetricsSystem.getServletHandler());

        mSyncExecutorService.submit(mBlockMasterSync);

        // Start the pinlist syncer to perform the periodical fetching
        mSyncExecutorService.submit(mPinListSync);

        // Start the session cleanup checker to perform the periodical checking
        mSyncExecutorService.submit(mSessionCleanerThread);

        // Start the space reserver 
        if (mSpaceReserver != null) {
            mSyncExecutorService.submit(mSpaceReserver);
        }

        mWebServer.startWebServer();
        mThriftServer.serve();
    }

    /**
     * Stops the block worker. This method should only be called to terminate the worker.
     *
     * @throws IOException if the data server fails to close.
     */
    public void stop() throws IOException {
        mDataServer.close();
        mThriftServer.stop();
        mThriftServerSocket.close();
        mBlockMasterSync.stop();
        mPinListSync.stop();
        mSessionCleanerThread.stop();
        mBlockMasterClient.close();
        if (mSpaceReserver != null) {
            mSpaceReserver.stop();
        }
        mFileSystemMasterClient.close();
        mMasterClientExecutorService.shutdown();
        mSyncExecutorService.shutdown();
        mWorkerMetricsSystem.stop();
        try {
            mWebServer.shutdownWebServer();
        } catch (Exception e) {
            LOG.error("Failed to stop web server", e);
        }
        mBlockDataManager.stop();
        while (!mDataServer.isClosed() || mThriftServer.isServing()) {
            // TODO(calvin): The reason to stop and close again is due to some issues in Thrift.
            mDataServer.close();
            mThriftServer.stop();
            mThriftServerSocket.close();
            CommonUtils.sleepMs(100);
        }
    }

    /**
     * Helper method to create a {@link org.apache.thrift.server.TThreadPoolServer} for handling
     * incoming RPC requests.
     *
     * @return a thrift server
     */
    private TThreadPoolServer createThriftServer() {
        int minWorkerThreads = mTachyonConf.getInt(Constants.WORKER_MIN_WORKER_THREADS);
        int maxWorkerThreads = mTachyonConf.getInt(Constants.WORKER_MAX_WORKER_THREADS);
        WorkerService.Processor<BlockServiceHandler> processor = new WorkerService.Processor<BlockServiceHandler>(
                mServiceHandler);
        return new TThreadPoolServer(new TThreadPoolServer.Args(mThriftServerSocket)
                .minWorkerThreads(minWorkerThreads).maxWorkerThreads(maxWorkerThreads).processor(processor)
                .transportFactory(new TFramedTransport.Factory())
                .protocolFactory(new TBinaryProtocol.Factory(true, true)));
    }

    /**
     * Helper method to create a {@link org.apache.thrift.transport.TServerSocket} for the RPC server
     *
     * @return a thrift server socket
     */
    private TServerSocket createThriftServerSocket() {
        try {
            return new TServerSocket(NetworkAddressUtils.getBindAddress(ServiceType.WORKER_RPC, mTachyonConf));
        } catch (TTransportException tte) {
            LOG.error(tte.getMessage(), tte);
            throw Throwables.propagate(tte);
        }
    }
}