diff --git a/common/build.gradle b/common/build.gradle index 6ce3c0bef41..5c70edf0b65 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -53,7 +53,7 @@ dependencies { compile 'org.aspectj:aspectjrt:1.8.13' compile 'org.aspectj:aspectjweaver:1.8.13' compile 'org.aspectj:aspectjtools:1.8.13' - compile group: 'com.github.tronprotocol', name: 'libp2p', version: 'test-v0.2.19' + compile group: 'com.github.tronprotocol', name: 'libp2p', version: 'test-v0.2.20' compile project(":protocol") } diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index e9f5e53531a..5e5da21baa6 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -31,6 +31,7 @@ public class CommonParameter { public static CommonParameter PARAMETER = new CommonParameter(); @Setter public static boolean ENERGY_LIMIT_HARD_FORK = false; + @Getter @Parameter(names = {"-c", "--config"}, description = "Config file (default:config.conf)") public String shellConfFileName = ""; @Getter @@ -633,6 +634,14 @@ public class CommonParameter { @Setter public long dynamicEnergyMaxFactor = 0L; + @Getter + @Setter + public boolean dynamicConfigEnable; + + @Getter + @Setter + public long dynamicConfigCheckInterval; + private static double calcMaxTimeRatio() { //return max(2.0, min(5.0, 5 * 4.0 / max(Runtime.getRuntime().availableProcessors(), 1))); return 5.0; diff --git a/common/src/main/java/org/tron/core/Constant.java b/common/src/main/java/org/tron/core/Constant.java index 6db4e16f3f8..2f08df3e931 100644 --- a/common/src/main/java/org/tron/core/Constant.java +++ b/common/src/main/java/org/tron/core/Constant.java @@ -365,4 +365,7 @@ public class Constant { public static final String NODE_SHUTDOWN_BLOCK_COUNT = "node.shutdown.BlockCount"; public static final String BLOCK_CACHE_TIMEOUT = "node.blockCacheTimeout"; + + public static final String DYNAMIC_CONFIG_ENABLE = "node.dynamicConfig.enable"; + public static final String DYNAMIC_CONFIG_CHECK_INTERVAL = "node.dynamicConfig.checkInterval"; } diff --git a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java index 78395f63a78..0e38e97baaf 100644 --- a/framework/src/main/java/org/tron/common/application/ApplicationImpl.java +++ b/framework/src/main/java/org/tron/common/application/ApplicationImpl.java @@ -7,6 +7,7 @@ import org.tron.common.parameter.CommonParameter; import org.tron.core.ChainBaseManager; import org.tron.core.config.args.Args; +import org.tron.core.config.args.DynamicArgs; import org.tron.core.consensus.ConsensusService; import org.tron.core.db.Manager; import org.tron.core.metrics.MetricsUtil; @@ -32,6 +33,9 @@ public class ApplicationImpl implements Application { @Autowired private ConsensusService consensusService; + @Autowired + private DynamicArgs dynamicArgs; + @Override public void setOptions(Args args) { // not used @@ -62,6 +66,7 @@ public void startup() { } consensusService.start(); MetricsUtil.init(); + dynamicArgs.init(); } @Override @@ -80,6 +85,7 @@ public void shutdown() { dbManager.stopRePushTriggerThread(); EventPluginLoader.getInstance().stopPlugin(); dbManager.stopFilterProcessThread(); + dynamicArgs.close(); logger.info("******** end to shutdown ********"); FullNode.shutDownSign = true; } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 39d241ab7a0..858f0d42d86 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -228,6 +228,8 @@ public static void clearParam() { PARAMETER.rateLimiterGlobalQps = 50000; PARAMETER.rateLimiterGlobalIpQps = 10000; PARAMETER.p2pDisable = false; + PARAMETER.dynamicConfigEnable = false; + PARAMETER.dynamicConfigCheckInterval = 600; } /** @@ -1147,6 +1149,18 @@ public static void setParam(final String[] args, final String confFileName) { Math.max(PARAMETER.dynamicEnergyMaxFactor, 0); } + PARAMETER.dynamicConfigEnable = config.hasPath(Constant.DYNAMIC_CONFIG_ENABLE) + && config.getBoolean(Constant.DYNAMIC_CONFIG_ENABLE); + if (config.hasPath(Constant.DYNAMIC_CONFIG_CHECK_INTERVAL)) { + PARAMETER.dynamicConfigCheckInterval + = config.getLong(Constant.DYNAMIC_CONFIG_CHECK_INTERVAL); + if (PARAMETER.dynamicConfigCheckInterval <= 0) { + PARAMETER.dynamicConfigCheckInterval = 600; + } + } else { + PARAMETER.dynamicConfigCheckInterval = 600; + } + logConfig(); } @@ -1199,7 +1213,7 @@ private static RateLimiterInitialization getRateLimiterFromConfig( return initialization; } - private static List getInetSocketAddress( + public static List getInetSocketAddress( final com.typesafe.config.Config config, String path, boolean filter) { List ret = new ArrayList<>(); if (!config.hasPath(path)) { @@ -1227,7 +1241,7 @@ private static List getInetSocketAddress( return ret; } - private static List getInetAddress( + public static List getInetAddress( final com.typesafe.config.Config config, String path) { List ret = new ArrayList<>(); if (!config.hasPath(path)) { diff --git a/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java b/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java new file mode 100644 index 00000000000..cbf167cb955 --- /dev/null +++ b/framework/src/main/java/org/tron/core/config/args/DynamicArgs.java @@ -0,0 +1,114 @@ +package org.tron.core.config.args; + +import static org.apache.commons.lang3.StringUtils.isNoneBlank; + +import com.typesafe.config.Config; +import java.io.File; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.tron.common.parameter.CommonParameter; +import org.tron.core.Constant; +import org.tron.core.config.Configuration; +import org.tron.core.net.TronNetService; + + +@Slf4j(topic = "app") +@Component +public class DynamicArgs { + private final CommonParameter parameter = Args.getInstance(); + + private long lastModified = 0; + + private ScheduledExecutorService reloadExecutor = Executors.newSingleThreadScheduledExecutor(); + + public void init() { + if (parameter.isDynamicConfigEnable()) { + logger.info("Start the dynamic loading configuration service"); + long checkInterval = parameter.getDynamicConfigCheckInterval(); + File config = getConfigFile(); + if (config == null) { + return; + } + lastModified = config.lastModified(); + reloadExecutor.scheduleWithFixedDelay(() -> { + try { + run(); + } catch (Exception e) { + logger.error("Exception caught when reloading configuration", e); + } + }, 10, checkInterval, TimeUnit.SECONDS); + } + } + + public void run() { + File config = getConfigFile(); + if (config != null) { + long lastModifiedTime = config.lastModified(); + if (lastModifiedTime > lastModified) { + reload(); + lastModified = lastModifiedTime; + } + } + } + + private File getConfigFile() { + String confFilePath; + if (isNoneBlank(parameter.getShellConfFileName())) { + confFilePath = parameter.getShellConfFileName(); + } else { + confFilePath = Constant.TESTNET_CONF; + } + + File confFile = new File(confFilePath); + if (!confFile.exists()) { + logger.warn("Configuration path is required! No such file {}", confFile); + return null; + } + return confFile; + } + + public void reload() { + logger.debug("Reloading ... "); + Config config = Configuration.getByFileName(parameter.getShellConfFileName(), + Constant.TESTNET_CONF); + + updateActiveNodes(config); + + updateTrustNodes(config); + } + + private void updateActiveNodes(Config config) { + List newActiveNodes = + Args.getInetSocketAddress(config, Constant.NODE_ACTIVE, true); + parameter.setActiveNodes(newActiveNodes); + List activeNodes = TronNetService.getP2pConfig().getActiveNodes(); + activeNodes.clear(); + activeNodes.addAll(newActiveNodes); + logger.debug("p2p active nodes : {}", + TronNetService.getP2pConfig().getActiveNodes().toString()); + } + + private void updateTrustNodes(Config config) { + List newPassiveNodes = Args.getInetAddress(config, Constant.NODE_PASSIVE); + parameter.setPassiveNodes(newPassiveNodes); + List trustNodes = TronNetService.getP2pConfig().getTrustNodes(); + trustNodes.clear(); + trustNodes.addAll(newPassiveNodes); + parameter.getActiveNodes().forEach(n -> trustNodes.add(n.getAddress())); + parameter.getFastForwardNodes().forEach(f -> trustNodes.add(f.getAddress())); + logger.debug("p2p trust nodes : {}", + TronNetService.getP2pConfig().getTrustNodes().toString()); + } + + public void close() { + logger.info("Closing the dynamic loading configuration service"); + reloadExecutor.shutdown(); + } +} \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/config/args/DynamicArgsTest.java b/framework/src/test/java/org/tron/core/config/args/DynamicArgsTest.java new file mode 100644 index 00000000000..2ca95239ad7 --- /dev/null +++ b/framework/src/test/java/org/tron/core/config/args/DynamicArgsTest.java @@ -0,0 +1,71 @@ +package org.tron.core.config.args; + +import java.io.File; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.tron.common.application.TronApplicationContext; +import org.tron.common.parameter.CommonParameter; +import org.tron.common.utils.FileUtil; +import org.tron.common.utils.ReflectUtils; +import org.tron.core.Constant; +import org.tron.core.config.DefaultConfig; +import org.tron.core.net.TronNetService; +import org.tron.p2p.P2pConfig; + +public class DynamicArgsTest { + protected TronApplicationContext context; + private DynamicArgs dynamicArgs; + private String dbPath = "output-dynamic-config-test"; + + @Before + public void init() { + Args.setParam(new String[]{"--output-directory", dbPath}, + Constant.TEST_CONF); + context = new TronApplicationContext(DefaultConfig.class); + dynamicArgs = context.getBean(DynamicArgs.class); + + } + + @After + public void destroy() { + Args.clearParam(); + context.destroy(); + FileUtil.deleteDir(new File(dbPath)); + } + + @Test + public void start() { + CommonParameter parameter = Args.getInstance(); + Assert.assertTrue(parameter.isDynamicConfigEnable()); + Assert.assertEquals(600, parameter.getDynamicConfigCheckInterval()); + + dynamicArgs.init(); + Assert.assertEquals(0, (long) ReflectUtils.getFieldObject(dynamicArgs, "lastModified")); + + TronNetService tronNetService = context.getBean(TronNetService.class); + ReflectUtils.setFieldValue(tronNetService, "p2pConfig", new P2pConfig()); + File config = new File(Constant.TESTNET_CONF); + if (!config.exists()) { + try { + config.createNewFile(); + } catch (Exception e) { + return; + } + dynamicArgs.run(); + try { + config.delete(); + } catch (Exception e) { + return; + } + } + try { + dynamicArgs.reload(); + } catch (Exception e) { + // no need to deal with + } + + dynamicArgs.close(); + } +} diff --git a/framework/src/test/resources/config-test.conf b/framework/src/test/resources/config-test.conf index d61e0eead64..76e99c8d4a1 100644 --- a/framework/src/test/resources/config-test.conf +++ b/framework/src/test/resources/config-test.conf @@ -340,4 +340,7 @@ vm = { } committee = { allowCreationOfContracts = 1 //mainnet:0 (reset by committee),test:1 -} \ No newline at end of file +} + +node.dynamicConfig.enable = true +node.dynamicConfig.checkInterval = 0 \ No newline at end of file