remove HandlerMapping.java and ability change call back

This commit is contained in:
Daydreamer-ia 2023-09-25 14:33:20 +08:00
parent c565a2bdd5
commit 41a3f13a66
5 changed files with 20 additions and 465 deletions

View File

@ -18,31 +18,17 @@ package com.alibaba.nacos.common.ability;
import com.alibaba.nacos.api.ability.constant.AbilityKey;
import com.alibaba.nacos.api.ability.initializer.AbilityPostProcessor;
import com.alibaba.nacos.api.ability.register.AbstractAbilityRegistry;
import com.alibaba.nacos.common.JustForTest;
import com.alibaba.nacos.common.ability.handler.HandlerMapping;
import com.alibaba.nacos.common.executor.ExecutorFactory;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.spi.NacosServiceLoader;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.MapUtil;
import com.alibaba.nacos.common.utils.ThreadUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* It is a capability control center, manager current node abilities or other control.
@ -54,25 +40,11 @@ public abstract class AbstractAbilityControlManager {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAbilityControlManager.class);
/**.
* These handlers will be invoked when the flag of ability change key:
* ability key from {@link com.alibaba.nacos.api.ability.constant.AbilityKey} value:
* components who want to be invoked if its interested ability turn on/off
*/
private final Map<AbilityKey, List<HandlerWithPriority>> handlerMappings = new ConcurrentHashMap<>();
/**.
* run for HandlerMapping
*/
private final Executor simpleThreadPool = ExecutorFactory.newSingleExecutorService();
/**.
* ability current node running
*/
protected final Map<String, Boolean> currentRunningAbility = new ConcurrentHashMap<>();
private final ReentrantLock lockForHandlerMappings = new ReentrantLock();
protected AbstractAbilityControlManager() {
ThreadUtils.addShutdownHook(this::destroy);
NotifyCenter.registerToPublisher(AbilityUpdateEvent.class, 16384);
@ -95,24 +67,32 @@ public abstract class AbstractAbilityControlManager {
return AbilityKey.mapStr(abilities);
}
/**.
* Turn on the ability whose key is <p>abilityKey</p>
/**
* Turn on the ability whose key is <p>abilityKey</p>.
*
* @param abilityKey ability key{@link AbilityKey}
* @return if turn success
*/
public boolean enableCurrentNodeAbility(AbilityKey abilityKey) {
return doTurn(true, abilityKey);
public void enableCurrentNodeAbility(AbilityKey abilityKey) {
doTurn(this.currentRunningAbility, abilityKey, true);
}
protected void doTurn(Map<String, Boolean> abilities, AbilityKey key, boolean turn) {
abilities.put(key.getName(), turn);
// notify event
AbilityUpdateEvent abilityUpdateEvent = new AbilityUpdateEvent();
abilityUpdateEvent.setTable(Collections.unmodifiableMap(currentRunningAbility));
abilityUpdateEvent.isOn = turn;
abilityUpdateEvent.abilityKey = key;
NotifyCenter.publishEvent(abilityUpdateEvent);
}
/**.
* Turn off the ability whose key is <p>abilityKey</p> {@link AbilityKey}
/**
* Turn off the ability whose key is <p>abilityKey</p> {@link AbilityKey}.
*
* @param abilityKey ability key
* @return if turn success
*/
public boolean disableCurrentNodeAbility(AbilityKey abilityKey) {
return doTurn(false, abilityKey);
public void disableCurrentNodeAbility(AbilityKey abilityKey) {
doTurn(this.currentRunningAbility, abilityKey, false);
}
/**.
@ -141,78 +121,11 @@ public abstract class AbstractAbilityControlManager {
return Collections.unmodifiableMap(currentRunningAbility);
}
/**.
* Turn on/off the ability of current node
*
* @param isOn is on
* @param abilityKey ability key from {@link AbilityKey}
* @return if turn success
*/
private boolean doTurn(boolean isOn, AbilityKey abilityKey) {
Boolean isEnabled = currentRunningAbility.get(abilityKey.getName());
// if not supporting this key
if (isEnabled == null) {
LOGGER.warn("[AbilityControlManager] Attempt to turn on/off a not existed ability!");
return false;
} else if (isOn == isEnabled) {
// if already turn on/off
return true;
}
// turn on/off
currentRunningAbility.put(abilityKey.getName(), isOn);
// handler mappings
triggerHandlerMappingAsyn(abilityKey, isOn, this.handlerMappings);
// notify event
AbilityUpdateEvent abilityUpdateEvent = new AbilityUpdateEvent();
abilityUpdateEvent.setTable(Collections.unmodifiableMap(currentRunningAbility));
abilityUpdateEvent.isOn = isOn;
abilityUpdateEvent.abilityKey = abilityKey;
NotifyCenter.publishEvent(abilityUpdateEvent);
return true;
}
/**
* Register the component which is managed by {@link AbstractAbilityControlManager}. if you are hoping that a
* component will be invoked when turn on/off the ability whose key is <p>abilityKey</p>.
*
* @param abilityKey component key.
* @param priority the higher the priority is, the faster it will be called.
* @param handlerMapping component instance.
*/
public void registerComponent(AbilityKey abilityKey, HandlerMapping handlerMapping, int priority) {
doRegisterComponent(abilityKey, handlerMapping, this.handlerMappings, lockForHandlerMappings, priority, currentRunningAbility);
}
/**.
* Register component with the lowest priority
*
* @param abilityKey ability key
* @param handlerMapping handler
*/
public void registerComponent(AbilityKey abilityKey, HandlerMapping handlerMapping) {
registerComponent(abilityKey, handlerMapping, -1);
}
/**.
* Remove the specific type handler for a certain ability
*
* @param abilityKey ability key
* @param handlerMappingClazz type
* @return the count of handlers are removed
*/
public int removeComponent(AbilityKey abilityKey, Class<? extends HandlerMapping> handlerMappingClazz) {
return doRemove(abilityKey, handlerMappingClazz, lockForHandlerMappings, handlerMappings);
}
/**.
* Close
*/
public final void destroy() {
LOGGER.warn("[DefaultAbilityControlManager] - Start destroying...");
((ThreadPoolExecutor) simpleThreadPool).shutdown();
if (MapUtil.isNotEmpty(handlerMappings)) {
handlerMappings.keySet().forEach(key -> doTriggerSyn(key, false, handlerMappings));
}
// hook
doDestroy();
LOGGER.warn("[DefaultAbilityControlManager] - Destruction of the end");
@ -225,127 +138,6 @@ public abstract class AbstractAbilityControlManager {
// for server ability manager
}
/**
* Remove the component instance of <p>handlerMappingClazz</p>.
*
* @param abilityKey ability key from {@link AbstractAbilityRegistry}
* @param handlerMappingClazz implement of {@link HandlerMapping}
* @param lock lock for operation
* @param handlerMappingsMap handler collection map
* @return the count of components have removed
*/
protected int doRemove(AbilityKey abilityKey, Class<? extends HandlerMapping> handlerMappingClazz, Lock lock,
Map<AbilityKey, List<HandlerWithPriority>> handlerMappingsMap) {
List<HandlerWithPriority> handlerMappings = handlerMappingsMap.get(abilityKey);
if (CollectionUtils.isEmpty(handlerMappings)) {
return 0;
}
lock.lock();
try {
AtomicInteger count = new AtomicInteger();
handlerMappings.removeIf(item -> {
if (item.handlerMapping.getClass().equals(handlerMappingClazz)) {
count.getAndIncrement();
return true;
}
return false;
});
return count.get();
} finally {
lock.unlock();
}
}
public int removeAll(AbilityKey abilityKey) {
List<HandlerWithPriority> remove = this.handlerMappings.remove(abilityKey);
return Optional.ofNullable(remove).orElse(Collections.emptyList()).size();
}
/**.
* Register the component into handlerMappings locking by lockForHandlerMappings to ensure concurrency security.
*
* @param abilityKey ability key
* @param handlerMapping component instance.
* @param handlerMappings container
* @param lockForHandlerMappings lock to ensure concurrency
* @param abilityTable behavioral basis of handler
*/
protected void doRegisterComponent(AbilityKey abilityKey, HandlerMapping handlerMapping,
Map<AbilityKey, List<HandlerWithPriority>> handlerMappings, Lock lockForHandlerMappings,
int priority, Map<String, Boolean> abilityTable) {
if (!currentRunningAbility.containsKey(abilityKey.getName())) {
LOGGER.warn("[AbilityHandlePostProcessor] Failed to register processor: {}, because illegal key!",
handlerMapping.getClass().getSimpleName());
}
// legal key
lockForHandlerMappings.lock();
try {
List<HandlerWithPriority> handlers = handlerMappings.getOrDefault(abilityKey, new CopyOnWriteArrayList<>());
HandlerWithPriority handlerWithPriority = new HandlerWithPriority(handlerMapping, priority);
handlers.add(handlerWithPriority);
handlerMappings.put(abilityKey, handlers);
// choose behavior
// enable default
if (abilityTable.getOrDefault(abilityKey.getName(), false)) {
handlerMapping.enable();
} else {
handlerMapping.disable();
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("[DefaultAbilityControlManager] Fail to register handler: {}", handlerMapping.getClass().getSimpleName());
} finally {
lockForHandlerMappings.unlock();
LOGGER.info("[DefaultAbilityControlManager] Successfully registered processor: {}",
handlerMapping.getClass().getSimpleName());
}
}
/**
* Invoke componments which linked to ability key asyn.
*
* @param key ability key from {@link AbstractAbilityRegistry}
* @param isEnabled turn on/off
* @param handlerMappingsMap handler collection
*/
protected void triggerHandlerMappingAsyn(AbilityKey key, boolean isEnabled,
Map<AbilityKey, List<HandlerWithPriority>> handlerMappingsMap) {
simpleThreadPool.execute(() -> doTriggerSyn(key, isEnabled, handlerMappingsMap));
}
/**
* Invoke componments which linked to ability key syn.
*
* @param key ability key from {@link AbstractAbilityRegistry}
* @param isEnabled turn on/off
* @param handlerMappingsMap handler collection
*/
protected void doTriggerSyn(AbilityKey key, boolean isEnabled,
Map<AbilityKey, List<HandlerWithPriority>> handlerMappingsMap) {
List<HandlerWithPriority> handlerWithPriorities = handlerMappingsMap.get(key);
// return if empty
if (CollectionUtils.isEmpty(handlerWithPriorities)) {
return;
}
Collections.sort(handlerWithPriorities);
// invoked all
handlerWithPriorities.forEach(handlerMappingWithPriorities -> {
// any error from current handler does not affect other handler
HandlerMapping handlerMapping = handlerMappingWithPriorities.handlerMapping;
try {
if (isEnabled) {
handlerMapping.enable();
} else {
handlerMapping.disable();
}
} catch (Throwable t) {
LOGGER.warn("[HandlerMapping] Failed to invoke {} :{}", handlerMapping.getClass().getSimpleName(),
t.getLocalizedMessage());
}
});
}
/**
* A legal nacos application has a ability control manager.
* If there are more than one, the one with higher priority is preferred
@ -353,40 +145,9 @@ public abstract class AbstractAbilityControlManager {
* @return priority
*/
public abstract int getPriority();
@JustForTest
protected Map<AbilityKey, List<HandlerWithPriority>> handlerMapping() {
return this.handlerMappings;
}
/**
* Support priority handler.
*/
protected class HandlerWithPriority implements Comparable<HandlerWithPriority> {
/**.
* Decorated
*/
public HandlerMapping handlerMapping;
/**.
* the higher the priority, the faster it will be called
*/
public int priority;
public HandlerWithPriority(HandlerMapping handlerMapping, int priority) {
this.handlerMapping = handlerMapping;
this.priority = priority;
}
@Override
public int compareTo(HandlerWithPriority o) {
return o.priority - this.priority;
}
}
/**.
* notify when current node ability changing
* notify when current node ability changing.
*/
public class AbilityUpdateEvent extends Event {

View File

@ -1,41 +0,0 @@
/*
* Copyright 1999-2022 Alibaba Group Holding Ltd.
*
* 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.alibaba.nacos.common.ability.handler;
/**.
* @author Daydreamer
* @description This component will be invoked if the ability of current node is turned on/off.
* @date 2022/7/12 19:21
**/
public interface HandlerMapping {
/**.
* It will be invoked in order to enable this component after update the
* ability table key to true
*/
default void enable() {
// Nothing to do!
}
/**.
* It will be invoked in order to disable this component after update the
* ability table key to false
*/
default void disable() {
// Nothing to do!
}
}

View File

@ -18,14 +18,12 @@ package com.alibaba.nacos.core.ability;
import com.alibaba.nacos.api.ability.constant.AbilityKey;
import com.alibaba.nacos.api.ability.constant.AbilityMode;
import com.alibaba.nacos.common.ability.handler.HandlerMapping;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
@ -34,10 +32,6 @@ public class AbilityControlManagerTest {
private TestServerAbilityControlManager serverAbilityControlManager = new TestServerAbilityControlManager();
private volatile int enabled = 0;
private volatile LinkedList<String> testPriority = new LinkedList<>();
@Before
public void inject() {
Map<String, Boolean> newTable = new HashMap<>();
@ -45,48 +39,6 @@ public class AbilityControlManagerTest {
serverAbilityControlManager.setCurrentSupportingAbility(newTable);
}
@Test
public void testComponent() throws InterruptedException {
enabled = 0;
// invoke enable() or disable() when registering
serverAbilityControlManager.registerComponent(AbilityKey.SERVER_TEST_1, new TestHandlerMapping(), -1);
Assert.assertEquals(1, serverAbilityControlManager.handlerMappingCount());
serverAbilityControlManager.enableCurrentNodeAbility(AbilityKey.SERVER_TEST_1);
// wait for invoking handler asyn
Thread.sleep(200L);
// nothing happens if it has enabled
Assert.assertEquals(enabled, 1);
Assert.assertTrue(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
// invoke disable()
serverAbilityControlManager.disableCurrentNodeAbility(AbilityKey.SERVER_TEST_1);
// wait for invoking handler asyn
Thread.sleep(200L);
// disable will invoke handler
Assert.assertEquals(enabled, 0);
Assert.assertFalse(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
serverAbilityControlManager.disableCurrentNodeAbility(AbilityKey.SERVER_TEST_1);
// wait for invoking handler asyn
Thread.sleep(200L);
// nothing to do because it has disable
Assert.assertEquals(enabled, 0);
Assert.assertFalse(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
serverAbilityControlManager.enableCurrentNodeAbility(AbilityKey.SERVER_TEST_1);
// wait for invoking handler asyn
Thread.sleep(200L);
Assert.assertEquals(enabled, 1);
Assert.assertTrue(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
serverAbilityControlManager.enableCurrentNodeAbility(AbilityKey.SERVER_TEST_1);
// wait for invoking handler asyn
Thread.sleep(200L);
Assert.assertEquals(enabled, 1);
Assert.assertTrue(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
}
@Test
public void testCurrentNodeAbility() {
Set<String> keySet = serverAbilityControlManager.getCurrentNodeAbilities().keySet();
@ -104,75 +56,6 @@ public class AbilityControlManagerTest {
});
}
@Test
public void testPriority() throws InterruptedException {
TestServerAbilityControlManager testClientAbilityControlManager = new TestServerAbilityControlManager();
AbilityKey key = AbilityKey.SERVER_TEST_1;
TestPriority handlerMapping1 = new TestPriority("1");
TestPriority handlerMapping2 = new TestPriority("2");
TestPriority handlerMapping3 = new TestPriority("3");
// first one, invoke enable()
testClientAbilityControlManager.registerComponent(key, handlerMapping2, 128);
// last one, invoke enable()
testClientAbilityControlManager.registerComponent(key, handlerMapping3);
// second one, invoke enable()
testClientAbilityControlManager.registerComponent(key, handlerMapping1, 12);
// trigger
testClientAbilityControlManager.trigger(key);
Assert.assertEquals(3, testClientAbilityControlManager.getHandlerMapping(key).size());
// wait for invoking
Thread.sleep(200L);
Assert.assertEquals("2", testPriority.poll());
Assert.assertEquals("3", testPriority.poll());
Assert.assertEquals("1", testPriority.poll());
// here are priority
Assert.assertEquals("2", testPriority.poll());
Assert.assertEquals("1", testPriority.poll());
Assert.assertEquals("3", testPriority.poll());
// remove
testClientAbilityControlManager.registerComponent(key, new TestHandlerMapping(), -1);
Assert.assertEquals(4, testClientAbilityControlManager.getHandlerMapping(key).size());
Assert.assertEquals(1, testClientAbilityControlManager.removeComponent(key, TestHandlerMapping.class));
Assert.assertEquals(3, testClientAbilityControlManager.getHandlerMapping(key).size());
testClientAbilityControlManager.removeAll(key);
Assert.assertNull(testClientAbilityControlManager.getHandlerMapping(key));
}
class TestPriority implements HandlerMapping {
String mark;
public TestPriority(String mark) {
// unique one
this.mark = mark.intern();
}
@Override
public void enable() {
testPriority.offer(mark);
}
@Override
public void disable() {
testPriority.offer(mark);
}
}
class TestHandlerMapping implements HandlerMapping {
@Override
public void enable() {
enabled++;
}
@Override
public void disable() {
enabled--;
}
}
}

View File

@ -16,11 +16,9 @@
package com.alibaba.nacos.core.ability;
import com.alibaba.nacos.api.ability.constant.AbilityKey;
import com.alibaba.nacos.common.JustForTest;
import com.alibaba.nacos.core.ability.control.ServerAbilityControlManager;
import java.util.List;
import java.util.Map;
public class TestServerAbilityControlManager extends ServerAbilityControlManager {
@ -30,20 +28,5 @@ public class TestServerAbilityControlManager extends ServerAbilityControlManager
currentRunningAbility.clear();
currentRunningAbility.putAll(ability);
}
@JustForTest
public int handlerMappingCount() {
return super.handlerMapping().size();
}
@JustForTest
public List<HandlerWithPriority> getHandlerMapping(AbilityKey abilityKey) {
return super.handlerMapping().get(abilityKey);
}
@JustForTest
public void trigger(AbilityKey abilityKey) {
triggerHandlerMappingAsyn(abilityKey, true, handlerMapping());
}
}

View File

@ -19,7 +19,6 @@ package com.alibaba.nacos.core.ability.config;
import com.alibaba.nacos.api.ability.constant.AbilityKey;
import com.alibaba.nacos.api.ability.register.AbstractAbilityRegistry;
import com.alibaba.nacos.api.ability.register.impl.ServerAbilities;
import com.alibaba.nacos.common.ability.handler.HandlerMapping;
import com.alibaba.nacos.common.event.ServerConfigChangeEvent;
import com.alibaba.nacos.core.ability.TestServerAbilityControlManager;
import com.alibaba.nacos.core.ability.control.ServerAbilityControlManager;
@ -45,8 +44,6 @@ public class AbilityConfigsTest {
private TestAbilityConfig abilityConfigs;
private int tmp;
private ServerAbilityControlManager serverAbilityControlManager;
private Map<AbilityKey, Boolean> currentAbilities;
@ -59,9 +56,6 @@ public class AbilityConfigsTest {
inject(abilityConfigs);
serverAbilityControlManager.enableCurrentNodeAbility(AbilityKey.SERVER_TEST_1);
serverAbilityControlManager.enableCurrentNodeAbility(AbilityKey.SERVER_TEST_2);
serverAbilityControlManager.registerComponent(AbilityKey.SERVER_TEST_1, new TestHandler());
serverAbilityControlManager.registerComponent(AbilityKey.SERVER_TEST_2, new TestHandler());
// tmp is 2 now
}
void inject(AbilityConfigs abilityConfigs) {
@ -118,47 +112,22 @@ public class AbilityConfigsTest {
abilityConfigs.onEvent(new ServerConfigChangeEvent());
Assert.assertTrue(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
Assert.assertTrue(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_2));
//wait for invoke
Thread.sleep(100);
Assert.assertEquals(tmp, 2);
// test change
environment.setProperty(AbilityConfigs.PREFIX + AbilityKey.SERVER_TEST_1.getName(), Boolean.FALSE.toString());
abilityConfigs.onEvent(new ServerConfigChangeEvent());
Assert.assertFalse(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
Assert.assertTrue(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_2));
//wait for invoke
Thread.sleep(100);
Assert.assertEquals(tmp, 1);
environment.setProperty(AbilityConfigs.PREFIX + AbilityKey.SERVER_TEST_1.getName(), Boolean.TRUE.toString());
abilityConfigs.onEvent(new ServerConfigChangeEvent());
Assert.assertTrue(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
//wait for invoke
Thread.sleep(100);
Assert.assertEquals(tmp, 2);
environment.setProperty(AbilityConfigs.PREFIX + AbilityKey.SERVER_TEST_1.getName(), Boolean.FALSE.toString());
environment.setProperty(AbilityConfigs.PREFIX + AbilityKey.SERVER_TEST_2.getName(), Boolean.FALSE.toString());
abilityConfigs.onEvent(new ServerConfigChangeEvent());
Assert.assertFalse(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_1));
Assert.assertFalse(serverAbilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_TEST_2));
//wait for invoke
Thread.sleep(100);
Assert.assertEquals(tmp, 0);
}
class TestHandler implements HandlerMapping {
@Override
public void enable() {
tmp++;
}
@Override
public void disable() {
tmp--;
}
}
}