com.pinterest.terrapin.controller.TerrapinControllerHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.pinterest.terrapin.controller.TerrapinControllerHandler.java

Source

/**
* Licensed to the Apache Software Foundation (ASF) 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 com.pinterest.terrapin.controller;

import com.google.common.collect.ImmutableSet;
import com.pinterest.terrapin.Constants;
import com.pinterest.terrapin.TerrapinUtil;
import com.pinterest.terrapin.base.OstrichAdminService;
import com.pinterest.terrapin.client.FileSetViewManager;
import com.pinterest.terrapin.client.TerrapinClient;
import com.pinterest.terrapin.thrift.generated.TerrapinController;
import com.pinterest.terrapin.zookeeper.ClusterInfo;
import com.pinterest.terrapin.zookeeper.ViewInfo;
import com.pinterest.terrapin.zookeeper.ZooKeeperManager;

import com.twitter.common.zookeeper.ZooKeeperClient;
import com.twitter.finagle.builder.Server;
import com.twitter.finagle.builder.ServerBuilder;
import com.twitter.finagle.stats.OstrichStatsReceiver;
import com.twitter.finagle.thrift.ThriftServerFramedCodec;
import com.twitter.ostrich.stats.Stats;
import com.twitter.util.Duration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.DFSClient;
import org.apache.helix.HelixAdmin;
import org.apache.helix.HelixManager;
import org.apache.helix.HelixManagerFactory;
import org.apache.helix.InstanceType;
import org.apache.helix.manager.zk.ZKHelixAdmin;
import org.apache.helix.manager.zk.ZkClient;
import org.apache.helix.model.ExternalView;
import org.apache.helix.model.HelixConfigScope;
import org.apache.helix.tools.ClusterSetup;
import org.apache.helix.util.ZKClientPool;
import org.apache.helix.webapp.HelixAdminWebApp;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Starts the terrapin thrift server.
 */
public class TerrapinControllerHandler {
    private static final Logger LOG = LoggerFactory.getLogger(TerrapinControllerHandler.class);

    private PropertiesConfiguration configuration;
    private HelixAdmin helixAdmin;
    private HelixManager helixManager;
    private HelixManager spectatorManager;
    private String clusterName;
    private DFSClient hdfsClient;
    private ZooKeeperManager zkManager;
    private HdfsManager hdfsManager;
    private Server server;
    private TerrapinRoutingTableProvider routingTableProvider;
    private GaugeManager gaugeManager;
    private HelixAdminWebApp webApp;
    private StatusServer statusServer;
    // for querying a fileset from admin UI
    private TerrapinClient sampleClient;

    public TerrapinControllerHandler(PropertiesConfiguration configuration) {
        this.configuration = configuration;
    }

    private void startThriftServer(int thriftPort) throws UnknownHostException {
        TerrapinController.ServiceIface serviceImpl = new TerrapinControllerServiceImpl(this.configuration,
                this.zkManager, this.hdfsClient, this.helixAdmin, this.clusterName);
        TerrapinController.Service service = new TerrapinController.Service(serviceImpl,
                new TBinaryProtocol.Factory());

        this.server = ServerBuilder.safeBuild(service,
                ServerBuilder.get().name("TerrapinController").codec(ThriftServerFramedCodec.get())
                        .hostConnectionMaxIdleTime(Duration.fromTimeUnit(
                                configuration.getInt(Constants.THRIFT_CONN_MAX_IDLE_TIME, 1), TimeUnit.MINUTES))
                        .maxConcurrentRequests(configuration.getInt(Constants.THRIFT_MAX_CONCURRENT_REQUESTS, 100))
                        .reportTo(new OstrichStatsReceiver(Stats.get("")))
                        .bindTo(new InetSocketAddress(thriftPort)));
        new OstrichAdminService(configuration.getInt(Constants.OSTRICH_METRICS_PORT, 9999)).start();
    }

    private void reconcileViews(List<String> resourceList) throws Exception {
        for (String resource : resourceList) {
            LOG.info("Reconciling resource " + resource);
            ExternalView externalView = helixAdmin.getResourceExternalView(this.clusterName, resource);
            if (externalView == null) {
                LOG.warn("External view null for " + resource + " - Skipping");
                continue;
            }
            ViewInfo viewInfo = this.zkManager.getViewInfo(resource);
            if (viewInfo == null) {
                LOG.info("Compressed view does not exist for " + resource);
                this.zkManager.setViewInfo(new ViewInfo(externalView));
            } else {
                ViewInfo currentViewInfo = new ViewInfo(externalView);
                if (!currentViewInfo.equals(viewInfo)) {
                    LOG.info("Mismatch b/w external view & compressed view for " + resource);
                    this.zkManager.setViewInfo(currentViewInfo);
                } else {
                    LOG.info("External view & compressed view match for " + resource);
                }
            }
        }
    }

    private void setUpHelixCluster(String zookeeperQuorum, String clusterName) {
        ZkClient zkClient = ZKClientPool.getZkClient(zookeeperQuorum);
        HelixAdmin helixAdmin = new ZKHelixAdmin(zkClient);
        try {
            if (!ImmutableSet.copyOf(helixAdmin.getClusters()).contains(clusterName)) {
                ClusterSetup helixClusterSetUp = new ClusterSetup(zkClient);
                helixClusterSetUp.addCluster(clusterName, false);
                helixClusterSetUp.setConfig(HelixConfigScope.ConfigScopeProperty.CLUSTER, clusterName,
                        "allowParticipantAutoJoin=true");
            }
        } finally {
            zkClient.close();
        }
    }

    public void start() throws Exception {
        String zookeeperQuorum = TerrapinUtil.getZKQuorumFromConf(configuration);
        // Ensure that the cluster exists and connect through helix.
        this.helixAdmin = new ZKHelixAdmin(zookeeperQuorum);
        this.clusterName = configuration.getString(Constants.HELIX_CLUSTER, Constants.HELIX_CLUSTER_NAME_DEFAULT);
        setUpHelixCluster(zookeeperQuorum, this.clusterName);

        String namenode = configuration.getString(Constants.HDFS_NAMENODE);
        int hdfsReplicationFactor = configuration.getInt(Constants.HDFS_REPLICATION,
                Constants.DEFAULT_HDFS_REPLICATION);

        // Start the zookeeper manager and watch filesets/compressed views.
        ZooKeeperClient zkClient = TerrapinUtil.getZooKeeperClient(zookeeperQuorum, 30);
        this.zkManager = new ZooKeeperManager(zkClient, this.clusterName);
        this.zkManager.createClusterPaths();
        this.zkManager.registerWatchAllFileSets();
        ClusterInfo clusterInfo = new ClusterInfo(namenode, hdfsReplicationFactor);
        // Set the HDFS replication factor and namenode address.
        this.zkManager.setClusterInfo(clusterInfo);

        // Initialize a sample client instance
        this.sampleClient = new TerrapinClient(new FileSetViewManager(zkClient, clusterName), clusterName,
                configuration.getInt(Constants.THRIFT_PORT, Constants.DEFAULT_THRIFT_PORT), 1000, 1000);

        List<String> resourceList = this.helixAdmin.getResourcesInCluster(this.clusterName);
        reconcileViews(resourceList);

        // Instantiate HDFS client.
        LOG.info("Connecting to HDFS: " + namenode);
        Configuration conf = new Configuration();
        conf.set("fs.default.name", namenode);
        this.hdfsClient = new DFSClient(conf);

        LOG.info("Starting Helix against " + zookeeperQuorum);
        int thriftPort = configuration.getInt(Constants.THRIFT_PORT, Constants.DEFAULT_THRIFT_PORT);
        String instanceName = InetAddress.getLocalHost().getHostName() + "_" + thriftPort;

        // Connect as spectator.
        this.spectatorManager = HelixManagerFactory.getZKHelixManager(this.clusterName, instanceName,
                InstanceType.SPECTATOR, zookeeperQuorum);
        this.spectatorManager.connect();
        this.routingTableProvider = new TerrapinRoutingTableProvider(zkManager, resourceList);
        this.routingTableProvider.start();
        this.gaugeManager = new GaugeManager(zkManager, Constants.GAUGE_MANAGER_EXEC_INTERVAL_SECONDS_DEFAULT);
        this.spectatorManager.addExternalViewChangeListener(this.routingTableProvider);

        this.hdfsManager = new HdfsManager(configuration, zkManager, this.clusterName, helixAdmin, this.hdfsClient,
                this.routingTableProvider);

        // Connect as controller.
        this.helixManager = HelixManagerFactory.getZKHelixManager(this.clusterName,
                InetAddress.getLocalHost().getHostName() + "_" + thriftPort, InstanceType.CONTROLLER,
                zookeeperQuorum);
        this.helixManager.connect();
        this.helixManager.addControllerListener(this.hdfsManager);

        LOG.info("Starting thrift server on " + thriftPort);
        // Start up the thrift server.
        startThriftServer(thriftPort);

        // Start the Helix Web App.
        this.webApp = new HelixAdminWebApp(zookeeperQuorum,
                configuration.getInt(Constants.HELIX_WEBAPP_PORT, 60000));
        this.webApp.start();

        // Start the status server to serve servlets
        this.statusServer = new StatusServer("status",
                configuration.getString(Constants.STATUS_SERVER_BINDING_ADDRESS, "0.0.0.0"),
                configuration.getInt(Constants.STATUS_SERVER_BINDING_PORT, 50030), false, this.clusterName,
                this.zkManager, this.hdfsClient, this.sampleClient);
        this.statusServer.start();
    }

    public void shutdown() {
        this.server.close(Duration.fromSeconds(60));
        webApp.stop();
        this.helixAdmin.close();
        this.helixManager.disconnect();
        this.routingTableProvider.shutdown();
        // Sleep a little to make sure that the zk node truly disappears.
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            LOG.error("Interrupted in shutdown. This should not happen.");
        }
        this.hdfsManager.shutdown();
        this.gaugeManager.shutdown();
        try {
            this.statusServer.stop();
        } catch (Exception e) {
            e.printStackTrace();
            LOG.warn("failed to stop status server", e);
        }
    }
}