All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.nacos.naming.raft.RaftStore Maven / Gradle / Ivy

The newest version!
/*
 * 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.naming.raft;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.monitor.MetricsMonitor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Properties;

import static com.alibaba.nacos.common.util.SystemUtils.NACOS_HOME;
import static com.alibaba.nacos.common.util.SystemUtils.NACOS_HOME_KEY;

/**
 * @author nacos
 */
public class RaftStore {

    private static String BASE_DIR = NACOS_HOME + File.separator + "raft";

    private static String META_FILE_NAME;

    private static String CACHE_DIR;

    private static Properties meta = new Properties();

    static {

        if (StringUtils.isNotBlank(System.getProperty(NACOS_HOME_KEY))) {
            BASE_DIR = NACOS_HOME + File.separator + "data" + File.separator + "naming";
        }

        META_FILE_NAME = BASE_DIR + File.separator + "meta.properties";
        CACHE_DIR = BASE_DIR + File.separator + "data";
    }

    public synchronized static void load() throws Exception {
        long start = System.currentTimeMillis();
        for (File cache : listCaches()) {
            if (cache.isDirectory() && cache.listFiles() != null) {
                for (File datumFile : cache.listFiles()) {
                    readDatum(datumFile, cache.getName());
                }
                continue;
            }
            readDatum(cache, StringUtils.EMPTY);
        }

        // load meta
        File meta = new File(META_FILE_NAME);
        if (!meta.exists() && !meta.getParentFile().mkdirs() && !meta.createNewFile()) {
            throw new IllegalStateException("failed to create meta file: " + meta.getAbsolutePath());
        }

        try (FileInputStream inStream = new FileInputStream(meta)) {
            RaftStore.meta.load(inStream);
            RaftCore.setTerm(NumberUtils.toLong(RaftStore.meta.getProperty("term"), 0L));
        }

        Loggers.RAFT.info("finish loading all datums, size: {} cost {} ms.", RaftCore.datumSize(), (System.currentTimeMillis() - start));
    }

    public synchronized static void load(String key) throws Exception {
        long start = System.currentTimeMillis();
        // load data
        for (File cache : listCaches()) {
            if (!cache.isFile()) {
                Loggers.RAFT.warn("warning: encountered directory in cache dir: {}", cache.getAbsolutePath());
            }

            if (!StringUtils.equals(decodeFileName(cache.getName()), key)) {
                continue;
            }
            readDatum(cache, StringUtils.EMPTY);
        }

        Loggers.RAFT.info("finish loading datum, key: {} cost {} ms.",
            key, (System.currentTimeMillis() - start));
    }

    public synchronized static void readDatum(File file, String namespaceId) throws IOException {

        ByteBuffer buffer;
        FileChannel fc = null;
        try {
            fc = new FileInputStream(file).getChannel();
            buffer = ByteBuffer.allocate((int) file.length());
            fc.read(buffer);

            String json = new String(buffer.array(), "UTF-8");
            if (StringUtils.isBlank(json)) {
                return;
            }

            Datum datum = JSON.parseObject(json, Datum.class);

            if (StringUtils.isBlank(namespaceId)) {
                namespaceId = Constants.REQUEST_PARAM_DEFAULT_NAMESPACE_ID;
            }

            if (!datum.key.contains(UtilsAndCommons.SWITCH_DOMAIN_NAME) && !datum.key.contains(namespaceId)) {

                if (datum.key.startsWith(UtilsAndCommons.DOMAINS_DATA_ID_PRE)) {
                    datum.key = UtilsAndCommons.DOMAINS_DATA_ID_PRE + namespaceId +
                        UtilsAndCommons.SERVICE_GROUP_CONNECTOR +
                        datum.key.substring(UtilsAndCommons.DOMAINS_DATA_ID_PRE.length());
                }

                if (datum.key.startsWith(UtilsAndCommons.IPADDRESS_DATA_ID_PRE)) {
                    datum.key = UtilsAndCommons.IPADDRESS_DATA_ID_PRE + namespaceId +
                        UtilsAndCommons.SERVICE_GROUP_CONNECTOR +
                        datum.key.substring(UtilsAndCommons.IPADDRESS_DATA_ID_PRE.length());
                }
            }

            RaftCore.addDatum(datum);
        } catch (Exception e) {
            Loggers.RAFT.warn("waning: failed to deserialize key: {}", file.getName());
            throw e;
        } finally {
            if (fc != null) {
                fc.close();
            }
        }
    }

    public synchronized static void write(final Datum datum) throws Exception {

        String namespaceId = null;

        if (datum.key.contains(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)) {
            String[] segments = datum.key.split(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)[0].split("\\.");
            namespaceId = segments[segments.length - 1];
        }

        File cacheFile;
        File oldCacheFile = null;

        if (StringUtils.isNotBlank(namespaceId)) {
            cacheFile = new File(CACHE_DIR + File.separator + namespaceId + File.separator + encodeFileName(datum.key));
        } else {
            cacheFile = new File(CACHE_DIR + File.separator + encodeFileName(datum.key));
        }

        if (Constants.REQUEST_PARAM_DEFAULT_NAMESPACE_ID.equals(namespaceId)) {
            // remove old format file:
            String originDatumKey = null;
            if (datum.key.startsWith(UtilsAndCommons.DOMAINS_DATA_ID_PRE)) {
                originDatumKey = UtilsAndCommons.DOMAINS_DATA_ID_PRE +
                    datum.key.split(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)[1];
            } else if (datum.key.startsWith(UtilsAndCommons.IPADDRESS_DATA_ID_PRE)) {
                originDatumKey = UtilsAndCommons.IPADDRESS_DATA_ID_PRE +
                    datum.key.split(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)[1];
            }

            oldCacheFile = new File(CACHE_DIR + File.separator + encodeFileName(originDatumKey));
            if (oldCacheFile.exists() && !oldCacheFile.delete()) {
                throw new IllegalStateException("remove old format file failed, key:" + originDatumKey);
            }
        }

        if (!cacheFile.exists() && !cacheFile.getParentFile().mkdirs() && !cacheFile.createNewFile()) {
            MetricsMonitor.getDiskException().increment();

            throw new IllegalStateException("can not make cache file: " + cacheFile.getName());
        }

        FileChannel fc = null;
        ByteBuffer data = ByteBuffer.wrap(JSON.toJSONString(datum).getBytes("UTF-8"));

        try {
            fc = new FileOutputStream(cacheFile, false).getChannel();
            fc.write(data, data.position());
            fc.force(true);
        } catch (Exception e) {
            MetricsMonitor.getDiskException().increment();
            throw e;
        } finally {
            if (fc != null) {
                fc.close();
            }
        }

        if (oldCacheFile != null) {
            oldCacheFile.delete();
        }

    }

    private static File[] listCaches() throws Exception {
        File cacheDir = new File(CACHE_DIR);
        if (!cacheDir.exists() && !cacheDir.mkdirs()) {
            throw new IllegalStateException("cloud not make out directory: " + cacheDir.getName());
        }

        return cacheDir.listFiles();
    }

    public static void delete(Datum datum) {

        if (datum.key.contains(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)) {
            // datum key contains namespace info:
            String namspaceId = null;
            String originDatumKey = null;
            if (datum.key.startsWith(UtilsAndCommons.DOMAINS_DATA_ID_PRE)) {
                namspaceId = datum.key.split(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)[0]
                    .substring(UtilsAndCommons.DOMAINS_DATA_ID_PRE.length());
                originDatumKey = UtilsAndCommons.DOMAINS_DATA_ID_PRE +
                    datum.key.split(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)[1];
            } else if (datum.key.startsWith(UtilsAndCommons.IPADDRESS_DATA_ID_PRE)) {
                namspaceId = datum.key.split(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)[0]
                    .substring(UtilsAndCommons.IPADDRESS_DATA_ID_PRE.length());
                originDatumKey = UtilsAndCommons.IPADDRESS_DATA_ID_PRE +
                    datum.key.split(UtilsAndCommons.SERVICE_GROUP_CONNECTOR)[1];
            }

            if (StringUtils.isNotBlank(namspaceId)) {

                if (namspaceId.equals(Constants.REQUEST_PARAM_DEFAULT_NAMESPACE_ID)) {

                    File cacheFile = new File(CACHE_DIR + File.separator + encodeFileName(originDatumKey));
                    if (cacheFile.exists() && !cacheFile.delete()) {
                        Loggers.RAFT.error("[RAFT-DELETE] failed to delete datum: {}, value: {}", datum.key, datum.value);
                        throw new IllegalStateException("failed to delete datum: " + datum.key);
                    }
                }

                File cacheFile = new File(CACHE_DIR + File.separator + namspaceId + File.separator + encodeFileName(datum.key));
                if (cacheFile.exists() && !cacheFile.delete()) {
                    Loggers.RAFT.error("[RAFT-DELETE] failed to delete datum: {}, value: {}", datum.key, datum.value);
                    throw new IllegalStateException("failed to delete datum: " + datum.key);
                }
            }
        } else {
            File cacheFile = new File(CACHE_DIR + File.separator + encodeFileName(datum.key));
            if (cacheFile.exists() && !cacheFile.delete()) {
                Loggers.RAFT.error("[RAFT-DELETE] failed to delete datum: {}, value: {}", datum.key, datum.value);
                throw new IllegalStateException("failed to delete datum: " + datum.key);
            }
        }
    }

    public static void updateTerm(long term) throws Exception {
        File file = new File(META_FILE_NAME);
        if (!file.exists() && !file.getParentFile().mkdirs() && !file.createNewFile()) {
            throw new IllegalStateException("failed to create meta file");
        }

        try (FileOutputStream outStream = new FileOutputStream(file)) {
            // write meta
            meta.setProperty("term", String.valueOf(term));
            meta.store(outStream, null);
        }
    }

    private static String encodeFileName(String fileName) {
        return fileName.replace(':', '#');
    }

    private static String decodeFileName(String fileName) {
        return fileName.replace("#", ":");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy