/**
* 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.schedule.default_assign;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.alibaba.jstorm.client.ConfigExtension;
import com.alibaba.jstorm.utils.JStormUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.jstorm.daemon.supervisor.SupervisorInfo;
import com.alibaba.jstorm.schedule.Assignment;
import com.alibaba.jstorm.schedule.IToplogyScheduler;
import com.alibaba.jstorm.schedule.TopologyAssignContext;
import com.alibaba.jstorm.utils.FailedAssignTopologyException;
public class DefaultTopologyScheduler implements IToplogyScheduler {
private static final Logger LOG = LoggerFactory.getLogger(DefaultTopologyScheduler.class);
private Map nimbusConf;
@Override
public void prepare(Map conf) {
nimbusConf = conf;
}
/**
* @@@ Here maybe exist one problem, some dead slots have been free
*
* @param context
*/
protected void freeUsed(TopologyAssignContext context) {
Set<Integer> canFree = new HashSet<Integer>();
canFree.addAll(context.getAllTaskIds());
canFree.removeAll(context.getUnstoppedTaskIds());
Map<String, SupervisorInfo> cluster = context.getCluster();
Assignment oldAssigns = context.getOldAssignment();
for (Integer task : canFree) {
ResourceWorkerSlot worker = oldAssigns.getWorkerByTaskId(task);
if (worker == null) {
LOG.warn("When free rebalance resource, no ResourceAssignment of task " + task);
continue;
}
SupervisorInfo supervisorInfo = cluster.get(worker.getNodeId());
if (supervisorInfo == null) {
continue;
}
supervisorInfo.getAvailableWorkerPorts().add(worker.getPort());
}
}
private Set<Integer> getNeedAssignTasks(DefaultTopologyAssignContext context) {
Set<Integer> needAssign = new HashSet<Integer>();
int assignType = context.getAssignType();
if (assignType == TopologyAssignContext.ASSIGN_TYPE_NEW) {
needAssign.addAll(context.getAllTaskIds());
} else if (assignType == TopologyAssignContext.ASSIGN_TYPE_REBALANCE) {
needAssign.addAll(context.getAllTaskIds());
needAssign.removeAll(context.getUnstoppedTaskIds());
} else { // ASSIGN_TYPE_MONITOR
Set<Integer> deadTasks = context.getDeadTaskIds();
needAssign.addAll(deadTasks);
}
return needAssign;
}
/**
* Get the task Map which the task is alive and will be kept Only when type is ASSIGN_TYPE_MONITOR, it is valid
*
* @param defaultContext
* @param needAssigns
* @return
*/
public Set<ResourceWorkerSlot> getKeepAssign(DefaultTopologyAssignContext defaultContext, Set<Integer> needAssigns) {
Set<Integer> keepAssignIds = new HashSet<Integer>();
keepAssignIds.addAll(defaultContext.getAllTaskIds());
keepAssignIds.removeAll(defaultContext.getUnstoppedTaskIds());
keepAssignIds.removeAll(needAssigns);
Set<ResourceWorkerSlot> keeps = new HashSet<ResourceWorkerSlot>();
if (keepAssignIds.isEmpty()) {
return keeps;
}
Assignment oldAssignment = defaultContext.getOldAssignment();
if (oldAssignment == null) {
return keeps;
}
keeps.addAll(defaultContext.getOldWorkers());
for (ResourceWorkerSlot worker : defaultContext.getOldWorkers()) {
for (Integer task : worker.getTasks()) {
if (!keepAssignIds.contains(task)) {
keeps.remove(worker);
break;
}
}
}
return keeps;
}
@Override
public Set<ResourceWorkerSlot> assignTasks(TopologyAssignContext context) throws FailedAssignTopologyException {
int assignType = context.getAssignType();
if (TopologyAssignContext.isAssignTypeValid(assignType) == false) {
throw new FailedAssignTopologyException("Invalide Assign Type " + assignType);
}
DefaultTopologyAssignContext defaultContext = new DefaultTopologyAssignContext(context);
if (assignType == TopologyAssignContext.ASSIGN_TYPE_REBALANCE) {
/**
* Mark all current assigned worker as available. Current assignment will be restored in task scheduler.
*/
freeUsed(defaultContext);
}
LOG.info("Dead tasks:" + defaultContext.getDeadTaskIds());
LOG.info("Unstopped tasks:" + defaultContext.getUnstoppedTaskIds());
Set<Integer> needAssignTasks = getNeedAssignTasks(defaultContext);
Set<ResourceWorkerSlot> keepAssigns = getKeepAssign(defaultContext, needAssignTasks);
// please use tree map to make task sequence
Set<ResourceWorkerSlot> ret = new HashSet<ResourceWorkerSlot>();
ret.addAll(keepAssigns);
ret.addAll(defaultContext.getUnstoppedWorkers());
int allocWorkerNum = defaultContext.getTotalWorkerNum() - defaultContext.getUnstoppedWorkerNum() - keepAssigns.size();
LOG.info("allocWorkerNum=" + allocWorkerNum + ", totalWorkerNum=" + defaultContext.getTotalWorkerNum() + ", keepWorkerNum=" + keepAssigns.size());
if (allocWorkerNum <= 0) {
LOG.warn("Don't need assign workers, all workers are fine " + defaultContext.toDetailString());
throw new FailedAssignTopologyException("Don't need assign worker, all workers are fine ");
}
List<ResourceWorkerSlot> availableWorkers = WorkerScheduler.getInstance().getAvailableWorkers(defaultContext, needAssignTasks, allocWorkerNum);
TaskScheduler taskScheduler = new TaskScheduler(defaultContext, needAssignTasks, availableWorkers);
Set<ResourceWorkerSlot> assignment = new HashSet<ResourceWorkerSlot>(taskScheduler.assign());
//setting worker's memory for TM
int topologyMasterId = defaultContext.getTopologyMasterTaskId();
Long tmWorkerMem = ConfigExtension.getMemSizePerTopologyMasterWorker(defaultContext.getStormConf());
if (tmWorkerMem != null){
for (ResourceWorkerSlot resourceWorkerSlot : assignment){
if (resourceWorkerSlot.getTasks().contains(topologyMasterId)){
resourceWorkerSlot.setMemSize(tmWorkerMem);
}
}
}
ret.addAll(assignment);
LOG.info("Keep Alive slots:" + keepAssigns);
LOG.info("Unstopped slots:" + defaultContext.getUnstoppedWorkers());
LOG.info("New assign slots:" + assignment);
return ret;
}
}