/** * 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.alibaba.jstorm.daemon.nimbus; import com.alibaba.jstorm.callback.Callback; import com.alibaba.jstorm.callback.impl.*; import com.alibaba.jstorm.cluster.StormBase; import com.alibaba.jstorm.cluster.StormStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Status changing * * @author version1: lixin version2: Longda */ public class StatusTransition { private final static Logger LOG = LoggerFactory.getLogger(StatusTransition.class); private NimbusData data; private Map<String, Object> topologyLocks = new ConcurrentHashMap<>(); public StatusTransition(NimbusData data) { this.data = data; } public <T> void transition(String topologyId, boolean errorOnNoTransition, StatusType changeStatus, T... args) throws Exception { // lock outside Object lock = topologyLocks.get(topologyId); if (lock == null) { lock = new Object(); topologyLocks.put(topologyId, lock); } if (data.getIsShutdown().get()) { LOG.info("Nimbus is shutting down, skip this event " + topologyId + "->" + changeStatus); return; } synchronized (lock) { transitionLock(topologyId, errorOnNoTransition, changeStatus, args); // update the lock times topologyLocks.put(topologyId, lock); } } /** * Changing status * * @param args -- will be used in the status changing callback */ public <T> void transitionLock(String topologyId, boolean errorOnNoTransition, StatusType changeStatus, T... args) throws Exception { // get ZK's topology node's data, which is StormBase StormBase stormbase = data.getStormClusterState().storm_base(topologyId, null); if (stormbase == null) { LOG.error("Cannot apply event: changing status " + topologyId + " -> " + changeStatus.getStatus() + ", cause: failed to get StormBase from ZK"); return; } StormStatus currentStatus = stormbase.getStatus(); if (currentStatus == null) { LOG.error("Cannot apply event: changing status " + topologyId + " -> " + changeStatus.getStatus() + ", cause: topologyStatus is null in ZK"); return; } // <currentStatus, Map<changingStatus, callback>> Map<StatusType, Map<StatusType, Callback>> callbackMap = stateTransitions(topologyId, currentStatus); // get current changingCallbacks Map<StatusType, Callback> changingCallbacks = callbackMap.get(currentStatus.getStatusType()); if (changingCallbacks == null || !changingCallbacks.containsKey(changeStatus) || changingCallbacks.get(changeStatus) == null) { String msg = "No transition for event: changing status:" + changeStatus.getStatus() + ", current status: " + currentStatus.getStatusType() + ", topology-id: " + topologyId; LOG.info(msg); if (errorOnNoTransition) { throw new RuntimeException(msg); } return; } Callback callback = changingCallbacks.get(changeStatus); Object obj = callback.execute(args); if (obj != null && obj instanceof StormStatus) { StormStatus newStatus = (StormStatus) obj; // update status to ZK data.getStormClusterState().update_storm(topologyId, newStatus); LOG.info("Successfully updated " + topologyId + " to status " + newStatus); } LOG.info("Successfully apply event: changing status " + topologyId + " -> " + changeStatus.getStatus()); } /** * generate status changing map * * @param topologyId topology id * @param currentStatus current topology status * @return Map[StatusType, Map[StatusType, Callback]] means Map[currentStatus, Map[changingStatus, Callback]] */ private Map<StatusType, Map<StatusType, Callback>> stateTransitions(String topologyId, StormStatus currentStatus) { /** * * 1. Status: this status will be stored in ZK killed/inactive/active/rebalancing 2. action: * * monitor -- every Config.NIMBUS_MONITOR_FREQ_SECS seconds will trigger this only valid when current status is active inactivate -- client will trigger * this action, only valid when current status is active activate -- client will trigger this action only valid when current status is inactive startup * -- when nimbus startup, it will trigger this action only valid when current status is killed/rebalancing kill -- client kill topology will trigger * this action, only valid when current status is active/inactive/killed remove -- 30 seconds after client submit kill command, it will do this action, * only valid when current status is killed rebalance -- client submit rebalance command, only valid when current status is active/deactive do_rebalance * -- 30 seconds after client submit rebalance command, it will do this action, only valid when current status is rebalance */ Map<StatusType, Map<StatusType, Callback>> rtn = new HashMap<>(); // current status is active Map<StatusType, Callback> activeMap = new HashMap<>(); activeMap.put(StatusType.monitor, new ReassignTransitionCallback(data, topologyId)); activeMap.put(StatusType.inactivate, new InactiveTransitionCallback()); activeMap.put(StatusType.startup, null); activeMap.put(StatusType.activate, null); activeMap.put(StatusType.kill, new KillTransitionCallback(data, topologyId)); activeMap.put(StatusType.remove, null); activeMap.put(StatusType.rebalance, new RebalanceTransitionCallback(data, topologyId, currentStatus)); activeMap.put(StatusType.do_rebalance, null); activeMap.put(StatusType.done_rebalance, null); activeMap.put(StatusType.update_topology, new UpdateTopologyTransitionCallback(data, topologyId, currentStatus)); rtn.put(StatusType.active, activeMap); // current status is inactive Map<StatusType, Callback> inactiveMap = new HashMap<>(); inactiveMap.put(StatusType.monitor, new ReassignTransitionCallback(data, topologyId, new StormStatus(StatusType.inactive))); inactiveMap.put(StatusType.inactivate, null); inactiveMap.put(StatusType.startup, null); inactiveMap.put(StatusType.activate, new ActiveTransitionCallback()); inactiveMap.put(StatusType.kill, new KillTransitionCallback(data, topologyId)); inactiveMap.put(StatusType.remove, null); inactiveMap.put(StatusType.rebalance, new RebalanceTransitionCallback(data, topologyId, currentStatus)); inactiveMap.put(StatusType.do_rebalance, null); inactiveMap.put(StatusType.done_rebalance, null); inactiveMap.put(StatusType.update_topology, null); rtn.put(StatusType.inactive, inactiveMap); // current status is killed Map<StatusType, Callback> killedMap = new HashMap<>(); killedMap.put(StatusType.monitor, null); killedMap.put(StatusType.inactivate, null); killedMap.put(StatusType.startup, new KillTransitionCallback(data, topologyId)); killedMap.put(StatusType.activate, null); killedMap.put(StatusType.kill, new KillTransitionCallback(data, topologyId)); killedMap.put(StatusType.remove, new RemoveTransitionCallback(data, topologyId)); killedMap.put(StatusType.rebalance, null); killedMap.put(StatusType.do_rebalance, null); killedMap.put(StatusType.done_rebalance, null); killedMap.put(StatusType.update_topology, null); rtn.put(StatusType.killed, killedMap); // current status is under rebalancing Map<StatusType, Callback> rebalancingMap = new HashMap<>(); StatusType rebalanceOldStatus = StatusType.active; if (currentStatus.getOldStatus() != null) { rebalanceOldStatus = currentStatus.getOldStatus().getStatusType(); // fix double rebalance, make the status always as rebalancing if (rebalanceOldStatus == StatusType.rebalancing) { rebalanceOldStatus = StatusType.active; } } rebalancingMap.put(StatusType.monitor, null); rebalancingMap.put(StatusType.inactivate, null); rebalancingMap.put(StatusType.startup, new RebalanceTransitionCallback(data, topologyId, new StormStatus(rebalanceOldStatus))); rebalancingMap.put(StatusType.activate, null); rebalancingMap.put(StatusType.kill, null); rebalancingMap.put(StatusType.remove, null); rebalancingMap.put(StatusType.rebalance, new RebalanceTransitionCallback(data, topologyId, currentStatus)); rebalancingMap.put(StatusType.do_rebalance, new DoRebalanceTransitionCallback(data, topologyId, new StormStatus(rebalanceOldStatus))); rebalancingMap.put(StatusType.done_rebalance, new DoneRebalanceTransitionCallback(data, topologyId)); rebalancingMap.put(StatusType.update_topology, null); rtn.put(StatusType.rebalancing, rebalancingMap); Map<StatusType, Callback> upgradingMap = new HashMap<>(); upgradingMap.put(StatusType.monitor, new RollbackTransitionCallback(data, topologyId)); upgradingMap.put(StatusType.inactivate, null); upgradingMap.put(StatusType.startup, null); upgradingMap.put(StatusType.activate, null); upgradingMap.put(StatusType.kill, new KillTransitionCallback(data, topologyId)); upgradingMap.put(StatusType.remove, new RemoveTransitionCallback(data, topologyId)); upgradingMap.put(StatusType.rebalance, null); upgradingMap.put(StatusType.do_rebalance, null); upgradingMap.put(StatusType.done_rebalance, null); upgradingMap.put(StatusType.update_topology, null); upgradingMap.put(StatusType.upgrading, null); rtn.put(StatusType.upgrading, upgradingMap); return rtn; } }