// Copyright 2016 Twitter. 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 com.twitter.heron.simulator.executors; import java.util.logging.Level; import java.util.logging.Logger; import com.twitter.heron.api.generated.TopologyAPI; import com.twitter.heron.common.basics.Communicator; import com.twitter.heron.common.basics.SlaveLooper; import com.twitter.heron.common.utils.metrics.MetricsCollector; import com.twitter.heron.common.utils.misc.PhysicalPlanHelper; import com.twitter.heron.proto.system.HeronTuples; import com.twitter.heron.proto.system.Metrics; import com.twitter.heron.proto.system.PhysicalPlans; import com.twitter.heron.simulator.instance.BoltInstance; import com.twitter.heron.simulator.instance.IInstance; import com.twitter.heron.simulator.instance.SpoutInstance; /** * InstanceExecutor helps to group all necessary resources for an instance into a class and, * provide methods to access these resources externally. * <p> * It helps uniform the ways to access instance resources */ public class InstanceExecutor implements Runnable { public static final int CAPACITY = 5; public static final double CURRENT_SAMPLE_WEIGHT = 0.5; private static final Logger LOG = Logger.getLogger(InstanceExecutor.class.getName()); private final PhysicalPlanHelper physicalPlanHelper; private final SlaveLooper looper; private final Communicator<HeronTuples.HeronTupleSet> streamInQueue; private final Communicator<HeronTuples.HeronTupleSet> streamOutQueue; private final Communicator<Metrics.MetricPublisherPublishMessage> metricsOutQueue; private IInstance instance; private volatile boolean toStop = false; private volatile boolean toActivate = false; private volatile boolean toDeactivate = false; private boolean isInstanceStarted = false; public InstanceExecutor(PhysicalPlans.PhysicalPlan physicalPlan, String instanceId) { streamInQueue = new Communicator<>(); streamOutQueue = new Communicator<>(); metricsOutQueue = new Communicator<>(); looper = new SlaveLooper(); MetricsCollector metricsCollector = new MetricsCollector(looper, metricsOutQueue); physicalPlanHelper = createPhysicalPlanHelper(physicalPlan, instanceId, metricsCollector); initInstanceManager(); LOG.log(Level.INFO, "Incarnating ourselves as {0} with task id {1}", new Object[]{physicalPlanHelper.getMyComponent(), physicalPlanHelper.getMyTaskId()}); } public Communicator<HeronTuples.HeronTupleSet> getStreamInQueue() { return streamInQueue; } public Communicator<HeronTuples.HeronTupleSet> getStreamOutQueue() { return streamOutQueue; } public Communicator<Metrics.MetricPublisherPublishMessage> getMetricsOutQueue() { return metricsOutQueue; } public String getInstanceId() { return physicalPlanHelper.getMyInstanceId(); } public String getComponentName() { return physicalPlanHelper.getMyComponent(); } public int getTaskId() { return physicalPlanHelper.getMyTaskId(); } protected IInstance createInstance() { return (physicalPlanHelper.getMySpout() != null) ? new SpoutInstance(physicalPlanHelper, streamInQueue, streamOutQueue, looper) : new BoltInstance(physicalPlanHelper, streamInQueue, streamOutQueue, looper); } protected PhysicalPlanHelper createPhysicalPlanHelper(PhysicalPlans.PhysicalPlan physicalPlan, String instanceId, MetricsCollector metricsCollector) { PhysicalPlanHelper localPhysicalPlanHelper = new PhysicalPlanHelper(physicalPlan, instanceId); // Bind the MetricsCollector with topologyContext localPhysicalPlanHelper.setTopologyContext(metricsCollector); return localPhysicalPlanHelper; } protected void initInstanceManager() { streamInQueue.setConsumer(looper); streamInQueue.init(CAPACITY, CAPACITY, CURRENT_SAMPLE_WEIGHT); streamOutQueue.setProducer(looper); streamOutQueue.init(CAPACITY, CAPACITY, CURRENT_SAMPLE_WEIGHT); metricsOutQueue.setProducer(looper); } // Flags would be set in other threads, // But we have to handle these flags inside the WakeableLooper thread protected void handleControlSignal() { if (toActivate) { if (!isInstanceStarted) { startInstance(); } instance.activate(); LOG.info("Activated instance: " + physicalPlanHelper.getMyInstanceId()); // Reset the flag value toActivate = false; } if (toDeactivate) { instance.deactivate(); LOG.info("Deactivated instance: " + physicalPlanHelper.getMyInstanceId()); // Reset the flag value toDeactivate = false; } if (toStop) { instance.stop(); LOG.info("Stopped instance: " + physicalPlanHelper.getMyInstanceId()); // Reset the flag value toStop = false; return; } } @Override public void run() { Thread.currentThread().setName(String.format("%s_%s", physicalPlanHelper.getMyComponent(), physicalPlanHelper.getMyInstanceId())); instance = createInstance(); if (physicalPlanHelper.getTopologyState().equals(TopologyAPI.TopologyState.RUNNING)) { startInstance(); } // Handle Control Signal if needed Runnable handleControlTask = new Runnable() { @Override public void run() { handleControlSignal(); } }; looper.addTasksOnWakeup(handleControlTask); looper.loop(); } public void stop() { toStop = true; looper.wakeUp(); } public void activate() { toActivate = true; looper.wakeUp(); } public void deactivate() { toDeactivate = true; looper.wakeUp(); } private void startInstance() { instance.start(); isInstanceStarted = true; LOG.info("Started instance."); } }