diff --git a/core/src/main/java/com/alibaba/nacos/core/control/SpringValueConfigsInitializer.java b/core/src/main/java/com/alibaba/nacos/core/control/SpringValueConfigsInitializer.java new file mode 100644 index 00000000000..f3f14bbcaaa --- /dev/null +++ b/core/src/main/java/com/alibaba/nacos/core/control/SpringValueConfigsInitializer.java @@ -0,0 +1,76 @@ +/* + * Copyright 1999-2020 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.core.control; + +import com.alibaba.nacos.common.utils.StringUtils; +import com.alibaba.nacos.plugin.control.configs.ControlConfigs; +import com.alibaba.nacos.plugin.control.configs.ControlConfigsInitializer; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * spring value for control configs. + * + * @author shiyiyue + */ +@Component +public class SpringValueConfigsInitializer implements ControlConfigsInitializer { + + @Value("${nacos.plugin.control.tps.barrier.creator:nacos}") + private String tpsBarrierCreator = "nacos"; + + @Value("${nacos.plugin.control.tps.barrier.rule.creator:nacos}") + private String tpsRuleBarrierCreator = "nacos"; + + @Value("${nacos.plugin.control.connection.runtime.ejector:nacos}") + private String connectionRuntimeEjector = "nacos"; + + @Value("${nacos.plugin.control.connection.manager:nacos}") + private String connectionManager = "nacos"; + + @Value("${nacos.plugin.control.tps.manager:nacos}") + private String tpsManager = "nacos"; + + @Value("${nacos.plugin.control.rule.external.storage:}") + private String ruleExternalStorage = ""; + + @Value("${nacos.plugin.control.rule.parser:nacos}") + private String ruleParser = "nacos"; + + @Value("${nacos.plugin.control.rule.local.basedir:}") + private String localRuleStorageBaseDir = ""; + + @Override + public void initialize(ControlConfigs controlConfigs) { + controlConfigs.setTpsManager(tpsManager); + controlConfigs.setTpsBarrierCreator(tpsBarrierCreator); + controlConfigs.setTpsRuleBarrierCreator(tpsRuleBarrierCreator); + + controlConfigs.setConnectionRuntimeEjector(connectionRuntimeEjector); + controlConfigs.setConnectionManager(connectionManager); + + controlConfigs.setRuleParser(ruleParser); + if (StringUtils.isNotBlank(localRuleStorageBaseDir)) { + controlConfigs.setLocalRuleStorageBaseDir(localRuleStorageBaseDir); + } else { + controlConfigs.setLocalRuleStorageBaseDir(EnvUtil.getNacosHome()); + } + controlConfigs.setRuleExternalStorage(ruleExternalStorage); + + } +} diff --git a/plugin/control/pom.xml b/plugin/control/pom.xml index 85fbe02e433..561be746b84 100644 --- a/plugin/control/pom.xml +++ b/plugin/control/pom.xml @@ -36,10 +36,7 @@ nacos-common provided - - com.alibaba.nacos - nacos-sys - + \ No newline at end of file diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ControlManagerCenter.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ControlManagerCenter.java index 3109b7e54ab..e0eb81e9a2d 100644 --- a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ControlManagerCenter.java +++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ControlManagerCenter.java @@ -23,8 +23,8 @@ import com.alibaba.nacos.plugin.control.connection.nacos.NacosConnectionControlManager; import com.alibaba.nacos.plugin.control.event.ConnectionLimitRuleChangeEvent; import com.alibaba.nacos.plugin.control.event.TpsControlRuleChangeEvent; -import com.alibaba.nacos.plugin.control.ruleactivator.NacosRuleParser; import com.alibaba.nacos.plugin.control.ruleactivator.RuleParser; +import com.alibaba.nacos.plugin.control.ruleactivator.RuleParserProxy; import com.alibaba.nacos.plugin.control.ruleactivator.RuleStorageProxy; import com.alibaba.nacos.plugin.control.tps.TpsControlManager; import com.alibaba.nacos.plugin.control.tps.nacos.NacosTpsControlManager; @@ -44,46 +44,8 @@ public class ControlManagerCenter { private ConnectionControlManager connectionControlManager; - private RuleParser ruleParser; - private RuleStorageProxy ruleStorageProxy; - private void initRuleParser() { - Collection ruleParsers = NacosServiceLoader.load(RuleParser.class); - String ruleParserName = ControlConfigs.getInstance().getRuleParser(); - - for (RuleParser ruleParserInternal : ruleParsers) { - if (ruleParserInternal.getName().equalsIgnoreCase(ruleParserName)) { - Loggers.CONTROL.info("Found rule parser of name={},class={}", ruleParserName, - ruleParserInternal.getClass().getSimpleName()); - ruleParser = ruleParserInternal; - break; - } - } - if (ruleParser == null) { - Loggers.CONTROL.warn("Fail to rule parser of name :" + ruleParserName); - ruleParser = new NacosRuleParser(); - } - - Collection connectionControlManagers = NacosServiceLoader - .load(ConnectionControlManager.class); - String connectionManagerName = ControlConfigs.getInstance().getConnectionManager(); - - for (ConnectionControlManager connectionControlManagerInternal : connectionControlManagers) { - if (connectionControlManagerInternal.getName().equalsIgnoreCase(connectionManagerName)) { - Loggers.CONTROL.info("Found rule parser of name={},class={}", ruleParserName, - connectionControlManagerInternal.getClass().getSimpleName()); - connectionControlManager = connectionControlManagerInternal; - break; - } - } - if (connectionControlManager == null) { - Loggers.CONTROL.warn("Fail to rule parser of name :" + ruleParserName); - connectionControlManager = new NacosConnectionControlManager(); - } - - } - private void initConnectionManager() { Collection connectionControlManagers = NacosServiceLoader @@ -127,7 +89,6 @@ private void initTpsControlManager() { private ControlManagerCenter() { initTpsControlManager(); - initRuleParser(); initConnectionManager(); ruleStorageProxy = new RuleStorageProxy(); } @@ -137,7 +98,7 @@ public RuleStorageProxy getRuleStorageProxy() { } public RuleParser getRuleParser() { - return ruleParser; + return RuleParserProxy.getInstance(); } public TpsControlManager getTpsControlManager() { diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/configs/ControlConfigs.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/configs/ControlConfigs.java index 0d4f62b05d0..4a2f840dc70 100644 --- a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/configs/ControlConfigs.java +++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/configs/ControlConfigs.java @@ -16,17 +16,15 @@ package com.alibaba.nacos.plugin.control.configs; -import com.alibaba.nacos.plugin.control.Loggers; -import com.alibaba.nacos.sys.utils.ApplicationUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; +import com.alibaba.nacos.common.spi.NacosServiceLoader; + +import java.util.Collection; /** * control configs params. * * @author shiyiyue */ -@Component public class ControlConfigs { private static ControlConfigs instance = null; @@ -34,15 +32,13 @@ public class ControlConfigs { public static ControlConfigs getInstance() { if (instance == null) { synchronized (ControlConfigs.class) { - try { - instance = ApplicationUtils.getBean(ControlConfigs.class); - } catch (Throwable throwable) { - Loggers.CONTROL - .warn("Fail to get control configs bean from spring context,use default constructor instance", - throwable); - } if (instance == null) { instance = new ControlConfigs(); + Collection load = NacosServiceLoader + .load(ControlConfigsInitializer.class); + for (ControlConfigsInitializer controlConfigsInitializer : load) { + controlConfigsInitializer.initialize(instance); + } } } } @@ -54,27 +50,22 @@ public static void setInstance(ControlConfigs instance) { ControlConfigs.instance = instance; } - @Value("${nacos.plugin.control.tps.barrier.creator:nacos}") private String tpsBarrierCreator = "nacos"; - @Value("${nacos.plugin.control.tps.barrier.rule.creator:nacos}") private String tpsRuleBarrierCreator = "nacos"; - @Value("${nacos.plugin.control.connection.runtime.ejector:nacos}") private String connectionRuntimeEjector = "nacos"; - @Value("${nacos.plugin.control.connection.manager:nacos}") private String connectionManager = "nacos"; - @Value("${nacos.plugin.control.tps.manager:nacos}") private String tpsManager = "nacos"; - @Value("${nacos.plugin.control.rule.external.storage:}") private String ruleExternalStorage = ""; - @Value("${nacos.plugin.control.rule.parser:nacos}") private String ruleParser = "nacos"; + private String localRuleStorageBaseDir = ""; + public String getTpsBarrierCreator() { return tpsBarrierCreator; } @@ -130,4 +121,12 @@ public String getTpsManager() { public void setTpsManager(String tpsManager) { this.tpsManager = tpsManager; } + + public String getLocalRuleStorageBaseDir() { + return localRuleStorageBaseDir; + } + + public void setLocalRuleStorageBaseDir(String localRuleStorageBaseDir) { + this.localRuleStorageBaseDir = localRuleStorageBaseDir; + } } diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/configs/ControlConfigsInitializer.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/configs/ControlConfigsInitializer.java new file mode 100644 index 00000000000..dad82704ad7 --- /dev/null +++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/configs/ControlConfigsInitializer.java @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2020 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.plugin.control.configs; + +/** + * control plugin configs initializer. + * + * @author shiyiyue + */ +public interface ControlConfigsInitializer { + + /** + * init control configs. + * + * @param controlConfigs control configs. + */ + void initialize(ControlConfigs controlConfigs); + +} diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/ControlRuleChangeActivator.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/ControlRuleChangeActivator.java index 1916a28ad2c..5984ac6be06 100644 --- a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/ControlRuleChangeActivator.java +++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/ControlRuleChangeActivator.java @@ -27,14 +27,12 @@ import com.alibaba.nacos.plugin.control.event.TpsControlRuleChangeEvent; import com.alibaba.nacos.plugin.control.tps.rule.TpsControlRule; import org.slf4j.Logger; -import org.springframework.stereotype.Component; /** * control rule activator. - * @author shiyiyue * + * @author shiyiyue */ -@Component public class ControlRuleChangeActivator { private static final Logger LOGGER = Loggers.CONTROL; diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/DiskUtils.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/DiskUtils.java new file mode 100644 index 00000000000..a068350cc36 --- /dev/null +++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/DiskUtils.java @@ -0,0 +1,556 @@ +/* + * Copyright 1999-2018 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.plugin.control.ruleactivator; + +import com.alibaba.nacos.common.utils.ByteUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.NullOutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.zip.CheckedInputStream; +import java.util.zip.CheckedOutputStream; +import java.util.zip.Checksum; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +/** + * IO operates on the utility class. + * + * @author liaochuntao + */ +public final class DiskUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(DiskUtils.class); + + private static final String NO_SPACE_CN = "设备上没有空间"; + + private static final String NO_SPACE_EN = "No space left on device"; + + private static final String DISK_QUATA_CN = "超出磁盘限额"; + + private static final String DISK_QUATA_EN = "Disk quota exceeded"; + + private static final Charset CHARSET = StandardCharsets.UTF_8; + + private static final CharsetDecoder DECODER = CHARSET.newDecoder(); + + public static void touch(String path, String fileName) throws IOException { + FileUtils.touch(Paths.get(path, fileName).toFile()); + } + + /** + * Implements the same behaviour as the "touch" utility on Unix. It creates a new file with size 0 or, if the file + * exists already, it is opened and closed without modifying it, but updating the file date and time. + * + *

NOTE: As from v1.3, this method throws an IOException if the last + * modified date of the file cannot be set. Also, as from v1.3 this method creates parent directories if they do not + * exist. + * + * @param file the File to touch + * @throws IOException If an I/O problem occurs + */ + public static void touch(File file) throws IOException { + FileUtils.touch(file); + } + + /** + * Creates a new empty file in the specified directory, using the given prefix and suffix strings to generate its + * name. The resulting {@code Path} is associated with the same {@code FileSystem} as the given directory. + * + *

The details as to how the name of the file is constructed is + * implementation dependent and therefore not specified. Where possible the {@code prefix} and {@code suffix} are + * used to construct candidate names in the same manner as the {@link File#createTempFile(String, String, + * File)} method. + * + * @param dir the path to directory in which to create the file + * @param prefix the prefix string to be used in generating the file's name; may be {@code null} + * @param suffix the suffix string to be used in generating the file's name; may be {@code null}, in which case + * "{@code .tmp}" is used + * @return the path to the newly created file that did not exist before this method was invoked + * @throws IllegalArgumentException if the prefix or suffix parameters cannot be used to generate a candidate + * file name + * @throws UnsupportedOperationException if the array contains an attribute that cannot be set atomically when + * creating the directory + * @throws IOException if an I/O error occurs or {@code dir} does not exist + * @throws SecurityException In the case of the default provider, and a security manager is installed, + * the {@link SecurityManager#checkWrite(String) checkWrite} method is invoked + * to check write access to the file. + */ + public static File createTmpFile(String dir, String prefix, String suffix) throws IOException { + return Files.createTempFile(Paths.get(dir), prefix, suffix).toFile(); + } + + /** + * Creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its + * name. The resulting {@code Path} is associated with the default {@code FileSystem}. + * + * @param prefix the prefix string to be used in generating the file's name; may be {@code null} + * @param suffix the suffix string to be used in generating the file's name; may be {@code null}, in which case + * "{@code .tmp}" is used + * @return the path to the newly created file that did not exist before this method was invoked + * @throws IllegalArgumentException if the prefix or suffix parameters cannot be used to generate a candidate + * file name + * @throws UnsupportedOperationException if the array contains an attribute that cannot be set atomically when + * creating the directory + * @throws IOException if an I/O error occurs or the temporary-file directory does not exist + * @throws SecurityException In the case of the default provider, and a security manager is installed, + * the {@link SecurityManager#checkWrite(String) checkWrite} method is invoked + * to check write access to the file. + */ + public static File createTmpFile(String prefix, String suffix) throws IOException { + return Files.createTempFile(prefix, suffix).toFile(); + } + + /** + * read file which under the path. + * + * @param path directory + * @param fileName filename + * @return content + */ + public static String readFile(String path, String fileName) { + File file = openFile(path, fileName); + if (file.exists()) { + return readFile(file); + } + return null; + } + + /** + * read file content by {@link InputStream}. + * + * @param is {@link InputStream} + * @return content + */ + public static String readFile(InputStream is) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { + StringBuilder textBuilder = new StringBuilder(); + String lineTxt = null; + while ((lineTxt = reader.readLine()) != null) { + textBuilder.append(lineTxt); + } + return textBuilder.toString(); + } catch (IOException e) { + return null; + } + } + + /** + * read this file content. + * + * @param file {@link File} + * @return content + */ + public static String readFile(File file) { + try (FileChannel fileChannel = new FileInputStream(file).getChannel()) { + StringBuilder text = new StringBuilder(); + ByteBuffer buffer = ByteBuffer.allocate(4096); + CharBuffer charBuffer = CharBuffer.allocate(4096); + while (fileChannel.read(buffer) != -1) { + buffer.flip(); + DECODER.decode(buffer, charBuffer, false); + charBuffer.flip(); + while (charBuffer.hasRemaining()) { + text.append(charBuffer.get()); + } + buffer.clear(); + charBuffer.clear(); + } + return text.toString(); + } catch (IOException e) { + return null; + } + } + + /** + * read this file content then return bytes. + * + * @param file {@link File} + * @return content bytes + */ + public static byte[] readFileBytes(File file) { + if (file.exists()) { + String result = readFile(file); + if (result != null) { + return ByteUtils.toBytes(result); + } + } + return null; + } + + public static byte[] readFileBytes(String path, String fileName) { + File file = openFile(path, fileName); + return readFileBytes(file); + } + + /** + * Writes the contents to the target file. + * + * @param file target file + * @param content content + * @param append write append mode + * @return write success + */ + public static boolean writeFile(File file, byte[] content, boolean append) { + try (FileChannel fileChannel = new FileOutputStream(file, append).getChannel()) { + ByteBuffer buffer = ByteBuffer.wrap(content); + fileChannel.write(buffer); + return true; + } catch (IOException ioe) { + if (ioe.getMessage() != null) { + String errMsg = ioe.getMessage(); + if (NO_SPACE_CN.equals(errMsg) || NO_SPACE_EN.equals(errMsg) || errMsg.contains(DISK_QUATA_CN) || errMsg + .contains(DISK_QUATA_EN)) { + LOGGER.warn("磁盘满,自杀退出"); + System.exit(0); + } + } + } + return false; + } + + public static void deleteQuietly(File file) { + Objects.requireNonNull(file, "file"); + FileUtils.deleteQuietly(file); + } + + public static void deleteQuietly(Path path) { + Objects.requireNonNull(path, "path"); + FileUtils.deleteQuietly(path.toFile()); + } + + /** + * delete target file. + * + * @param path directory + * @param fileName filename + * @return delete success + */ + public static boolean deleteFile(String path, String fileName) { + File file = Paths.get(path, fileName).toFile(); + if (file.exists()) { + return file.delete(); + } + return false; + } + + public static void deleteDirectory(String path) throws IOException { + FileUtils.deleteDirectory(new File(path)); + } + + public static void forceMkdir(String path) throws IOException { + FileUtils.forceMkdir(new File(path)); + } + + public static void forceMkdir(File file) throws IOException { + FileUtils.forceMkdir(file); + } + + public static void deleteDirThenMkdir(String path) throws IOException { + deleteDirectory(path); + forceMkdir(path); + } + + public static void copyDirectory(File srcDir, File destDir) throws IOException { + FileUtils.copyDirectory(srcDir, destDir); + } + + public static void copyFile(File src, File target) throws IOException { + FileUtils.copyFile(src, target); + } + + public static File openFile(String path, String fileName) { + return openFile(path, fileName, false); + } + + /** + * open file. + * + * @param path directory + * @param fileName filename + * @param rewrite if rewrite is true, will delete old file and create new one + * @return {@link File} + */ + public static File openFile(String path, String fileName, boolean rewrite) { + File directory = new File(path); + boolean mkdirs = true; + if (!directory.exists()) { + mkdirs = directory.mkdirs(); + } + if (!mkdirs) { + LOGGER.error("[DiskUtils] can't create directory"); + return null; + } + File file = new File(path, fileName); + try { + boolean create = true; + if (!file.exists()) { + file.createNewFile(); + } + if (file.exists()) { + if (rewrite) { + file.delete(); + } else { + create = false; + } + } + if (create) { + file.createNewFile(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return file; + } + + // copy from sofa-jraft + + /** + * Compress a folder in a directory. + * + * @param rootDir directory + * @param sourceDir folder + * @param outputFile output file + * @param checksum checksum + * @throws IOException IOException + */ + public static void compress(final String rootDir, final String sourceDir, final String outputFile, + final Checksum checksum) throws IOException { + try (final FileOutputStream fos = new FileOutputStream(outputFile); + final CheckedOutputStream cos = new CheckedOutputStream(fos, checksum); + final ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(cos))) { + compressDirectoryToZipFile(rootDir, sourceDir, zos); + zos.flush(); + fos.getFD().sync(); + } + } + + // copy from sofa-jraft + + private static void compressDirectoryToZipFile(final String rootDir, final String sourceDir, + final ZipOutputStream zos) throws IOException { + final String dir = Paths.get(rootDir, sourceDir).toString(); + final File[] files = Objects.requireNonNull(new File(dir).listFiles(), "files"); + for (final File file : files) { + final String child = Paths.get(sourceDir, file.getName()).toString(); + if (file.isDirectory()) { + compressDirectoryToZipFile(rootDir, child, zos); + } else { + try (final FileInputStream fis = new FileInputStream(file); + final BufferedInputStream bis = new BufferedInputStream(fis)) { + compressIntoZipFile(child, bis, zos); + } + } + } + } + + /** + * Compress an input stream to zip file. + * + * @param childName child name in zip file + * @param inputStream input stream needed compress + * @param outputFile output file + * @param checksum check sum + * @throws IOException IOException during compress + */ + public static void compressIntoZipFile(final String childName, final InputStream inputStream, + final String outputFile, final Checksum checksum) throws IOException { + try (final FileOutputStream fileOutputStream = new FileOutputStream(outputFile); + final CheckedOutputStream checkedOutputStream = new CheckedOutputStream(fileOutputStream, checksum); + final ZipOutputStream zipStream = new ZipOutputStream(new BufferedOutputStream(checkedOutputStream))) { + compressIntoZipFile(childName, inputStream, zipStream); + zipStream.flush(); + fileOutputStream.getFD().sync(); + } + } + + private static void compressIntoZipFile(final String childName, final InputStream inputStream, + final ZipOutputStream zipOutputStream) throws IOException { + zipOutputStream.putNextEntry(new ZipEntry(childName)); + IOUtils.copy(inputStream, zipOutputStream); + } + + // copy from sofa-jraft + + /** + * Unzip the target file to the specified folder. + * + * @param sourceFile target file + * @param outputDir specified folder + * @param checksum checksum + * @throws IOException IOException + */ + public static void decompress(final String sourceFile, final String outputDir, final Checksum checksum) + throws IOException { + try (final FileInputStream fis = new FileInputStream(sourceFile); + final CheckedInputStream cis = new CheckedInputStream(fis, checksum); + final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(cis))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + final String fileName = entry.getName(); + final File entryFile = new File(Paths.get(outputDir, fileName).toString()); + FileUtils.forceMkdir(entryFile.getParentFile()); + try (final FileOutputStream fos = new FileOutputStream(entryFile); + final BufferedOutputStream bos = new BufferedOutputStream(fos)) { + IOUtils.copy(zis, bos); + bos.flush(); + fos.getFD().sync(); + } + } + // Continue to read all remaining bytes(extra metadata of ZipEntry) directly from the checked stream, + // Otherwise, the checksum value maybe unexpected. + // + // See https://coderanch.com/t/279175/java/ZipInputStream + IOUtils.copy(cis, NullOutputStream.NULL_OUTPUT_STREAM); + } + } + + /** + * Unzip the target file to byte array. + * + * @param sourceFile target file + * @param checksum checksum + * @return decompress byte array + * @throws IOException IOException during decompress + */ + public static byte[] decompress(final String sourceFile, final Checksum checksum) throws IOException { + byte[] result; + try (final FileInputStream fis = new FileInputStream(sourceFile); + final CheckedInputStream cis = new CheckedInputStream(fis, checksum); + final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(cis)); + final ByteArrayOutputStream bos = new ByteArrayOutputStream(1024)) { + while (zis.getNextEntry() != null) { + IOUtils.copy(zis, bos); + bos.flush(); + } + IOUtils.copy(cis, NullOutputStream.NULL_OUTPUT_STREAM); + result = bos.toByteArray(); + } + return result; + } + + /** + * Returns an Iterator for the lines in a File. + *

+ * This method opens an InputStream for the file. When you have finished with the iterator you should + * close the stream to free internal resources. This can be done by calling the {@link + * org.apache.commons.io.LineIterator#close()} or {@link org.apache.commons.io.LineIterator#closeQuietly(org.apache.commons.io.LineIterator)} + * method. + *

+ * The recommended usage pattern is: + *
+     * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
+     * try {
+     *   while (it.hasNext()) {
+     *     String line = it.nextLine();
+     *     /// do something with line
+     *   }
+     * } finally {
+     *   LineIterator.closeQuietly(iterator);
+     * }
+     * 
+ *

+ * If an exception occurs during the creation of the iterator, the underlying stream is closed. + *

+ * + * @param file the file to open for input, must not be null + * @param encoding the encoding to use, null means platform default + * @return an Iterator of the lines in the file, never null + * @throws IOException in case of an I/O error (file closed) + * @since 1.2 + */ + public static LineIterator lineIterator(File file, String encoding) throws IOException { + return new LineIterator(FileUtils.lineIterator(file, encoding)); + } + + /** + * Returns an Iterator for the lines in a File using the default encoding for the VM. + * + * @param file the file to open for input, must not be null + * @return an Iterator of the lines in the file, never null + * @throws IOException in case of an I/O error (file closed) + * @see #lineIterator(File, String) + * @since 1.3 + */ + public static LineIterator lineIterator(File file) throws IOException { + return new LineIterator(FileUtils.lineIterator(file, null)); + } + + public static class LineIterator implements AutoCloseable { + + private final org.apache.commons.io.LineIterator target; + + /** + * Constructs an iterator of the lines for a Reader. + * + * @param target {@link org.apache.commons.io.LineIterator} + */ + LineIterator(org.apache.commons.io.LineIterator target) { + this.target = target; + } + + public boolean hasNext() { + return target.hasNext(); + } + + public String next() { + return target.next(); + } + + public String nextLine() { + return target.nextLine(); + } + + @Override + public void close() throws IOException { + target.close(); + } + + public void remove() { + target.remove(); + } + + public void forEachRemaining(Consumer action) { + target.forEachRemaining(action); + } + } + +} diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java index cfacfa5fae1..dcb1fc64018 100644 --- a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java +++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java @@ -18,8 +18,6 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.plugin.control.Loggers; -import com.alibaba.nacos.sys.env.EnvUtil; -import com.alibaba.nacos.sys.utils.DiskUtils; import org.slf4j.Logger; import java.io.File; @@ -38,16 +36,30 @@ public class LocalDiskRuleStorage implements RuleStorage { private static final Logger LOGGER = Loggers.CONTROL; + private String localRruleBaseDir = defaultBaseDir(); + private File checkTpsBaseDir() { - File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "tps" + File.separator); + File baseDir = new File(localRruleBaseDir, "data" + File.separator + "tps" + File.separator); if (!baseDir.exists()) { baseDir.mkdirs(); } return baseDir; } + public String getLocalRruleBaseDir() { + return localRruleBaseDir; + } + + public void setLocalRruleBaseDir(String localRruleBaseDir) { + this.localRruleBaseDir = localRruleBaseDir; + } + + private static String defaultBaseDir() { + return LocalDiskRuleStorage.class.getResource("/").getPath(); + } + private File getConnectionRuleFile() { - File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "connection" + File.separator); + File baseDir = new File(localRruleBaseDir, "data" + File.separator + "connection" + File.separator); if (!baseDir.exists()) { baseDir.mkdir(); } diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java index 4bf335dbf59..77d60452071 100644 --- a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java +++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java @@ -35,11 +35,14 @@ public class RuleStorageProxy { private static final RuleStorageProxy INSTANCE = new RuleStorageProxy(); - private LocalDiskRuleStorage localDiskRuleStorage = new LocalDiskRuleStorage(); + private LocalDiskRuleStorage localDiskRuleStorage = null; private ExternalRuleStorage externalRuleStorage = null; + ControlRuleChangeActivator controlRuleChangeActivator = null; + public RuleStorageProxy() { + Collection persistRuleActivators = NacosServiceLoader.load(ExternalRuleStorage.class); String rulePersistActivator = ControlConfigs.getInstance().getRuleExternalStorage(); @@ -53,6 +56,15 @@ public RuleStorageProxy() { if (externalRuleStorage == null && StringUtils.isNotBlank(rulePersistActivator)) { LOGGER.error("Fail to found persist rule storage of name :" + rulePersistActivator); } + + //local disk storage. + localDiskRuleStorage = new LocalDiskRuleStorage(); + if (StringUtils.isNotBlank(ControlConfigs.getInstance().getLocalRuleStorageBaseDir())) { + localDiskRuleStorage.setLocalRruleBaseDir(ControlConfigs.getInstance().getLocalRuleStorageBaseDir()); + } + + controlRuleChangeActivator = new ControlRuleChangeActivator(); + } public RuleStorage getLocalDiskStorage() {