You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2022/02/08 02:17:45 UTC

[dubbo] branch 3.0 updated: [3.0] FileCacheStore refactor & Add FileCacheStoreFactory (#9655)

This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new adea101  [3.0] FileCacheStore refactor & Add FileCacheStoreFactory (#9655)
adea101 is described below

commit adea101f93555101a5a0eaec6951c055bfe1ede8
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Tue Feb 8 10:17:00 2022 +0800

    [3.0] FileCacheStore refactor & Add FileCacheStoreFactory (#9655)
---
 .../apache/dubbo/common/cache/FileCacheStore.java  | 239 +++++++++------------
 .../dubbo/common/cache/FileCacheStoreFactory.java  | 170 +++++++++++++++
 .../org/apache/dubbo/common/utils/NetUtils.java    |   2 +-
 .../org/apache/dubbo/common/utils/StringUtils.java |  12 +-
 .../common/cache/FileCacheStoreFactoryTest.java    |  74 +++++++
 .../dubbo/common/cache/FileCacheStoreTest.java     |  20 +-
 .../apache/dubbo/common/utils/StringUtilsTest.java |   6 +-
 .../apache/dubbo/qos/command/impl/CountTelnet.java |   4 +-
 .../apache/dubbo/qos/command/impl/PortTelnet.java  |   2 +-
 .../dubbo/qos/command/impl/SelectTelnet.java       |   2 +-
 .../dubbo/qos/command/impl/ShutdownTelnet.java     |   2 +-
 .../apache/dubbo/qos/legacy/LogTelnetHandler.java  |   2 +-
 .../dubbo/qos/legacy/TraceTelnetHandler.java       |   4 +-
 .../client/metadata/store/MetaCacheManager.java    |  14 +-
 .../metadata/store/MetaCacheManagerTest.java       |   2 +-
 .../telnet/support/command/ClearTelnetHandler.java |   2 +-
 .../telnet/support/command/LogTelnetHandler.java   |   2 +-
 17 files changed, 401 insertions(+), 158 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java
index acfda26..a8ecc80 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStore.java
@@ -27,48 +27,35 @@ import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
+import java.io.Writer;
 import java.nio.channels.FileLock;
-import java.nio.channels.OverlappingFileLockException;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * Local file interaction class that can back different caches.
- *
+ * <p>
  * All items in local file are of human friendly format.
  */
 public class FileCacheStore {
     private static final Logger logger = LoggerFactory.getLogger(FileCacheStore.class);
 
-    private static final int DEL = 0x7F;
-    private static final char ESCAPE = '%';
-    private static final Set<Character> ILLEGALS = new HashSet<Character>();
-    private static final String SUFFIX = ".dubbo.cache";
-
-    private File basePath;
+    private String cacheFilePath;
     private File cacheFile;
-    private FileLock directoryLock;
     private File lockFile;
+    private FileLock directoryLock;
 
-    public FileCacheStore(String basePath, String fileName) throws IOException, PathNotExclusiveException {
-        if (basePath == null) {
-            basePath = System.getProperty("user.home") + "/.dubbo/";
-        }
-        this.basePath = new File(basePath);
-
-        this.cacheFile = getFile(fileName, SUFFIX);
-        if (cacheFile != null && !cacheFile.exists()) {
-            cacheFile.createNewFile();
-        }
+    private FileCacheStore(String cacheFilePath, File cacheFile, File lockFile, FileLock directoryLock) {
+        this.cacheFilePath = cacheFilePath;
+        this.cacheFile = cacheFile;
+        this.lockFile = lockFile;
+        this.directoryLock = directoryLock;
     }
 
-    public Map<String, String> loadCache(int entrySize) throws IOException {
+    public synchronized Map<String, String> loadCache(int entrySize) throws IOException {
         Map<String, String> properties = new HashMap<>();
         try (BufferedReader reader = new BufferedReader(new FileReader(cacheFile))) {
             int count = 1;
@@ -93,105 +80,6 @@ public class FileCacheStore {
         return properties;
     }
 
-    public File getFile(String cacheName, String suffix) throws PathNotExclusiveException {
-        cacheName = safeName(cacheName);
-        if (!cacheName.endsWith(suffix)) {
-            cacheName = cacheName + suffix;
-        }
-        return getFile(cacheName);
-    }
-
-    /**
-     * Get a file object for the given name
-     *
-     * @param name the file name
-     * @return a file object
-     */
-    public File getFile(String name) throws PathNotExclusiveException {
-        synchronized (this) {
-            File candidate = basePath;
-            // ensure cache store path exists
-            if (!candidate.isDirectory() && !candidate.mkdirs()) {
-                throw new RuntimeException("Cache store path can't be created: " + candidate);
-            }
-
-            boolean autoCreated = false;
-            int index = 1;
-            while (true) {
-                try {
-                    tryFileLock(name);
-                    break;
-                } catch (PathNotExclusiveException e) {
-                    autoCreated = true;
-                    index++;
-                    name += index;
-                    if (index > 3) {
-                        logger.warn("Path '" + basePath + "/" + name
-                            + "' has already used by an existing Dubbo process.\n Please specify another one explicitly.");
-                        throw e;
-                    }
-                }
-            }
-            if (autoCreated && index < 3) {
-                logger.warn("Auto-generated cache file name is " + basePath + "/" + name);
-            }
-        }
-
-        File file = new File(basePath, name);
-        for (File parent = file.getParentFile(); parent != null; parent = parent.getParentFile()) {
-            if (basePath.equals(parent)) {
-                return file;
-            }
-        }
-
-        throw new IllegalArgumentException("Attempted to access file outside the dubbo cache path");
-    }
-
-    /**
-     * sanitize a name for valid file or directory name
-     *
-     * @param name
-     * @return sanitized version of name
-     */
-    private static String safeName(String name) {
-        int len = name.length();
-        StringBuilder sb = new StringBuilder(len);
-        for (int i = 0; i < len; i++) {
-            char c = name.charAt(i);
-            if (c <= ' ' || c >= DEL || (c >= 'A' && c <= 'Z') || ILLEGALS.contains(c) || c == ESCAPE) {
-                sb.append(ESCAPE);
-                sb.append(String.format("%04x", (int) c));
-            } else {
-                sb.append(c);
-            }
-        }
-        return sb.toString();
-    }
-
-    private void tryFileLock(String fileName) throws PathNotExclusiveException {
-        lockFile = new File(basePath.getAbsoluteFile(), fileName + ".lock");
-        lockFile.deleteOnExit();
-
-        FileLock dirLock;
-        try {
-            lockFile.createNewFile();
-            if (!lockFile.exists()) {
-                throw new AssertionError("Failed to create lock file " + lockFile);
-            }
-            FileChannel lockFileChannel = new RandomAccessFile(lockFile, "rw").getChannel();
-            dirLock = lockFileChannel.tryLock();
-        } catch (OverlappingFileLockException ofle) {
-            dirLock = null;
-        } catch (IOException ioe) {
-            throw new RuntimeException(ioe);
-        }
-
-        if (dirLock == null) {
-            throw new PathNotExclusiveException(basePath.getAbsolutePath() + "/" + fileName + " is not exclusive.");
-        }
-
-        this.directoryLock = dirLock;
-    }
 
     private void unlock() {
         if (directoryLock != null && directoryLock.isValid()) {
@@ -205,12 +93,15 @@ public class FileCacheStore {
         }
     }
 
-    public void refreshCache(Map<String, String> properties, String comment) {
+    public synchronized void refreshCache(Map<String, String> properties, String comment, long maxFileSize) {
         if (CollectionUtils.isEmptyMap(properties)) {
             return;
         }
 
-        try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(cacheFile, false), StandardCharsets.UTF_8))) {
+        try (LimitedLengthBufferedWriter bw =
+                 new LimitedLengthBufferedWriter(
+                     new OutputStreamWriter(
+                         new FileOutputStream(cacheFile, false), StandardCharsets.UTF_8), maxFileSize)) {
             bw.write("#" + comment);
             bw.newLine();
             bw.write("#" + new Date());
@@ -222,6 +113,10 @@ public class FileCacheStore {
                 bw.newLine();
             }
             bw.flush();
+            long remainSize = bw.getRemainSize();
+            if (remainSize < 0) {
+                logger.info("Cache file was truncated for exceeding the maximum file size " + maxFileSize + " byte. Exceeded by " + (-remainSize) + " byte.");
+            }
         } catch (IOException e) {
             logger.warn("Update cache error.");
         }
@@ -233,17 +128,97 @@ public class FileCacheStore {
         }
     }
 
-    private static class PathNotExclusiveException extends Exception {
-        public PathNotExclusiveException() {
-            super();
+    public synchronized void destroy() {
+        unlock();
+        FileCacheStoreFactory.removeCache(cacheFilePath);
+    }
+
+    /**
+     * for unit test only
+     */
+    @Deprecated
+    protected String getCacheFilePath() {
+        return cacheFilePath;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+        private String cacheFilePath;
+        private File cacheFile;
+        private File lockFile;
+        private FileLock directoryLock;
+
+        private Builder() {
         }
 
-        public PathNotExclusiveException(String msg) {
-            super(msg);
+        public Builder cacheFilePath(String cacheFilePath) {
+            this.cacheFilePath = cacheFilePath;
+            return this;
+        }
+
+        public Builder cacheFile(File cacheFile) {
+            this.cacheFile = cacheFile;
+            return this;
+        }
+
+        public Builder lockFile(File lockFile) {
+            this.lockFile = lockFile;
+            return this;
+        }
+
+        public Builder directoryLock(FileLock directoryLock) {
+            this.directoryLock = directoryLock;
+            return this;
+        }
+
+        public FileCacheStore build() {
+            return new FileCacheStore(cacheFilePath, cacheFile, lockFile, directoryLock);
         }
     }
 
-    public void destroy() {
-        unlock();
+    protected static class Empty extends FileCacheStore {
+
+        private Empty(String cacheFilePath) {
+            super(cacheFilePath, null, null, null);
+        }
+
+        public static Empty getInstance(String cacheFilePath) {
+            return new Empty(cacheFilePath);
+        }
+
+        @Override
+        public Map<String, String> loadCache(int entrySize) throws IOException {
+            return Collections.emptyMap();
+        }
+
+        @Override
+        public void refreshCache(Map<String, String> properties, String comment, long maxFileSize) {
+        }
+    }
+
+    private static class LimitedLengthBufferedWriter extends BufferedWriter {
+
+        private long remainSize;
+
+        public LimitedLengthBufferedWriter(Writer out, long maxSize) {
+            super(out);
+            this.remainSize = maxSize == 0 ? Long.MAX_VALUE : maxSize;
+        }
+
+        @Override
+        public void write(String str) throws IOException {
+            remainSize -= str.getBytes(StandardCharsets.UTF_8).length;
+            if (remainSize < 0) {
+                return;
+            }
+            super.write(str);
+        }
+
+        public long getRemainSize() {
+            return remainSize;
+        }
     }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java
new file mode 100644
index 0000000..13fe540
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/cache/FileCacheStoreFactory.java
@@ -0,0 +1,170 @@
+/*
+ * 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.dubbo.common.cache;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * ClassLoader Level static share.
+ * Prevent FileCacheStore being operated in multi-application
+ */
+public class FileCacheStoreFactory {
+    private final static Logger logger = LoggerFactory.getLogger(FileCacheStoreFactory.class);
+    private static final Map<String, FileCacheStore> cacheMap = new ConcurrentHashMap<>();
+
+    private static final String SUFFIX = ".dubbo.cache";
+    private static final char ESCAPE = '%';
+    private static final Set<Character> LEGAL_CHARACTERS = Collections.unmodifiableSet(new HashSet<Character>(){{
+        // - $ . _ 0-9 a-z A-Z
+        add('-');
+        add('$');
+        add('.');
+        add('_');
+        for (char c = '0'; c <= '9'; c++) {
+            add(c);
+        }
+        for (char c = 'a'; c <= 'z'; c++) {
+            add(c);
+        }
+        for (char c = 'A'; c <= 'Z'; c++) {
+            add(c);
+        }
+    }});
+
+    public static FileCacheStore getInstance(String basePath, String cacheName) {
+        if (basePath == null) {
+            basePath = System.getProperty("user.home") + "/.dubbo/";
+        }
+
+        File candidate = new File(basePath);
+        // ensure cache store path exists
+        if (!candidate.isDirectory() && !candidate.mkdirs()) {
+            throw new RuntimeException("Cache store path can't be created: " + candidate);
+        }
+
+        cacheName = safeName(cacheName);
+        if (!cacheName.endsWith(SUFFIX)) {
+            cacheName = cacheName + SUFFIX;
+        }
+
+        String cacheFilePath = basePath + File.separator + cacheName;
+
+        return cacheMap.computeIfAbsent(cacheFilePath, (k) -> getFile(k));
+    }
+
+    /**
+     * sanitize a name for valid file or directory name
+     *
+     * @param name origin file name
+     * @return sanitized version of name
+     */
+    private static String safeName(String name) {
+        int len = name.length();
+        StringBuilder sb = new StringBuilder(len);
+        for (int i = 0; i < len; i++) {
+            char c = name.charAt(i);
+            if (LEGAL_CHARACTERS.contains(c)) {
+                sb.append(c);
+            } else {
+                sb.append(ESCAPE);
+                sb.append(String.format("%04x", (int) c));
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Get a file object for the given name
+     *
+     * @param name the file name
+     * @return a file object
+     */
+    private static FileCacheStore getFile(String name) {
+        try {
+            FileCacheStore.Builder builder = FileCacheStore.newBuilder();
+            tryFileLock(builder, name);
+            File file = new File(name);
+            if (!file.exists()) {
+                file.createNewFile();
+            }
+
+            builder.cacheFilePath(name)
+                .cacheFile(file);
+            return builder.build();
+        } catch (Throwable t) {
+            logger.info("Failed to create file store cache. Local file cache will be disabled. Cache file name: " + name, t);
+            return FileCacheStore.Empty.getInstance(name);
+        }
+    }
+
+    private static void tryFileLock(FileCacheStore.Builder builder, String fileName) throws PathNotExclusiveException {
+        File lockFile = new File(fileName + ".lock");
+        lockFile.deleteOnExit();
+
+        FileLock dirLock;
+        try {
+            lockFile.createNewFile();
+            if (!lockFile.exists()) {
+                throw new AssertionError("Failed to create lock file " + lockFile);
+            }
+            FileChannel lockFileChannel = new RandomAccessFile(lockFile, "rw").getChannel();
+            dirLock = lockFileChannel.tryLock();
+        } catch (OverlappingFileLockException ofle) {
+            dirLock = null;
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe);
+        }
+
+        if (dirLock == null) {
+            throw new PathNotExclusiveException(fileName + " is not exclusive.");
+        }
+
+        builder.directoryLock(dirLock).lockFile(lockFile);
+    }
+
+    protected static void removeCache(String cacheFileName) {
+        cacheMap.remove(cacheFileName);
+    }
+
+    /**
+     * for unit test only
+     */
+    @Deprecated
+    protected static Map<String, FileCacheStore> getCacheMap() {
+        return cacheMap;
+    }
+
+    private static class PathNotExclusiveException extends Exception {
+        public PathNotExclusiveException(String msg) {
+            super(msg);
+        }
+    }
+
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
index a0b56a8..c4f24df 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
@@ -643,7 +643,7 @@ public class NetUtils {
         int i = host.indexOf('.');
         if (i > 0) {
             String prefix = host.substring(0, i);
-            if (StringUtils.isInteger(prefix)) {
+            if (StringUtils.isNumber(prefix)) {
                 int p = Integer.parseInt(prefix);
                 return p >= 224 && p <= 239;
             }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java
index 386921a..3332b40 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java
@@ -61,7 +61,7 @@ public final class StringUtils {
 
     private static final Logger logger = LoggerFactory.getLogger(StringUtils.class);
     private static final Pattern KVP_PATTERN = Pattern.compile("([_.a-zA-Z0-9][-_.a-zA-Z0-9]*)[=](.*)"); //key value pair pattern.
-    private static final Pattern INT_PATTERN = Pattern.compile("^\\d+$");
+    private static final Pattern NUM_PATTERN = Pattern.compile("^\\d+$");
     private static final Pattern PARAMETERS_PATTERN = Pattern.compile("^\\[((\\s*\\{\\s*[\\w_\\-\\.]+\\s*:\\s*.+?\\s*\\}\\s*,?\\s*)+)\\s*\\]$");
     private static final Pattern PAIR_PARAMETERS_PATTERN = Pattern.compile("^\\{\\s*([\\w-_\\.]+)\\s*:\\s*(.+)\\s*\\}$");
     private static final int PAD_LIMIT = 8192;
@@ -535,12 +535,16 @@ public final class StringUtils {
      * @param str
      * @return is integer
      */
-    public static boolean isInteger(String str) {
-        return isNotEmpty(str) && INT_PATTERN.matcher(str).matches();
+    public static boolean isNumber(String str) {
+        return isNotEmpty(str) && NUM_PATTERN.matcher(str).matches();
     }
 
     public static int parseInteger(String str) {
-        return isInteger(str) ? Integer.parseInt(str) : 0;
+        return isNumber(str) ? Integer.parseInt(str) : 0;
+    }
+
+    public static long parseLong(String str) {
+        return isNumber(str) ? Long.parseLong(str) : 0;
     }
 
     /**
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java
new file mode 100644
index 0000000..65f5e28
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreFactoryTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dubbo.common.cache;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Paths;
+
+public class FileCacheStoreFactoryTest {
+
+    @Test
+    public void testSafeName() throws URISyntaxException {
+        FileCacheStore store1 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "../../../dubbo");
+        Assertions.assertEquals(getDirectoryOfClassPath() + File.separator + "..%002f..%002f..%002fdubbo.dubbo.cache", store1.getCacheFilePath());
+        store1.destroy();
+
+        FileCacheStore store2 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "../../../中文");
+        Assertions.assertEquals(getDirectoryOfClassPath() + File.separator + "..%002f..%002f..%002f%4e2d%6587.dubbo.cache", store2.getCacheFilePath());
+        store2.destroy();
+    }
+
+    @Test
+    public void testPathIsFile() throws URISyntaxException, IOException {
+        String basePath = getDirectoryOfClassPath();
+        String filePath = basePath + File.separator + "isFile";
+        new File(filePath).createNewFile();
+
+        Assertions.assertThrows(RuntimeException.class, () -> FileCacheStoreFactory.getInstance(filePath, "dubbo"));
+    }
+
+    @Test
+    public void testCacheContains() throws URISyntaxException {
+        FileCacheStore store1 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "testCacheContains");
+        Assertions.assertNotNull(store1.getCacheFilePath());
+
+        FileCacheStoreFactory.getCacheMap().remove(store1.getCacheFilePath());
+        FileCacheStore store2 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "testCacheContains");
+        Assertions.assertEquals(FileCacheStore.Empty.class, store2.getClass());
+
+        store1.destroy();
+        store2.destroy();
+
+        FileCacheStore store3 = FileCacheStoreFactory.getInstance(getDirectoryOfClassPath(), "testCacheContains");
+        Assertions.assertNotNull(store3.getCacheFilePath());
+        store3.destroy();
+    }
+
+    private String getDirectoryOfClassPath() throws URISyntaxException {
+        URL resource = this.getClass().getResource("/log4j.xml");
+        String path = Paths.get(resource.toURI()).toFile().getAbsolutePath();
+        int index = path.indexOf("log4j.xml");
+        String directoryPath = path.substring(0, index);
+        return directoryPath;
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java
index 9b766d0..6d8bf11 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/cache/FileCacheStoreTest.java
@@ -33,7 +33,7 @@ public class FileCacheStoreTest {
     public void testCache() throws Exception {
         String directoryPath = getDirectoryOfClassPath();
         String filePath = "test-cache.dubbo.cache";
-        cacheStore = new FileCacheStore(directoryPath, filePath);
+        cacheStore = FileCacheStoreFactory.getInstance(directoryPath, filePath);
         Map<String, String> properties = cacheStore.loadCache(10);
         assertEquals(2, properties.size());
 
@@ -42,8 +42,8 @@ public class FileCacheStoreTest {
         newProperties.put("newKey2", "newValue2");
         newProperties.put("newKey3", "newValue3");
         newProperties.put("newKey4", "newValue4");
-        cacheStore = new FileCacheStore(directoryPath, "non-exit.dubbo.cache");
-        cacheStore.refreshCache(newProperties, "test refresh cache");
+        cacheStore = FileCacheStoreFactory.getInstance(directoryPath, "non-exit.dubbo.cache");
+        cacheStore.refreshCache(newProperties, "test refresh cache", 0);
         Map<String, String> propertiesLimitTo2 = cacheStore.loadCache(2);
         assertEquals(2, propertiesLimitTo2.size());
 
@@ -53,6 +53,20 @@ public class FileCacheStoreTest {
         cacheStore.destroy();
     }
 
+    @Test
+    public void testFileSizeExceed() throws Exception {
+        String directoryPath = getDirectoryOfClassPath();
+        Map<String, String> newProperties = new HashMap<>();
+        newProperties.put("newKey1", "newValue1");
+        newProperties.put("newKey2", "newValue2");
+        newProperties.put("newKey3", "newValue3");
+        newProperties.put("newKey4", "newValue4");
+        cacheStore = FileCacheStoreFactory.getInstance(directoryPath, "non-exit.dubbo.cache");
+        cacheStore.refreshCache(newProperties, "test refresh cache", 2);
+        Map<String, String> propertiesLimitTo1 = cacheStore.loadCache(2);
+        assertEquals(0, propertiesLimitTo1.size());
+    }
+
     private String getDirectoryOfClassPath() throws URISyntaxException {
         URL resource = this.getClass().getResource("/log4j.xml");
         String path = Paths.get(resource.toURI()).toFile().getAbsolutePath();
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java
index cd42972..043ac40 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java
@@ -164,9 +164,9 @@ public class StringUtilsTest {
 
     @Test
     public void testIsInteger() throws Exception {
-        assertFalse(StringUtils.isInteger(null));
-        assertFalse(StringUtils.isInteger(""));
-        assertTrue(StringUtils.isInteger("123"));
+        assertFalse(StringUtils.isNumber(null));
+        assertFalse(StringUtils.isNumber(""));
+        assertTrue(StringUtils.isNumber("123"));
     }
 
     @Test
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
index 03599f7..1064e9f 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
@@ -71,13 +71,13 @@ public class CountTelnet implements BaseCommand {
         } else {
             method = args.length > 0 ? args[0] : null;
         }
-        if (StringUtils.isInteger(method)) {
+        if (StringUtils.isNumber(method)) {
             times = method;
             method = null;
         } else {
             times = args.length > 2 ? args[2] : "1";
         }
-        if (!StringUtils.isInteger(times)) {
+        if (!StringUtils.isNumber(times)) {
             return "Illegal times " + times + ", must be integer.";
         }
         final int t = Integer.parseInt(times);
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
index db7635c..a28e411 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PortTelnet.java
@@ -48,7 +48,7 @@ public class PortTelnet implements BaseCommand {
                 if ("-l".equals(part)) {
                     detail = true;
                 } else {
-                    if (!StringUtils.isInteger(part)) {
+                    if (!StringUtils.isNumber(part)) {
                         return "Illegal port " + part + ", must be integer.";
                     }
                     port = part;
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
index f49544e..ff15ed1 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
@@ -54,7 +54,7 @@ public class SelectTelnet implements BaseCommand {
         if (CollectionUtils.isEmpty(methodList)) {
             return "Please use the invoke command first.";
         }
-        if (!StringUtils.isInteger(message) || Integer.parseInt(message) < 1 || Integer.parseInt(message) > methodList.size()) {
+        if (!StringUtils.isNumber(message) || Integer.parseInt(message) < 1 || Integer.parseInt(message) > methodList.size()) {
             return "Illegal index ,please input select 1~" + methodList.size();
         }
         Method method = methodList.get(Integer.parseInt(message) - 1);
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
index aef3983..08f268d 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
@@ -42,7 +42,7 @@ public class ShutdownTelnet implements BaseCommand {
 
         int sleepMilliseconds = 0;
         if (args != null && args.length > 0) {
-            if (args.length == 2 && "-t".equals(args[0]) && StringUtils.isInteger(args[1])) {
+            if (args.length == 2 && "-t".equals(args[0]) && StringUtils.isNumber(args[1])) {
                 sleepMilliseconds = Integer.parseInt(args[1]);
             } else {
                 return "Invalid parameter,please input like shutdown -t 10000";
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java
index 3cbfc83..958c369 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/LogTelnetHandler.java
@@ -49,7 +49,7 @@ public class LogTelnetHandler implements TelnetHandler {
             buf.append("EXAMPLE: log error / log 100");
         } else {
             String[] str = message.split(" ");
-            if (!StringUtils.isInteger(str[0])) {
+            if (!StringUtils.isNumber(str[0])) {
                 LoggerFactory.setLevel(Level.valueOf(message.toUpperCase()));
             } else {
                 int showLogLength = Integer.parseInt(str[0]);
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/TraceTelnetHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/TraceTelnetHandler.java
index 2488da4..d4d6397 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/TraceTelnetHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/legacy/TraceTelnetHandler.java
@@ -54,11 +54,11 @@ public class TraceTelnetHandler implements TelnetHandler {
             method = parts.length > 0 ? parts[0] : null;
             times = parts.length > 1 ? parts[1] : "1";
         }
-        if (StringUtils.isInteger(method)) {
+        if (StringUtils.isNumber(method)) {
             times = method;
             method = null;
         }
-        if (!StringUtils.isInteger(times)) {
+        if (!StringUtils.isNumber(times)) {
             return "Illegal times " + times + ", must be integer.";
         }
         Invoker<?> invoker = null;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java
index cf013f2..6ad990c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManager.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.registry.client.metadata.store;
 
 import org.apache.dubbo.common.cache.FileCacheStore;
+import org.apache.dubbo.common.cache.FileCacheStoreFactory;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.resource.Disposable;
@@ -76,7 +77,7 @@ public class MetaCacheManager implements ScopeModelAware, Disposable {
         cache = new LRUCache<>(entrySize);
 
         try {
-            cacheStore = new FileCacheStore(filePath, fileName);
+            cacheStore = FileCacheStoreFactory.getInstance(filePath, fileName);
             Map<String, String> properties = cacheStore.loadCache(entrySize);
             logger.info("Successfully loaded meta cache from file " + fileName + ", entries " + properties.size());
             for (Map.Entry<String, String> entry : properties.entrySet()) {
@@ -88,7 +89,10 @@ public class MetaCacheManager implements ScopeModelAware, Disposable {
             }
             // executorService can be empty if FileCacheStore fails
             executorService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-cache-refresh", true));
-            executorService.scheduleWithFixedDelay(new CacheRefreshTask(cacheStore, cache), 10, INTERVAL, TimeUnit.MINUTES);
+
+            String rawMaxFileSize = System.getProperty("dubbo.meta.cache.maxFileSize");
+            long maxFileSize = StringUtils.parseLong(rawMaxFileSize);
+            executorService.scheduleWithFixedDelay(new CacheRefreshTask(cacheStore, cache, maxFileSize), 10, INTERVAL, TimeUnit.MINUTES);
         } catch (Exception e) {
             logger.error("Load metadata from local cache file error ", e);
         }
@@ -137,10 +141,12 @@ public class MetaCacheManager implements ScopeModelAware, Disposable {
     protected static class CacheRefreshTask implements Runnable {
         private final FileCacheStore cacheStore;
         private final LRUCache<String, MetadataInfo> cache;
+        private final long maxFileSize;
 
-        public CacheRefreshTask(FileCacheStore cacheStore, LRUCache<String, MetadataInfo> cache) {
+        public CacheRefreshTask(FileCacheStore cacheStore, LRUCache<String, MetadataInfo> cache, long maxFileSize) {
             this.cacheStore = cacheStore;
             this.cache = cache;
+            this.maxFileSize = maxFileSize;
         }
 
         @Override
@@ -157,7 +163,7 @@ public class MetaCacheManager implements ScopeModelAware, Disposable {
             }
 
             logger.info("Dumping meta caches, latest entries " + properties.size());
-            cacheStore.refreshCache(properties, DEFAULT_COMMENT);
+            cacheStore.refreshCache(properties, DEFAULT_COMMENT, maxFileSize);
         }
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java
index d039778..95db4c8 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/store/MetaCacheManagerTest.java
@@ -93,7 +93,7 @@ public class MetaCacheManagerTest {
             cacheManager.put("3", metadataInfo3);
 
             try {
-                MetaCacheManager.CacheRefreshTask task = new MetaCacheManager.CacheRefreshTask(cacheManager.cacheStore, cacheManager.cache);
+                MetaCacheManager.CacheRefreshTask task = new MetaCacheManager.CacheRefreshTask(cacheManager.cacheStore, cacheManager.cache, 0);
                 task.run();
             } catch (Exception e) {
                 fail();
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
index 957d5ea..f7de70e 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
@@ -34,7 +34,7 @@ public class ClearTelnetHandler implements TelnetHandler {
     public String telnet(Channel channel, String message) {
         int lines = 100;
         if (message.length() > 0) {
-            if (!StringUtils.isInteger(message)) {
+            if (!StringUtils.isNumber(message)) {
                 return "Illegal lines " + message + ", must be integer.";
             }
             lines = Math.min(MAX_LINES,Integer.parseInt(message));
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java
index 6c0167d..144fabb 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/support/command/LogTelnetHandler.java
@@ -49,7 +49,7 @@ public class LogTelnetHandler implements TelnetHandler {
             buf.append("EXAMPLE: log error / log 100");
         } else {
             String[] str = message.split(" ");
-            if (!StringUtils.isInteger(str[0])) {
+            if (!StringUtils.isNumber(str[0])) {
                 LoggerFactory.setLevel(Level.valueOf(message.toUpperCase()));
             } else {
                 int showLogLength = Integer.parseInt(str[0]);