Aaron Wang
1 year ago
committed by
GitHub
14 changed files with 342 additions and 92 deletions
@ -0,0 +1,24 @@
|
||||
/* |
||||
* 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 org.apache.dolphinscheduler.common.enums; |
||||
|
||||
public enum ServerStatus { |
||||
|
||||
NORMAL, ABNORMAL, BUSY |
||||
|
||||
} |
@ -0,0 +1,35 @@
|
||||
/* |
||||
* 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 org.apache.dolphinscheduler.server.master.registry; |
||||
|
||||
import org.apache.dolphinscheduler.common.model.MasterHeartBeat; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* The listener used in {@link ServerNodeManager} to notify the change of master info. |
||||
*/ |
||||
public interface MasterInfoChangeListener { |
||||
|
||||
/** |
||||
* Used to notify the change of master info. |
||||
* |
||||
* @param masterNodeInfo master node info map, key is master address, value is master info. |
||||
*/ |
||||
void notify(Map<String, MasterHeartBeat> masterNodeInfo); |
||||
} |
@ -0,0 +1,116 @@
|
||||
/* |
||||
* 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 org.apache.dolphinscheduler.server.master.registry; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.ServerStatus; |
||||
import org.apache.dolphinscheduler.common.model.MasterHeartBeat; |
||||
import org.apache.dolphinscheduler.common.model.Server; |
||||
import org.apache.dolphinscheduler.server.master.config.MasterConfig; |
||||
import org.apache.dolphinscheduler.service.queue.MasterPriorityQueue; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.locks.Lock; |
||||
import java.util.concurrent.locks.ReentrantLock; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
@Slf4j |
||||
public class MasterSlotManager { |
||||
|
||||
@Autowired |
||||
protected ServerNodeManager serverNodeManager; |
||||
|
||||
@Autowired |
||||
protected MasterConfig masterConfig; |
||||
|
||||
private volatile int currentSlot = 0; |
||||
private volatile int totalSlot = 0; |
||||
|
||||
@PostConstruct |
||||
public void init() { |
||||
serverNodeManager.addMasterInfoChangeListener(new MasterSlotManager.SlotChangeListener()); |
||||
} |
||||
|
||||
public int getSlot() { |
||||
return currentSlot; |
||||
} |
||||
|
||||
public int getMasterSize() { |
||||
return totalSlot; |
||||
} |
||||
|
||||
public class SlotChangeListener implements MasterInfoChangeListener { |
||||
|
||||
private final Lock slotLock = new ReentrantLock(); |
||||
|
||||
private final MasterPriorityQueue masterPriorityQueue = new MasterPriorityQueue(); |
||||
|
||||
@Override |
||||
public void notify(Map<String, MasterHeartBeat> masterNodeInfo) { |
||||
List<Server> serverList = masterNodeInfo.values().stream() |
||||
.filter(heartBeat -> !heartBeat.getServerStatus().equals(ServerStatus.ABNORMAL)) |
||||
.map(this::convertHeartBeatToServer).collect(Collectors.toList()); |
||||
syncMasterNodes(serverList); |
||||
} |
||||
|
||||
/** |
||||
* sync master nodes |
||||
*/ |
||||
private void syncMasterNodes(List<Server> masterNodes) { |
||||
slotLock.lock(); |
||||
try { |
||||
this.masterPriorityQueue.clear(); |
||||
this.masterPriorityQueue.putAll(masterNodes); |
||||
int tempCurrentSlot = masterPriorityQueue.getIndex(masterConfig.getMasterAddress()); |
||||
int tempTotalSlot = masterNodes.size(); |
||||
if (tempCurrentSlot < 0) { |
||||
totalSlot = 0; |
||||
currentSlot = 0; |
||||
log.warn("Current master is not in active master list"); |
||||
} else if (tempCurrentSlot != currentSlot || tempTotalSlot != totalSlot) { |
||||
totalSlot = tempTotalSlot; |
||||
currentSlot = tempCurrentSlot; |
||||
log.info("Update master nodes, total master size: {}, current slot: {}", totalSlot, currentSlot); |
||||
} |
||||
} finally { |
||||
slotLock.unlock(); |
||||
} |
||||
} |
||||
|
||||
private Server convertHeartBeatToServer(MasterHeartBeat masterHeartBeat) { |
||||
Server server = new Server(); |
||||
server.setCreateTime(new Date(masterHeartBeat.getStartupTime())); |
||||
server.setLastHeartbeatTime(new Date(masterHeartBeat.getReportTime())); |
||||
server.setId(masterHeartBeat.getProcessId()); |
||||
server.setHost(masterHeartBeat.getHost()); |
||||
server.setPort(masterHeartBeat.getPort()); |
||||
|
||||
return server; |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,94 @@
|
||||
/* |
||||
* 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 org.apache.dolphinscheduler.server.master.registry; |
||||
|
||||
import org.apache.dolphinscheduler.common.enums.ServerStatus; |
||||
import org.apache.dolphinscheduler.common.model.MasterHeartBeat; |
||||
import org.apache.dolphinscheduler.server.master.config.MasterConfig; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.jupiter.api.Assertions; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.api.extension.ExtendWith; |
||||
import org.mockito.InjectMocks; |
||||
import org.mockito.Mock; |
||||
import org.mockito.Mockito; |
||||
import org.mockito.junit.jupiter.MockitoExtension; |
||||
|
||||
@ExtendWith(MockitoExtension.class) |
||||
public class MasterSlotManagerTest { |
||||
|
||||
@InjectMocks |
||||
private MasterSlotManager masterSlotManager = Mockito.spy(new MasterSlotManager()); |
||||
|
||||
@Mock |
||||
private MasterConfig masterConfig; |
||||
|
||||
@Test |
||||
void testNormalMasterSlots() { |
||||
// on normal Master side
|
||||
Mockito.when(masterConfig.getMasterAddress()).thenReturn("127.0.0.1:7777"); |
||||
|
||||
sendHeartBeat(ServerStatus.ABNORMAL, ServerStatus.NORMAL); |
||||
Assertions.assertEquals(1, masterSlotManager.getMasterSize()); |
||||
Assertions.assertEquals(0, masterSlotManager.getSlot()); |
||||
|
||||
sendHeartBeat(ServerStatus.NORMAL, ServerStatus.NORMAL); |
||||
Assertions.assertEquals(2, masterSlotManager.getMasterSize()); |
||||
Assertions.assertEquals(1, masterSlotManager.getSlot()); |
||||
} |
||||
|
||||
@Test |
||||
void testOverloadMasterSlots() { |
||||
// on abnormal Master side
|
||||
Mockito.when(masterConfig.getMasterAddress()).thenReturn("127.0.0.1:6666"); |
||||
|
||||
sendHeartBeat(ServerStatus.ABNORMAL, ServerStatus.NORMAL); |
||||
Assertions.assertEquals(0, masterSlotManager.getMasterSize()); |
||||
Assertions.assertEquals(0, masterSlotManager.getSlot()); |
||||
|
||||
sendHeartBeat(ServerStatus.NORMAL, ServerStatus.NORMAL); |
||||
Assertions.assertEquals(2, masterSlotManager.getMasterSize()); |
||||
Assertions.assertEquals(0, masterSlotManager.getSlot()); |
||||
} |
||||
|
||||
public void sendHeartBeat(ServerStatus serverStatus1, ServerStatus serverStatus2) { |
||||
MasterSlotManager.SlotChangeListener slotChangeListener = masterSlotManager.new SlotChangeListener(); |
||||
|
||||
Map<String, MasterHeartBeat> masterNodeInfo = new HashMap<>(); |
||||
// generate heartbeat
|
||||
MasterHeartBeat masterHeartBeat1 = MasterHeartBeat.builder() |
||||
.startupTime(System.currentTimeMillis()) |
||||
.serverStatus(serverStatus1) |
||||
.host("127.0.0.1") |
||||
.port(6666) |
||||
.build(); |
||||
MasterHeartBeat masterHeartBeat2 = MasterHeartBeat.builder() |
||||
.startupTime(System.currentTimeMillis()) |
||||
.serverStatus(serverStatus2) |
||||
.host("127.0.0.1") |
||||
.port(7777) |
||||
.build(); |
||||
masterNodeInfo.put("127.0.0.1:6666", masterHeartBeat1); |
||||
masterNodeInfo.put("127.0.0.1:7777", masterHeartBeat2); |
||||
|
||||
slotChangeListener.notify(masterNodeInfo); |
||||
} |
||||
} |
Loading…
Reference in new issue