package com.alimama.mdrill.topology;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.log4j.Logger;
import com.alipay.bluewhale.core.cluster.StormClusterState;
import com.alipay.bluewhale.core.custom.IAssignment;
import com.alipay.bluewhale.core.daemon.NodePort;
import com.alipay.bluewhale.core.daemon.supervisor.SupervisorInfo;
import com.alipay.bluewhale.core.task.common.TaskInfo;
import com.alipay.bluewhale.core.utils.StormUtils;
/**
*
#----任务分配,注意,原先分布在同一个位置的任务,迁移后还必须分布在一起,比如说acker:0与merger:10分布在一起了,那么迁移后他们还要在一起,为了保险起见,建议同一个端口跑多个任务的,可以像下面acker,heartbeat那样----
mdrill.task.ports.adhoc: "6901~6902"
mdrill.task.assignment.adhoc:
- "####看到这里估计大家都会晕,但是这个是任务调度很重要的地方,出去透透气,回来搞定这里吧####"
- "####下面为初始分布,用于同一台机器之间没有宕机的调度####"
- "merge:10&shard@0:0~4;tiansuan1.kgb.cm4:6601,6701~6704;6602,6711~6714;6603,6721~6724"
- "merge:1&shard@0:5~9;tiansuan2.kgb.cm4:6601,6701~6704;6602,6711~6714;6603,6721~6724"
- "merge:2&shard@0:48~53;adhoc1.kgb.cm6:6601,6701~6705;6602,6711~6715;6603,6721~6725"
- "merge:3&shard@0:10~14;adhoc2.kgb.cm6:6601,6701~6704;6602,6711~6714;6603,6721~6724"
- "merge:4&shard@0:15~19;adhoc3.kgb.cm6:6601,6701~6704;6602,6711~6714;6603,6721~6724"
- "merge:5&shard@0:20~24;adhoc4.kgb.cm6:6601,6701~6704;6602,6711~6714;6603,6721~6724"
- "merge:6&shard@0:25~30;adhoc5.kgb.cm6:6601,6701~6705;6602,6711~6715;6603,6721~6725"
- "merge:7&shard@0:31~36;adhoc6.kgb.cm6:6601,6701~6705;6602,6711~6715;6603,6721~6725"
- "merge:8&shard@0:37~41;adhoc7.kgb.cm6:6601,6701~6704;6602,6711~6714;6603,6721~6724"
- "merge:9&shard@0:42~47;adhoc8.kgb.cm6:6601,6701~6705;6602,6711~6715;6603,6721~6725"
- "shard@0:54~59;adhocbak.kgb.cm6:6701~6705;6711~6715;6721~6725"
- ""
- "##__acker,heartbeat,merge:0他们共用同一个端口,记住要时时刻刻,他们共享的都是同一个端口,别拆开了##"
- "__acker:0;adhocbak.kgb.cm6:6601;6602;6603"
- "heartbeat:0;adhocbak.kgb.cm6:6601;6602;6603"
- "merge:0;adhocbak.kgb.cm6:6601;6602;6603"
- ""
- "#######################################################################################################################"
- "####如果某台机器宕机了,任务无法在固定的分配都某一台,就需要使用下面的配置,宕机的任务会迁移到新的机器上####"
- "####注意这里的端口不要与上面的端口有重叠,某个任务也不要继续分配在上面的机器,因为那台机器已经宕机了####"
- "####要注意如果某一台机器宕机,他的任务要均匀的分布到集群其他机器上,不要全部迁移到同一台机器上####"
- "merge:2&shard@0:58,59,16,26,38;tiansuan1.kgb.cm4:6604,6721~6724;6605,6731~6734"
- "merge:3&shard@0:59,50,17,27,39;tiansuan2.kgb.cm4:6604,6721~6724;6605,6731~6734"
- "merge:4&shard@0:1,10,18,28,40,57;adhoc1.kgb.cm6:6604,6721~6725;6605,6731~6735"
- "shard@0:2,51,19,29,41;adhoc2.kgb.cm6:6604,6721~6724;6605,6731~6734"
- "merge:6&shard@0:3,52,20,30,42;adhoc3.kgb.cm6:6604,6721~6724;6605,6731~6734"
- "merge:7&shard@0:4,53,33,31,43;adhoc4.kgb.cm6:6604,6721~6724;6605,6731~6734"
- "merge:8&shard@0:6,11,21,32,44,56;adhoc5.kgb.cm6:6604,6721~6725;6605,6731~6735"
- "merge:9&shard@0:7,12,22,37,45,55;adhoc6.kgb.cm6:6604,6721~6725;6605,6731~6735"
- "merge:5&shard@0:8,13,23,34,46;adhoc7.kgb.cm6:6604,6721~6724;6605,6731~6734"
- "merge:10&shard@0:9,14,24,35,54,5,;adhoc8.kgb.cm6:6604,6721~6725;6605,6731~6735"
- "merge:1&shard@0:15,25,36,47,0;adhocbak.kgb.cm6:6604,6721~6725;6605,6731~6735"
- "##__acker,heartbeat,merge:0他们共用同一个端口,记住要时时刻刻,他们共享的都是同一个端口##"
- "__acker:0;adhoc2.kgb.cm6:6604;6605"
- "heartbeat:0;adhoc2.kgb.cm6:6604;6605"
- "merge:0;adhoc2.kgb.cm6:6604;6605"
*/
public class MdrillDefaultTaskAssignment implements IAssignment {
private static Logger LOG = Logger.getLogger(MdrillDefaultTaskAssignment.class);
public static String MDRILL_ASSIGNMENT_DEFAULT = "mdrill.task.assignment";
public static String MDRILL_ASSIGNMENT_PORTS = "mdrill.task.ports";
private HashMap<TaskIndex,ArrayList<HostPort>> taskIndexAssign=new HashMap<TaskIndex, ArrayList<HostPort>>();
private HashMap<TaskId,Integer> taskId2Index=new HashMap<TaskId, Integer>();
private HashMap<Integer,TaskInfo> taskId2TaskInfo=new HashMap<Integer,TaskInfo>();
private HashSet<Integer> allowPorts=new HashSet<Integer>();
private HashSet<HostPort> hostports=new HashSet<HostPort>();
private Map<String, SupervisorInfo> supInfos;
private Map topology_conf;
private StormClusterState zkCluster;
private String topologyId;
private int reassign_num=0;
@Override
public void setup(Map topology_conf, String topologyId,
StormClusterState zkCluster,
Map<NodePort, List<Integer>> keepAssigned,
Map<String, SupervisorInfo> supInfos) {
LOG.info("assignment "+MdrillDefaultTaskAssignment.class.getCanonicalName()+" setup "+topologyId);
this.topology_conf=topology_conf;
this.supInfos = supInfos;
this.zkCluster = zkCluster;
this.topologyId = topologyId;
}
public Map<NodePort, List<Integer>> keeperSlots(
Map<NodePort, List<Integer>> aliveAssigned, int numTaskIds,
int numWorkers)
{
Map<NodePort, List<Integer>> rtn=new HashMap<NodePort, List<Integer>>();
rtn.putAll(aliveAssigned);
return rtn;
}
@Override
public List<NodePort> slotsAssignment(List<NodePort> freedSlots,
int reassign_num, Set<Integer> reassignIds) {
this.reassign_num=reassign_num;
return freedSlots;
}
@Override
public Map<Integer, NodePort> tasksAssignment(List<NodePort> reassignSlots,
Set<Integer> reassignIds) {
Map<Integer, NodePort> rtn = new HashMap<Integer, NodePort>();
if(reassignIds.size()<=0)
{
return rtn;
}
this.initAssignment();
this.taskIdToIndex();
LOG.info("taskId2Index "+taskId2Index.toString());
LOG.info("taskIndexAssign "+taskIndexAssign.toString());
LOG.info("taskId2TaskInfo "+taskId2TaskInfo.toString());
LOG.info("hostports "+hostports.toString());
LOG.info("supInfos "+supInfos.toString());
ArrayList<Integer> nonAssignTask=new ArrayList<Integer>();
ArrayList<NodePort> nonAssignNodePort=new ArrayList<NodePort>();
HashSet<NodePort> UsedNodePort=new HashSet<NodePort>();
HashMap<HostPort,NodePort> host2node=new HashMap<HostPort, NodePort>();
for(NodePort p:reassignSlots)
{
SupervisorInfo info=this.supInfos.get(p.getNode());
if(info!=null)
{
HostPort hp=new HostPort(info.getHostName(), p.getPort());
if(hostports.contains(hp))
{
host2node.put(hp,p);
continue;
}
}
if(!this.allowPorts.contains(p.getPort()))
{
continue;
}
nonAssignNodePort.add(p);
}
for(Integer task:reassignIds)
{
TaskInfo info=taskId2TaskInfo.get(task);
if(info==null)
{
nonAssignTask.add(task);
LOG.info("can`t find task:"+task+","+this.topologyId);
continue;
}
String componname=info.getComponentId();
Integer index=taskId2Index.get(new TaskId(componname, task));
if(index==null)
{
nonAssignTask.add(task);
LOG.info("can`t find taskindex:"+task+","+this.topologyId+",index:"+index);
continue;
}
ArrayList<HostPort> taskAssignList=taskIndexAssign.get(new TaskIndex(componname, index));
if(taskAssignList==null||taskAssignList.size()==0)
{
nonAssignTask.add(task);
continue;
}
boolean isbreak=false;
for(HostPort hp:taskAssignList)
{
NodePort np=host2node.get(hp);
if(np!=null)
{
UsedNodePort.add(np);
LOG.info("assign:task:"+componname+"@"+task+"@"+index+",HostPort:"+hp.toString()+","+",NodePort:"+np.toString());
rtn.put(task, np);
isbreak=true;
break;
}
}
if(isbreak)
{
continue;
}
nonAssignTask.add(task);
}
if (nonAssignTask.size() > 0 ) {
int left=this.reassign_num-UsedNodePort.size();
ArrayList<NodePort> randomList=new ArrayList<NodePort>();
if(left>0)
{
List<NodePort> sortlist=sortSlots(nonAssignNodePort);
randomList.addAll( sortlist.subList(0, Math.min(left, sortlist.size())));
}
if(randomList.size()>0)
{
LOG.info("random assign:nonAssignTask:"+nonAssignTask.toString()+",randomList:"+randomList.toString());
rtn.putAll(this.randAssign(randomList, nonAssignTask,rtn));
}else{
LOG.error("nofreeNode"+nonAssignTask.toString()+">>"+reassignSlots.toString());
}
}
return rtn;
}
public static List<NodePort> sortSlots(ArrayList<NodePort> allSlots) {
List<NodePort> sortedFreeSlots=null;
if(allSlots!=null){
Map<String, List<NodePort>> tmp = new HashMap<String, List<NodePort>>();
// group by first,按照node来分类
for (Iterator<NodePort> it = allSlots.iterator(); it.hasNext();) {
NodePort np =it.next();
if (tmp.containsKey(np.getNode())) {
List<NodePort> lst = tmp.get(np.getNode());
lst.add(np);
tmp.put(np.getNode(), lst);
} else {
List<NodePort> lst = new ArrayList<NodePort>();
lst.add(np);
tmp.put(np.getNode(), lst);
}
}
// interleave
List<List<NodePort>> splitup=new ArrayList<List<NodePort>>(tmp.values());
sortedFreeSlots = StormUtils.interleave_all(splitup);
}
return sortedFreeSlots;
}
private Map<Integer, NodePort> randAssign(ArrayList<NodePort> randomNP,ArrayList<Integer> nonassign,Map<Integer, NodePort> assignRef)
{
Map<NodePort,ArrayList<Integer>> reref = new HashMap<NodePort,ArrayList<Integer>>();
for(Entry<Integer, NodePort> e:assignRef.entrySet())
{
NodePort np=e.getValue();
ArrayList<Integer> tlis=reref.get(np);
if(tlis==null)
{
tlis=new ArrayList<Integer>();
reref.put(np, tlis);
}
tlis.add(e.getKey());
}
Map<Integer, NodePort> rtn = new HashMap<Integer, NodePort>();
int index = 0;
if (nonassign != null && randomNP != null && randomNP.size() != 0) {
for (Integer task : nonassign) {
while(true)
{
if (index >= randomNP.size()) {
index = 0;
}
NodePort np=randomNP.get(index);
ArrayList<Integer> tlis=reref.get(np);
if(tlis!=null&&tlis.size()>0)
{
tlis.remove(0);
index++;
continue;
}
rtn.put(task, np);
index++;
break;
}
}
}
return rtn;
}
@Override
public void cleanup() {
}
private void taskIdToIndex()
{
Set<Integer> allTaskIds = StormUtils.listToSet(this.zkCluster.task_ids(this.topologyId));
HashSet<String> componnames=new HashSet<String>();
HashMap<String,ArrayList<Integer>> componname2Ids=new HashMap<String,ArrayList<Integer>>();
for (Integer tid : allTaskIds) {
TaskInfo info = this.zkCluster.task_info(this.topologyId, tid);
if (info == null) {
LOG.info("can`t find TaskInfo "+this.topologyId+","+ tid);
continue;
}
taskId2TaskInfo.put(tid, info);
String componentId=info.getComponentId();
componnames.add(componentId);
ArrayList<Integer> tasks=componname2Ids.get(componentId);
if(tasks==null)
{
tasks=new ArrayList<Integer>();
componname2Ids.put(componentId, tasks);
}
tasks.add(tid);
}
for(String componentId:componnames)
{
ArrayList<Integer> tasks=componname2Ids.get(componentId);
Collections.sort(tasks);
for(int j=0; j<tasks.size(); j++) {
taskId2Index.put(new TaskId(componentId, tasks.get(j)), j);
}
}
}
private ArrayList<TaskIndex> parseTaskIndexList(String taskIndexListstr)
{
ArrayList<TaskIndex> taskIndexList=new ArrayList<TaskIndex>();
String[] comp_taskIndexs=taskIndexListstr.split("&");
for(String comp:comp_taskIndexs)
{
String[] taskList=comp.split(":");
if(taskList.length<2)
{
continue;
}
String componname=taskList[0];
ArrayList<Integer> tasks=this.parseInts(taskList[1]);
for(Integer index:tasks)
{
taskIndexList.add(new TaskIndex(componname, index));
}
}
return taskIndexList;
}
private ArrayList<HostPort> parsePerHostPort(String hostportstr,ArrayList<HostPort> prev_hostports)
{
String[] hostSetlist=hostportstr.split("&");
ArrayList<HostPort> hostports=new ArrayList<HostPort>();
int hostports_index=0;
for(String subhost:hostSetlist)
{
if(subhost==null||subhost.isEmpty())
{
continue;
}
String[] hostPorts=subhost.split(":");
boolean isUsedPrefHost=false;
if(hostPorts.length<1)
{
continue;
}
if(hostPorts.length==1)
{
isUsedPrefHost=true;
}
String hostname=null;
ArrayList<Integer> ports=null;
if(isUsedPrefHost)
{
hostname="prev";
ports=this.parseInts(hostPorts[0]);
}else{
hostname=hostPorts[0];
ports=this.parseInts(hostPorts[1]);
}
if(ports.size()==0)
{
continue;
}
for(Integer p:ports)
{
if(isUsedPrefHost)
{
String privhostname=null;
if(prev_hostports!=null)
{
HostPort prevHostport=prev_hostports.get(hostports_index);
if(prevHostport!=null)
{
privhostname=prevHostport.hostname;
}
}
if(privhostname==null)
{
privhostname="mdrill_unset_hostname";
}
hostports.add(new HostPort(privhostname, p));
}else{
hostports.add(new HostPort(hostname, p));
}
hostports_index++;
}
}
return hostports;
}
private void setEachAssignment(String ass)
{
String[] cols=ass.split(";");
if(cols.length<2)
{
return ;
}
ArrayList<TaskIndex> taskIndexList=this.parseTaskIndexList(cols[0]);
if(taskIndexList.size()==0)
{
return ;
}
ArrayList<HostPort> prev_hostports=null;
for(int j=1;j<cols.length;j++)
{
if(cols[j]==null||cols[j].isEmpty())
{
continue;
}
ArrayList<HostPort> hostports=this.parsePerHostPort(cols[j], prev_hostports);
prev_hostports=hostports;
int index=0;
int maxsize=hostports.size();
for(TaskIndex task:taskIndexList)
{
if(index>=maxsize)
{
index=0;
}
HostPort hp=hostports.get(index);
index++;
ArrayList<HostPort> list=this.taskIndexAssign.get(task);
if(list==null)
{
list=new ArrayList<HostPort>();
this.taskIndexAssign.put(task, list);
}
list.add(hp);
this.hostports.add(hp);
}
}
}
private void initAssignment()
{
Object allowPortsstr=topology_conf.get(MDRILL_ASSIGNMENT_PORTS);
if(allowPortsstr!=null)
{
ArrayList<Integer> ports=this.parseInts(String.valueOf(allowPortsstr));
this.allowPorts.addAll(ports);
}
List<String> assignment=(List<String>) topology_conf.get(MDRILL_ASSIGNMENT_DEFAULT);
if(assignment==null)
{
return ;
}
for(String ass:assignment)
{
if(ass==null||ass.isEmpty()||ass.startsWith("#"))
{
continue;
}
this.setEachAssignment(ass);
}
}
private ArrayList<Integer> parseInts(String str)
{
ArrayList<Integer> rtn=new ArrayList<Integer>();
String[] list=str.split(",");
for(String s:list)
{
String[] spans=s.split("~");
int begin=Integer.parseInt(spans[0]);
int end=begin;
if(spans.length>1)
{
end=Integer.parseInt(spans[1]);
}
for(int i=begin;i<=end;i++)
{
rtn.add(i);
}
}
return rtn;
}
public static class TaskIndex{
private String componname;
private int index;
public TaskIndex(String componname, int index) {
this.componname = componname;
this.index = index;
}
@Override
public String toString() {
return "TaskIndex [componname=" + componname + ", index=" + index
+ "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((componname == null) ? 0 : componname.hashCode());
result = prime * result + index;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TaskIndex other = (TaskIndex) obj;
if (componname == null) {
if (other.componname != null)
return false;
} else if (!componname.equals(other.componname))
return false;
if (index != other.index)
return false;
return true;
}
}
public static class TaskId{
private String componname;
private int tid;
public TaskId(String componname, int index) {
this.componname = componname;
this.tid = index;
}
@Override
public String toString() {
return "TaskId [componname=" + componname + ", tid=" + tid + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((componname == null) ? 0 : componname.hashCode());
result = prime * result + tid;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TaskId other = (TaskId) obj;
if (componname == null) {
if (other.componname != null)
return false;
} else if (!componname.equals(other.componname))
return false;
if (tid != other.tid)
return false;
return true;
}
}
public static class HostPort{
private String hostname;
private int port;
public HostPort(String hostnme, int port) {
this.hostname = hostnme;
this.port = port;
}
@Override
public String toString() {
return "HostPort [hostname=" + hostname + ", port=" + port + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((hostname == null) ? 0 : hostname.hashCode());
result = prime * result + port;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HostPort other = (HostPort) obj;
if (hostname == null) {
if (other.hostname != null)
return false;
} else if (!hostname.equals(other.hostname))
return false;
if (port != other.port)
return false;
return true;
}
}
}