[Refactor] Remove KvStorage and ConsistencyService. (#12489)
* Remove KvStorage and ConsistencyService. * Fix switch manager dead lock until timeout. * Ignore env vote leader effect in IT. * Wait leader voted before start do test case.
This commit is contained in:
parent
7133411bc2
commit
bbed5a4eb7
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.core.storage;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.storage.kv.FileKvStorage;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.MemoryKvStorage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ket-value Storage factory.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
public final class StorageFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create {@link KvStorage} implementation.
|
|
||||||
*
|
|
||||||
* @param type type of {@link KvStorage}
|
|
||||||
* @param label label for {@code RocksStorage}
|
|
||||||
* @param baseDir base dir of storage file.
|
|
||||||
* @return implementation of {@link KvStorage}
|
|
||||||
* @throws Exception exception during creating {@link KvStorage}
|
|
||||||
*/
|
|
||||||
public static KvStorage createKvStorage(KvStorage.KvType type, final String label, final String baseDir)
|
|
||||||
throws Exception {
|
|
||||||
switch (type) {
|
|
||||||
case File:
|
|
||||||
return new FileKvStorage(baseDir);
|
|
||||||
case Memory:
|
|
||||||
return new MemoryKvStorage();
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("this kv type : [" + type.name() + "] not support");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.core.storage.kv;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.common.utils.ByteUtils;
|
|
||||||
import com.alibaba.nacos.core.exception.ErrorCode;
|
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
|
||||||
import com.alibaba.nacos.sys.utils.DiskUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kv storage based on file system. // TODO 写文件的方式需要优化
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
public class FileKvStorage implements KvStorage {
|
|
||||||
|
|
||||||
private final String baseDir;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that a consistent view exists when implementing file copies.
|
|
||||||
*/
|
|
||||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
|
||||||
|
|
||||||
private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
|
|
||||||
|
|
||||||
private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
|
|
||||||
|
|
||||||
public FileKvStorage(String baseDir) throws IOException {
|
|
||||||
this.baseDir = baseDir;
|
|
||||||
DiskUtils.forceMkdir(baseDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] get(byte[] key) throws KvStorageException {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
final String fileName = new String(key);
|
|
||||||
File file = Paths.get(baseDir, fileName).toFile();
|
|
||||||
if (file.exists()) {
|
|
||||||
return DiskUtils.readFileBytes(file);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<byte[], byte[]> batchGet(List<byte[]> keys) throws KvStorageException {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
Map<byte[], byte[]> result = new HashMap<>(keys.size());
|
|
||||||
for (byte[] key : keys) {
|
|
||||||
byte[] val = get(key);
|
|
||||||
if (val != null) {
|
|
||||||
result.put(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(byte[] key, byte[] value) throws KvStorageException {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
final String fileName = new String(key);
|
|
||||||
File file = Paths.get(baseDir, fileName).toFile();
|
|
||||||
try {
|
|
||||||
DiskUtils.touch(file);
|
|
||||||
DiskUtils.writeFile(file, value, false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new KvStorageException(ErrorCode.KVStorageWriteError, e);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void batchPut(List<byte[]> keys, List<byte[]> values) throws KvStorageException {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
if (keys.size() != values.size()) {
|
|
||||||
throw new KvStorageException(ErrorCode.KVStorageBatchWriteError,
|
|
||||||
"key's size must be equal to value's size");
|
|
||||||
}
|
|
||||||
int size = keys.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
put(keys.get(i), values.get(i));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete(byte[] key) throws KvStorageException {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
final String fileName = new String(key);
|
|
||||||
DiskUtils.deleteFile(baseDir, fileName);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void batchDelete(List<byte[]> keys) throws KvStorageException {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
for (byte[] key : keys) {
|
|
||||||
delete(key);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doSnapshot(String backupPath) throws KvStorageException {
|
|
||||||
writeLock.lock();
|
|
||||||
try {
|
|
||||||
File srcDir = Paths.get(baseDir).toFile();
|
|
||||||
File descDir = Paths.get(backupPath).toFile();
|
|
||||||
DiskUtils.copyDirectory(srcDir, descDir);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new KvStorageException(ErrorCode.IOCopyDirError, e);
|
|
||||||
} finally {
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void snapshotLoad(String path) throws KvStorageException {
|
|
||||||
writeLock.lock();
|
|
||||||
try {
|
|
||||||
File srcDir = Paths.get(path).toFile();
|
|
||||||
// If snapshot path is non-exist, means snapshot is empty
|
|
||||||
if (srcDir.exists()) {
|
|
||||||
// First clean up the local file information, before the file copy
|
|
||||||
DiskUtils.deleteDirThenMkdir(baseDir);
|
|
||||||
File descDir = Paths.get(baseDir).toFile();
|
|
||||||
DiskUtils.copyDirectory(srcDir, descDir);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new KvStorageException(ErrorCode.IOCopyDirError, e);
|
|
||||||
} finally {
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<byte[]> allKeys() throws KvStorageException {
|
|
||||||
List<byte[]> result = new LinkedList<>();
|
|
||||||
File[] files = new File(baseDir).listFiles();
|
|
||||||
if (null != files) {
|
|
||||||
for (File each : files) {
|
|
||||||
if (each.isFile()) {
|
|
||||||
result.add(ByteUtils.toBytes(each.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.core.storage.kv;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Universal KV storage interface.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
public interface KvStorage {
|
|
||||||
|
|
||||||
enum KvType {
|
|
||||||
/**
|
|
||||||
* Local file storage.
|
|
||||||
*/
|
|
||||||
File,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local memory storage.
|
|
||||||
*/
|
|
||||||
Memory,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RocksDB storage.
|
|
||||||
*/
|
|
||||||
RocksDB,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get data by key.
|
|
||||||
*
|
|
||||||
* @param key byte[]
|
|
||||||
* @return byte[]
|
|
||||||
* @throws KvStorageException KVStorageException
|
|
||||||
*/
|
|
||||||
byte[] get(byte[] key) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* batch get by List byte[].
|
|
||||||
*
|
|
||||||
* @param keys List byte[]
|
|
||||||
* @return Map byte[], byte[]
|
|
||||||
* @throws KvStorageException KvStorageException
|
|
||||||
*/
|
|
||||||
Map<byte[], byte[]> batchGet(List<byte[]> keys) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write data.
|
|
||||||
*
|
|
||||||
* @param key byte[]
|
|
||||||
* @param value byte[]
|
|
||||||
* @throws KvStorageException KvStorageException
|
|
||||||
*/
|
|
||||||
void put(byte[] key, byte[] value) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* batch write.
|
|
||||||
*
|
|
||||||
* @param keys List byte[]
|
|
||||||
* @param values List byte[]
|
|
||||||
* @throws KvStorageException KvStorageException
|
|
||||||
*/
|
|
||||||
void batchPut(List<byte[]> keys, List<byte[]> values) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete with key.
|
|
||||||
*
|
|
||||||
* @param key byte[]
|
|
||||||
* @throws KvStorageException KvStorageException
|
|
||||||
*/
|
|
||||||
void delete(byte[] key) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* batch delete with keys.
|
|
||||||
*
|
|
||||||
* @param keys List byte[]
|
|
||||||
* @throws KvStorageException KvStorageException
|
|
||||||
*/
|
|
||||||
void batchDelete(List<byte[]> keys) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* do snapshot.
|
|
||||||
*
|
|
||||||
* @param backupPath snapshot file save path
|
|
||||||
* @throws KvStorageException KVStorageException
|
|
||||||
*/
|
|
||||||
void doSnapshot(final String backupPath) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* load snapshot.
|
|
||||||
*
|
|
||||||
* @param path The path to the snapshot file
|
|
||||||
* @throws KvStorageException KVStorageException
|
|
||||||
*/
|
|
||||||
void snapshotLoad(String path) throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all keys.
|
|
||||||
*
|
|
||||||
* @return all keys
|
|
||||||
* @throws KvStorageException KVStorageException
|
|
||||||
*/
|
|
||||||
List<byte[]> allKeys() throws KvStorageException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* shutdown.
|
|
||||||
*/
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.core.storage.kv;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.exception.ErrorCode;
|
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
|
||||||
import com.alipay.sofa.jraft.util.BytesUtil;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Realization of KV storage based on memory.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
public class MemoryKvStorage implements KvStorage {
|
|
||||||
|
|
||||||
private final Map<Key, byte[]> storage = new ConcurrentSkipListMap<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] get(byte[] key) throws KvStorageException {
|
|
||||||
return storage.get(new Key(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<byte[], byte[]> batchGet(List<byte[]> keys) throws KvStorageException {
|
|
||||||
Map<byte[], byte[]> result = new HashMap<>(keys.size());
|
|
||||||
for (byte[] key : keys) {
|
|
||||||
byte[] val = storage.get(new Key(key));
|
|
||||||
if (val != null) {
|
|
||||||
result.put(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(byte[] key, byte[] value) throws KvStorageException {
|
|
||||||
storage.put(new Key(key), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void batchPut(List<byte[]> keys, List<byte[]> values) throws KvStorageException {
|
|
||||||
if (keys.size() != values.size()) {
|
|
||||||
throw new KvStorageException(ErrorCode.KVStorageBatchWriteError.getCode(),
|
|
||||||
"key's size must be equal to value's size");
|
|
||||||
}
|
|
||||||
int size = keys.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
storage.put(new Key(keys.get(i)), values.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete(byte[] key) throws KvStorageException {
|
|
||||||
storage.remove(new Key(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void batchDelete(List<byte[]> keys) throws KvStorageException {
|
|
||||||
for (byte[] key : keys) {
|
|
||||||
storage.remove(new Key(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doSnapshot(String backupPath) throws KvStorageException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void snapshotLoad(String path) throws KvStorageException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<byte[]> allKeys() throws KvStorageException {
|
|
||||||
List<byte[]> result = new LinkedList<>();
|
|
||||||
for (Key each : storage.keySet()) {
|
|
||||||
result.add(each.origin);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
storage.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Key implements Comparable<Key> {
|
|
||||||
|
|
||||||
private final byte[] origin;
|
|
||||||
|
|
||||||
private Key(byte[] origin) {
|
|
||||||
this.origin = origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(Key o) {
|
|
||||||
return BytesUtil.compare(origin, o.origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Key key = (Key) o;
|
|
||||||
return Arrays.equals(origin, key.origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Arrays.hashCode(origin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 1999-2021 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.storage;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.FileKvStorage;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.junit.jupiter.api.AfterAll;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link FileKvStorage} unit tests.
|
|
||||||
*
|
|
||||||
* @author chenglu
|
|
||||||
* @date 2021-06-10 18:27
|
|
||||||
*/
|
|
||||||
class FileKvStorageTest {
|
|
||||||
|
|
||||||
private KvStorage kvStorage;
|
|
||||||
|
|
||||||
private String baseDir;
|
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
static void clean() {
|
|
||||||
String dir = System.getProperty("user.home") + File.separator + "nacos_file_kv_storage_test_brotherluxcq";
|
|
||||||
String backupDir = System.getProperty("user.home") + File.separator + "nacos_file_kv_storage_test_backup_brotherluxcq";
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileUtils.deleteDirectory(new File(dir));
|
|
||||||
FileUtils.deleteDirectory(new File(backupDir));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void init() {
|
|
||||||
try {
|
|
||||||
baseDir = System.getProperty("user.home");
|
|
||||||
String dir = baseDir + File.separator + "nacos_file_kv_storage_test_brotherluxcq";
|
|
||||||
kvStorage = StorageFactory.createKvStorage(KvStorage.KvType.File, null, dir);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testPutAndGetAndDelete() {
|
|
||||||
try {
|
|
||||||
byte[] key = "key".getBytes();
|
|
||||||
byte[] value = "value".getBytes();
|
|
||||||
kvStorage.put(key, value);
|
|
||||||
byte[] value1 = kvStorage.get(key);
|
|
||||||
assertArrayEquals(value, value1);
|
|
||||||
|
|
||||||
assertNotNull(kvStorage.allKeys());
|
|
||||||
|
|
||||||
kvStorage.delete(key);
|
|
||||||
assertNull(kvStorage.get(key));
|
|
||||||
|
|
||||||
kvStorage.put(key, value);
|
|
||||||
kvStorage.shutdown();
|
|
||||||
assertEquals(kvStorage.allKeys().size(), kvStorage.allKeys().size());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testBatchPutAndGet() {
|
|
||||||
try {
|
|
||||||
List<byte[]> keys = Arrays.asList("key1".getBytes(), "key2".getBytes());
|
|
||||||
List<byte[]> values = Arrays.asList("value1".getBytes(), "value2".getBytes());
|
|
||||||
kvStorage.batchPut(keys, values);
|
|
||||||
|
|
||||||
Map<byte[], byte[]> res = kvStorage.batchGet(keys);
|
|
||||||
assertNotNull(res);
|
|
||||||
|
|
||||||
res.forEach((key, value) -> {
|
|
||||||
if (Arrays.equals(key, "key1".getBytes())) {
|
|
||||||
assertArrayEquals("value1".getBytes(), value);
|
|
||||||
} else if (Arrays.equals(key, "key2".getBytes())) {
|
|
||||||
assertArrayEquals("value2".getBytes(), value);
|
|
||||||
} else {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
kvStorage.batchDelete(keys);
|
|
||||||
assertEquals(0, kvStorage.batchGet(values).size());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSnapshot() {
|
|
||||||
String backupDir = baseDir + File.separator + "nacos_file_kv_storage_test_backup_brotherluxcq";
|
|
||||||
try {
|
|
||||||
File file = new File(backupDir);
|
|
||||||
if (!file.exists()) {
|
|
||||||
boolean dirResult = file.mkdirs();
|
|
||||||
if (!dirResult) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kvStorage.doSnapshot(backupDir);
|
|
||||||
} catch (KvStorageException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
kvStorage.snapshotLoad(backupDir);
|
|
||||||
byte[] key = "key".getBytes();
|
|
||||||
byte[] value = kvStorage.get(key);
|
|
||||||
assertArrayEquals("value".getBytes(), value);
|
|
||||||
} catch (KvStorageException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 1999-2021 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.storage;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.MemoryKvStorage;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link MemoryKvStorage} unit tests.
|
|
||||||
*
|
|
||||||
* @author chenglu
|
|
||||||
* @date 2021-06-10 18:02
|
|
||||||
*/
|
|
||||||
class MemoryKvStorageTest {
|
|
||||||
|
|
||||||
private KvStorage kvStorage;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void init() {
|
|
||||||
try {
|
|
||||||
kvStorage = StorageFactory.createKvStorage(KvStorage.KvType.Memory, null, null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testPutAndGetAndDelete() {
|
|
||||||
try {
|
|
||||||
byte[] key = "key".getBytes();
|
|
||||||
byte[] value = "value".getBytes();
|
|
||||||
kvStorage.put(key, value);
|
|
||||||
byte[] value1 = kvStorage.get(key);
|
|
||||||
assertArrayEquals(value, value1);
|
|
||||||
|
|
||||||
assertNotNull(kvStorage.allKeys());
|
|
||||||
|
|
||||||
kvStorage.delete(key);
|
|
||||||
assertNull(kvStorage.get(key));
|
|
||||||
|
|
||||||
kvStorage.put(key, value);
|
|
||||||
kvStorage.shutdown();
|
|
||||||
assertEquals(0, kvStorage.allKeys().size());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testBatchPutAndGet() {
|
|
||||||
try {
|
|
||||||
List<byte[]> keys = Arrays.asList("key1".getBytes(), "key2".getBytes());
|
|
||||||
List<byte[]> values = Arrays.asList("value1".getBytes(), "value2".getBytes());
|
|
||||||
kvStorage.batchPut(keys, values);
|
|
||||||
|
|
||||||
Map<byte[], byte[]> res = kvStorage.batchGet(keys);
|
|
||||||
assertNotNull(res);
|
|
||||||
|
|
||||||
res.forEach((key, value) -> {
|
|
||||||
if (Arrays.equals(key, "key1".getBytes())) {
|
|
||||||
assertArrayEquals("value1".getBytes(), value);
|
|
||||||
} else if (Arrays.equals(key, "key2".getBytes())) {
|
|
||||||
assertArrayEquals("value2".getBytes(), value);
|
|
||||||
} else {
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
kvStorage.batchDelete(keys);
|
|
||||||
assertEquals(0, kvStorage.batchGet(values).size());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSnapshot() {
|
|
||||||
try {
|
|
||||||
kvStorage.doSnapshot("/");
|
|
||||||
} catch (Exception e) {
|
|
||||||
assertTrue(e instanceof UnsupportedOperationException);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
kvStorage.snapshotLoad("/");
|
|
||||||
} catch (Exception e) {
|
|
||||||
assertTrue(e instanceof UnsupportedOperationException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 1999-2021 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.storage;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.storage.kv.FileKvStorage;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.MemoryKvStorage;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link StorageFactory} unit tests.
|
|
||||||
*
|
|
||||||
* @author chenglu
|
|
||||||
* @date 2021-06-10 17:55
|
|
||||||
*/
|
|
||||||
class StorageFactoryTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCreateKvStorage() {
|
|
||||||
try {
|
|
||||||
KvStorage kvStorage = StorageFactory.createKvStorage(KvStorage.KvType.Memory, "", "/");
|
|
||||||
assertTrue(kvStorage instanceof MemoryKvStorage);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
KvStorage kvStorage = StorageFactory.createKvStorage(KvStorage.KvType.File, "", "/");
|
|
||||||
assertTrue(kvStorage instanceof FileKvStorage);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
StorageFactory.createKvStorage(KvStorage.KvType.RocksDB, "", "/");
|
|
||||||
} catch (Exception e) {
|
|
||||||
assertTrue(e instanceof IllegalArgumentException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,14 +16,15 @@
|
|||||||
|
|
||||||
package com.alibaba.nacos.naming.cluster;
|
package com.alibaba.nacos.naming.cluster;
|
||||||
|
|
||||||
import com.alibaba.nacos.naming.consistency.ConsistencyService;
|
import com.alibaba.nacos.common.utils.StringUtils;
|
||||||
|
import com.alibaba.nacos.core.distributed.ProtocolManager;
|
||||||
|
import com.alibaba.nacos.naming.constants.Constants;
|
||||||
import com.alibaba.nacos.naming.misc.GlobalExecutor;
|
import com.alibaba.nacos.naming.misc.GlobalExecutor;
|
||||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||||
import com.alibaba.nacos.common.utils.StringUtils;
|
import com.alipay.sofa.jraft.RouteTable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,14 +36,14 @@ import java.util.Optional;
|
|||||||
@Service
|
@Service
|
||||||
public class ServerStatusManager {
|
public class ServerStatusManager {
|
||||||
|
|
||||||
@Resource(name = "persistentConsistencyServiceDelegate")
|
private final ProtocolManager protocolManager;
|
||||||
private ConsistencyService consistencyService;
|
|
||||||
|
|
||||||
private final SwitchDomain switchDomain;
|
private final SwitchDomain switchDomain;
|
||||||
|
|
||||||
private ServerStatus serverStatus = ServerStatus.STARTING;
|
private ServerStatus serverStatus = ServerStatus.STARTING;
|
||||||
|
|
||||||
public ServerStatusManager(SwitchDomain switchDomain) {
|
public ServerStatusManager(ProtocolManager protocolManager, SwitchDomain switchDomain) {
|
||||||
|
this.protocolManager = protocolManager;
|
||||||
this.switchDomain = switchDomain;
|
this.switchDomain = switchDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,19 +59,30 @@ public class ServerStatusManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consistencyService.isAvailable()) {
|
if (hasLeader()) {
|
||||||
serverStatus = ServerStatus.UP;
|
serverStatus = ServerStatus.UP;
|
||||||
} else {
|
} else {
|
||||||
serverStatus = ServerStatus.DOWN;
|
serverStatus = ServerStatus.DOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasLeader() {
|
||||||
|
if (protocolManager.getCpProtocol() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return null != RouteTable.getInstance().selectLeader(Constants.NAMING_PERSISTENT_SERVICE_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
public ServerStatus getServerStatus() {
|
public ServerStatus getServerStatus() {
|
||||||
return serverStatus;
|
return serverStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getErrorMsg() {
|
public Optional<String> getErrorMsg() {
|
||||||
return consistencyService.getErrorMsg();
|
if (hasLeader()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of("No leader for raft group " + Constants.NAMING_PERSISTENT_SERVICE_GROUP
|
||||||
|
+ ", please see logs `alipay-jraft.log` or `naming-raft.log` to see details.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ServerStatusUpdater implements Runnable {
|
public class ServerStatusUpdater implements Runnable {
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consistence service for all implementations to derive.
|
|
||||||
*
|
|
||||||
* <p>We announce this consistency service to decouple the specific consistency implementation with business logic.
|
|
||||||
* User should not be aware of what consistency protocol is being used.
|
|
||||||
*
|
|
||||||
* <p>In this way, we also provide space for user to extend the underlying consistency protocols, as long as they obey
|
|
||||||
* our consistency baseline.
|
|
||||||
*
|
|
||||||
* @author nkorange
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public interface ConsistencyService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Put a data related to a key to Nacos cluster.
|
|
||||||
*
|
|
||||||
* @param key key of data, this key should be globally unique
|
|
||||||
* @param value value of data
|
|
||||||
* @throws NacosException nacos exception
|
|
||||||
*/
|
|
||||||
void put(String key, Record value) throws NacosException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a data from Nacos cluster.
|
|
||||||
*
|
|
||||||
* @param key key of data
|
|
||||||
* @throws NacosException nacos exception
|
|
||||||
*/
|
|
||||||
void remove(String key) throws NacosException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a data from Nacos cluster.
|
|
||||||
*
|
|
||||||
* @param key key of data
|
|
||||||
* @return data related to the key
|
|
||||||
* @throws NacosException nacos exception
|
|
||||||
*/
|
|
||||||
Datum get(String key) throws NacosException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen for changes of a data.
|
|
||||||
*
|
|
||||||
* @param key key of data
|
|
||||||
* @param listener callback of data change
|
|
||||||
* @throws NacosException nacos exception
|
|
||||||
*/
|
|
||||||
void listen(String key, RecordListener listener) throws NacosException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel listening of a data.
|
|
||||||
*
|
|
||||||
* @param key key of data
|
|
||||||
* @param listener callback of data change
|
|
||||||
* @throws NacosException nacos exception
|
|
||||||
*/
|
|
||||||
void unListen(String key, RecordListener listener) throws NacosException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the error message of the consistency protocol.
|
|
||||||
*
|
|
||||||
* @return the consistency protocol error message.
|
|
||||||
*/
|
|
||||||
Optional<String> getErrorMsg();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the status of this consistency service.
|
|
||||||
*
|
|
||||||
* @return true if available
|
|
||||||
*/
|
|
||||||
boolean isAvailable();
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data listener public interface.
|
|
||||||
*
|
|
||||||
* @author nacos
|
|
||||||
*/
|
|
||||||
public interface RecordListener<T extends Record> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the listener was registered with this key.
|
|
||||||
*
|
|
||||||
* @param key candidate key
|
|
||||||
* @return true if the listener was registered with this key
|
|
||||||
*/
|
|
||||||
boolean interests(String key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the listener is to be removed by matching the 'key'.
|
|
||||||
*
|
|
||||||
* @param key key to match
|
|
||||||
* @return true if match success
|
|
||||||
*/
|
|
||||||
boolean matchUnlistenKey(String key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action to do if data of target key has changed.
|
|
||||||
*
|
|
||||||
* @param key target key
|
|
||||||
* @param value data of the key
|
|
||||||
* @throws Exception exception
|
|
||||||
*/
|
|
||||||
void onChange(String key, T value) throws Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action to do if data of target key has been removed.
|
|
||||||
*
|
|
||||||
* @param key target key
|
|
||||||
* @throws Exception exception
|
|
||||||
*/
|
|
||||||
void onDelete(String key) throws Exception;
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.common.notify.Event;
|
|
||||||
import com.alibaba.nacos.consistency.DataOperation;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value changes events. //TODO Recipients need to implement the ability to receive batch events
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
public class ValueChangeEvent extends Event {
|
|
||||||
|
|
||||||
private final String key;
|
|
||||||
|
|
||||||
private final Record value;
|
|
||||||
|
|
||||||
private final DataOperation action;
|
|
||||||
|
|
||||||
public ValueChangeEvent(String key, Record value, DataOperation action) {
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
this.action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Record getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataOperation getAction() {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueChangeEventBuilder builder() {
|
|
||||||
return new ValueChangeEventBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class ValueChangeEventBuilder {
|
|
||||||
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
private Record value;
|
|
||||||
|
|
||||||
private DataOperation action;
|
|
||||||
|
|
||||||
private ValueChangeEventBuilder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueChangeEventBuilder key(String key) {
|
|
||||||
this.key = key;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueChangeEventBuilder value(Record value) {
|
|
||||||
this.value = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueChangeEventBuilder action(DataOperation action) {
|
|
||||||
this.action = action;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueChangeEvent build() {
|
|
||||||
return new ValueChangeEvent(key, value, action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.naming.consistency.ConsistencyService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A consistency service that guarantee CP consistency for the published data.
|
|
||||||
*
|
|
||||||
* <p>CP consistency is hereby defined as follows:
|
|
||||||
*
|
|
||||||
* <p>Once the writing operation returned client a success, the data within the operation is guaranteed to be
|
|
||||||
* successfully written to the cluster. And the data should be consistent between servers after some time without any
|
|
||||||
* outside interfere.
|
|
||||||
*
|
|
||||||
* @author nkorange
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public interface PersistentConsistencyService extends ConsistencyService {
|
|
||||||
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
|
||||||
import com.alibaba.nacos.core.distributed.ProtocolManager;
|
|
||||||
import com.alibaba.nacos.naming.consistency.Datum;
|
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
|
||||||
import com.alibaba.nacos.naming.consistency.persistent.impl.BasePersistentServiceProcessor;
|
|
||||||
import com.alibaba.nacos.naming.consistency.persistent.impl.PersistentServiceProcessor;
|
|
||||||
import com.alibaba.nacos.naming.consistency.persistent.impl.StandalonePersistentServiceProcessor;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
import com.alibaba.nacos.sys.env.EnvUtil;
|
|
||||||
import org.springframework.context.annotation.DependsOn;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persistent consistency service delegate.
|
|
||||||
*
|
|
||||||
* @author xiweng.yy
|
|
||||||
*/
|
|
||||||
@DependsOn("ProtocolManager")
|
|
||||||
@Component("persistentConsistencyServiceDelegate")
|
|
||||||
public class PersistentConsistencyServiceDelegateImpl implements PersistentConsistencyService {
|
|
||||||
|
|
||||||
private final BasePersistentServiceProcessor persistentServiceProcessor;
|
|
||||||
|
|
||||||
public PersistentConsistencyServiceDelegateImpl(ProtocolManager protocolManager) throws Exception {
|
|
||||||
this.persistentServiceProcessor = createPersistentServiceProcessor(protocolManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(String key, Record value) throws NacosException {
|
|
||||||
persistentServiceProcessor.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove(String key) throws NacosException {
|
|
||||||
persistentServiceProcessor.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Datum get(String key) throws NacosException {
|
|
||||||
return persistentServiceProcessor.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void listen(String key, RecordListener listener) throws NacosException {
|
|
||||||
persistentServiceProcessor.listen(key, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unListen(String key, RecordListener listener) throws NacosException {
|
|
||||||
persistentServiceProcessor.unListen(key, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return persistentServiceProcessor.isAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<String> getErrorMsg() {
|
|
||||||
return persistentServiceProcessor.getErrorMsg();
|
|
||||||
}
|
|
||||||
|
|
||||||
private BasePersistentServiceProcessor createPersistentServiceProcessor(ProtocolManager protocolManager)
|
|
||||||
throws Exception {
|
|
||||||
final BasePersistentServiceProcessor processor =
|
|
||||||
EnvUtil.getStandaloneMode() ? new StandalonePersistentServiceProcessor()
|
|
||||||
: new PersistentServiceProcessor(protocolManager);
|
|
||||||
processor.afterConstruct();
|
|
||||||
return processor;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.common.notify.Event;
|
|
||||||
import com.alibaba.nacos.common.notify.listener.Subscriber;
|
|
||||||
import com.alibaba.nacos.common.utils.ConcurrentHashSet;
|
|
||||||
import com.alibaba.nacos.consistency.DataOperation;
|
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
|
||||||
import com.alibaba.nacos.naming.consistency.ValueChangeEvent;
|
|
||||||
import com.alibaba.nacos.naming.misc.Loggers;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* persistent notifier, It is responsible for notifying interested listeners of all write changes to the data.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
public final class PersistentNotifier extends Subscriber<ValueChangeEvent> {
|
|
||||||
|
|
||||||
private final Map<String, ConcurrentHashSet<RecordListener>> listenerMap = new ConcurrentHashMap<>(32);
|
|
||||||
|
|
||||||
private final Function<String, Record> find;
|
|
||||||
|
|
||||||
public PersistentNotifier(Function<String, Record> find) {
|
|
||||||
this.find = find;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* register listener with key.
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
* @param listener {@link RecordListener}
|
|
||||||
*/
|
|
||||||
public void registerListener(final String key, final RecordListener listener) {
|
|
||||||
listenerMap.computeIfAbsent(key, s -> new ConcurrentHashSet<>()).add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* deregister listener by key.
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
* @param listener {@link RecordListener}
|
|
||||||
*/
|
|
||||||
public void deregisterListener(final String key, final RecordListener listener) {
|
|
||||||
if (!listenerMap.containsKey(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
listenerMap.get(key).remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* deregister all listener by key.
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
*/
|
|
||||||
public void deregisterAllListener(final String key) {
|
|
||||||
listenerMap.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, ConcurrentHashSet<RecordListener>> getListeners() {
|
|
||||||
return listenerMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* notify value to listener with {@link DataOperation} and key.
|
|
||||||
*
|
|
||||||
* @param key key
|
|
||||||
* @param action {@link DataOperation}
|
|
||||||
* @param value value
|
|
||||||
* @param <T> type
|
|
||||||
*/
|
|
||||||
public <T extends Record> void notify(final String key, final DataOperation action, final T value) {
|
|
||||||
|
|
||||||
if (!listenerMap.containsKey(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (RecordListener listener : listenerMap.get(key)) {
|
|
||||||
try {
|
|
||||||
if (action == DataOperation.CHANGE) {
|
|
||||||
listener.onChange(key, value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (action == DataOperation.DELETE) {
|
|
||||||
listener.onDelete(key);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Loggers.RAFT.error("[NACOS-RAFT] error while notifying listener of key: {}", key, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEvent(ValueChangeEvent event) {
|
|
||||||
notify(event.getKey(), event.getAction(), find.apply(event.getKey()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<? extends Event> subscribeType() {
|
|
||||||
return ValueChangeEvent.class;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,265 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent.impl;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
|
||||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
|
||||||
import com.alibaba.nacos.common.notify.NotifyCenter;
|
|
||||||
import com.alibaba.nacos.common.utils.ByteUtils;
|
|
||||||
import com.alibaba.nacos.common.utils.TypeUtils;
|
|
||||||
import com.alibaba.nacos.consistency.DataOperation;
|
|
||||||
import com.alibaba.nacos.consistency.SerializeFactory;
|
|
||||||
import com.alibaba.nacos.consistency.Serializer;
|
|
||||||
import com.alibaba.nacos.consistency.cp.RequestProcessor4CP;
|
|
||||||
import com.alibaba.nacos.consistency.entity.ReadRequest;
|
|
||||||
import com.alibaba.nacos.consistency.entity.Response;
|
|
||||||
import com.alibaba.nacos.consistency.entity.WriteRequest;
|
|
||||||
import com.alibaba.nacos.consistency.snapshot.SnapshotOperation;
|
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import com.alibaba.nacos.naming.consistency.Datum;
|
|
||||||
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
|
||||||
import com.alibaba.nacos.naming.consistency.ValueChangeEvent;
|
|
||||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
|
|
||||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentNotifier;
|
|
||||||
import com.alibaba.nacos.naming.constants.Constants;
|
|
||||||
import com.alibaba.nacos.naming.misc.Loggers;
|
|
||||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* New service data persistence handler.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP
|
|
||||||
implements PersistentConsistencyService {
|
|
||||||
|
|
||||||
enum Op {
|
|
||||||
/**
|
|
||||||
* write ops.
|
|
||||||
*/
|
|
||||||
Write("Write"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* read ops.
|
|
||||||
*/
|
|
||||||
Read("Read"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete ops.
|
|
||||||
*/
|
|
||||||
Delete("Delete");
|
|
||||||
|
|
||||||
protected final String desc;
|
|
||||||
|
|
||||||
Op(String desc) {
|
|
||||||
this.desc = desc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final KvStorage kvStorage;
|
|
||||||
|
|
||||||
protected final Serializer serializer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether an unrecoverable error occurred.
|
|
||||||
*/
|
|
||||||
protected volatile boolean hasError = false;
|
|
||||||
|
|
||||||
protected volatile String jRaftErrorMsg;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If use old raft, should not notify listener even new listener add.
|
|
||||||
*/
|
|
||||||
protected volatile boolean startNotify = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* During snapshot processing, the processing of other requests needs to be paused.
|
|
||||||
*/
|
|
||||||
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
|
||||||
|
|
||||||
protected final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
|
|
||||||
|
|
||||||
protected final PersistentNotifier notifier;
|
|
||||||
|
|
||||||
protected final int queueMaxSize = 16384;
|
|
||||||
|
|
||||||
protected final int priority = 10;
|
|
||||||
|
|
||||||
public BasePersistentServiceProcessor() throws Exception {
|
|
||||||
this.kvStorage = new NamingKvStorage(Paths.get(UtilsAndCommons.DATA_BASE_DIR, "data").toString());
|
|
||||||
this.serializer = SerializeFactory.getSerializer("JSON");
|
|
||||||
this.notifier = new PersistentNotifier(key -> {
|
|
||||||
try {
|
|
||||||
byte[] data = kvStorage.get(ByteUtils.toBytes(key));
|
|
||||||
Datum datum = serializer.deserialize(data, getDatumTypeFromKey(key));
|
|
||||||
return null != datum ? datum.value : null;
|
|
||||||
} catch (KvStorageException ex) {
|
|
||||||
throw new NacosRuntimeException(ex.getErrCode(), ex.getErrMsg());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new NacosRuntimeException(NacosException.SERVER_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void afterConstruct() {
|
|
||||||
NotifyCenter.registerToPublisher(ValueChangeEvent.class, queueMaxSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response onRequest(ReadRequest request) {
|
|
||||||
final Lock lock = readLock;
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
final List<byte[]> keys = serializer
|
|
||||||
.deserialize(request.getData().toByteArray(), TypeUtils.parameterize(List.class, byte[].class));
|
|
||||||
final Map<byte[], byte[]> result = kvStorage.batchGet(keys);
|
|
||||||
final BatchReadResponse response = new BatchReadResponse();
|
|
||||||
result.forEach(response::append);
|
|
||||||
return Response.newBuilder().setSuccess(true).setData(ByteString.copyFrom(serializer.serialize(response)))
|
|
||||||
.build();
|
|
||||||
} catch (KvStorageException e) {
|
|
||||||
return Response.newBuilder().setSuccess(false).setErrMsg(e.getErrMsg()).build();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Loggers.RAFT.warn("On read request failed, ", e);
|
|
||||||
return Response.newBuilder().setSuccess(false).setErrMsg(e.getMessage()).build();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response onApply(WriteRequest request) {
|
|
||||||
final byte[] data = request.getData().toByteArray();
|
|
||||||
final Lock lock = readLock;
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
final BatchWriteRequest bwRequest = serializer.deserialize(data, BatchWriteRequest.class);
|
|
||||||
final Op op = Op.valueOf(request.getOperation());
|
|
||||||
switch (op) {
|
|
||||||
case Write:
|
|
||||||
kvStorage.batchPut(bwRequest.getKeys(), bwRequest.getValues());
|
|
||||||
break;
|
|
||||||
case Delete:
|
|
||||||
kvStorage.batchDelete(bwRequest.getKeys());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return Response.newBuilder().setSuccess(false).setErrMsg("unsupport operation : " + op).build();
|
|
||||||
}
|
|
||||||
publishValueChangeEvent(op, bwRequest);
|
|
||||||
return Response.newBuilder().setSuccess(true).build();
|
|
||||||
} catch (KvStorageException e) {
|
|
||||||
return Response.newBuilder().setSuccess(false).setErrMsg(e.getErrMsg()).build();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Loggers.RAFT.warn("On apply write request failed, ", e);
|
|
||||||
return Response.newBuilder().setSuccess(false).setErrMsg(e.getMessage()).build();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void publishValueChangeEvent(final Op op, final BatchWriteRequest request) {
|
|
||||||
final List<byte[]> keys = request.getKeys();
|
|
||||||
final List<byte[]> values = request.getValues();
|
|
||||||
for (int i = 0; i < keys.size(); i++) {
|
|
||||||
final String key = new String(keys.get(i));
|
|
||||||
// Ignore old 1.x version data
|
|
||||||
if (!KeyBuilder.matchSwitchKey(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final Datum datum = serializer.deserialize(values.get(i), getDatumTypeFromKey(key));
|
|
||||||
final Record value = null != datum ? datum.value : null;
|
|
||||||
final ValueChangeEvent event = ValueChangeEvent.builder().key(key).value(value)
|
|
||||||
.action(Op.Delete.equals(op) ? DataOperation.DELETE : DataOperation.CHANGE).build();
|
|
||||||
NotifyCenter.publishEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String group() {
|
|
||||||
return Constants.NAMING_PERSISTENT_SERVICE_GROUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SnapshotOperation> loadSnapshotOperate() {
|
|
||||||
return Collections.singletonList(new NamingSnapshotOperation(this.kvStorage, lock, serializer));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable error) {
|
|
||||||
super.onError(error);
|
|
||||||
hasError = true;
|
|
||||||
jRaftErrorMsg = error.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Type getDatumTypeFromKey(String key) {
|
|
||||||
return TypeUtils.parameterize(Datum.class, getClassOfRecordFromKey(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<? extends Record> getClassOfRecordFromKey(String key) {
|
|
||||||
if (KeyBuilder.matchSwitchKey(key)) {
|
|
||||||
return com.alibaba.nacos.naming.misc.SwitchDomain.class;
|
|
||||||
}
|
|
||||||
return Record.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void notifierDatumIfAbsent(String key, RecordListener listener) throws NacosException {
|
|
||||||
if (KeyBuilder.SERVICE_META_KEY_PREFIX.equals(key)) {
|
|
||||||
notifierAllServiceMeta(listener);
|
|
||||||
} else {
|
|
||||||
Datum datum = get(key);
|
|
||||||
if (null != datum) {
|
|
||||||
notifierDatum(key, datum, listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This notify should only notify once during startup.
|
|
||||||
*/
|
|
||||||
private void notifierAllServiceMeta(RecordListener listener) throws NacosException {
|
|
||||||
for (byte[] each : kvStorage.allKeys()) {
|
|
||||||
String key = new String(each);
|
|
||||||
if (listener.interests(key)) {
|
|
||||||
Datum datum = get(key);
|
|
||||||
if (null != datum) {
|
|
||||||
notifierDatum(key, datum, listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifierDatum(String key, Datum datum, RecordListener listener) {
|
|
||||||
try {
|
|
||||||
listener.onChange(key, datum.value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Loggers.RAFT.error("NACOS-RAFT failed to notify listener", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,166 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent.impl;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.exception.ErrorCode;
|
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
|
||||||
import com.alibaba.nacos.core.storage.StorageFactory;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.MemoryKvStorage;
|
|
||||||
import com.alibaba.nacos.sys.utils.TimerContext;
|
|
||||||
import com.alibaba.nacos.naming.misc.Loggers;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kv storage implementation for naming.
|
|
||||||
*
|
|
||||||
* @author xiweng.yy
|
|
||||||
*/
|
|
||||||
public class NamingKvStorage extends MemoryKvStorage {
|
|
||||||
|
|
||||||
private static final String LOAD_SNAPSHOT = NamingKvStorage.class.getSimpleName() + ".snapshotLoad";
|
|
||||||
|
|
||||||
private static final String LABEL = "naming-persistent";
|
|
||||||
|
|
||||||
private final String baseDir;
|
|
||||||
|
|
||||||
private final KvStorage baseDirStorage;
|
|
||||||
|
|
||||||
public NamingKvStorage(final String baseDir) throws Exception {
|
|
||||||
this.baseDir = baseDir;
|
|
||||||
this.baseDirStorage = StorageFactory.createKvStorage(KvStorage.KvType.File, LABEL, baseDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] get(byte[] key) throws KvStorageException {
|
|
||||||
// First get the data from the memory Cache
|
|
||||||
byte[] result = super.get(key);
|
|
||||||
if (null == result) {
|
|
||||||
try {
|
|
||||||
KvStorage storage = getStorage();
|
|
||||||
result = null == storage ? null : storage.get(key);
|
|
||||||
if (null != result) {
|
|
||||||
super.put(key, result);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new KvStorageException(ErrorCode.KVStorageWriteError.getCode(),
|
|
||||||
"Get data failed, key: " + new String(key) + ", detail: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<byte[], byte[]> batchGet(List<byte[]> keys) throws KvStorageException {
|
|
||||||
Map<byte[], byte[]> result = new HashMap<>(keys.size());
|
|
||||||
for (byte[] key : keys) {
|
|
||||||
byte[] val = get(key);
|
|
||||||
if (val != null) {
|
|
||||||
result.put(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(byte[] key, byte[] value) throws KvStorageException {
|
|
||||||
try {
|
|
||||||
KvStorage storage = getStorage();
|
|
||||||
storage.put(key, value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new KvStorageException(ErrorCode.KVStorageWriteError.getCode(),
|
|
||||||
"Put data failed, key: " + new String(key) + ", detail: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
// after actual storage put success, put it in memory, memory put should success all the time
|
|
||||||
super.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void batchPut(List<byte[]> keys, List<byte[]> values) throws KvStorageException {
|
|
||||||
if (keys.size() != values.size()) {
|
|
||||||
throw new KvStorageException(ErrorCode.KVStorageBatchWriteError,
|
|
||||||
"key's size must be equal to value's size");
|
|
||||||
}
|
|
||||||
int size = keys.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
put(keys.get(i), values.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete(byte[] key) throws KvStorageException {
|
|
||||||
try {
|
|
||||||
KvStorage storage = getStorage();
|
|
||||||
if (null != storage) {
|
|
||||||
storage.delete(key);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new KvStorageException(ErrorCode.KVStorageDeleteError.getCode(),
|
|
||||||
"Delete data failed, key: " + new String(key) + ", detail: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
// after actual storage delete success, put it in memory, memory delete should success all the time
|
|
||||||
super.delete(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void batchDelete(List<byte[]> keys) throws KvStorageException {
|
|
||||||
for (byte[] each : keys) {
|
|
||||||
delete(each);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doSnapshot(String backupPath) throws KvStorageException {
|
|
||||||
baseDirStorage.doSnapshot(backupPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void snapshotLoad(String path) throws KvStorageException {
|
|
||||||
TimerContext.start(LOAD_SNAPSHOT);
|
|
||||||
try {
|
|
||||||
baseDirStorage.snapshotLoad(path);
|
|
||||||
loadSnapshotFromActualStorage(baseDirStorage);
|
|
||||||
} finally {
|
|
||||||
TimerContext.end(LOAD_SNAPSHOT, Loggers.RAFT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadSnapshotFromActualStorage(KvStorage actualStorage) throws KvStorageException {
|
|
||||||
for (byte[] each : actualStorage.allKeys()) {
|
|
||||||
byte[] datum = actualStorage.get(each);
|
|
||||||
super.put(each, datum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<byte[]> allKeys() throws KvStorageException {
|
|
||||||
return super.allKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
baseDirStorage.shutdown();
|
|
||||||
super.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private KvStorage getStorage() throws Exception {
|
|
||||||
return baseDirStorage;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 1999-2023 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.consistency.persistent.impl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter old version data operation {@link com.alibaba.nacos.naming.consistency.persistent.impl.BasePersistentServiceProcessor.Op}.
|
||||||
|
*
|
||||||
|
* @author xiweng.yy
|
||||||
|
*/
|
||||||
|
public enum OldDataOperation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write ops.
|
||||||
|
*/
|
||||||
|
Write("Write"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read ops.
|
||||||
|
*/
|
||||||
|
Read("Read"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete ops.
|
||||||
|
*/
|
||||||
|
Delete("Delete");
|
||||||
|
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
OldDataOperation(String desc) {
|
||||||
|
this.desc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDesc() {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
}
|
@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent.impl;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
|
||||||
import com.alibaba.nacos.common.notify.NotifyCenter;
|
|
||||||
import com.alibaba.nacos.common.utils.ByteUtils;
|
|
||||||
import com.alibaba.nacos.common.utils.StringUtils;
|
|
||||||
import com.alibaba.nacos.consistency.ProtocolMetaData;
|
|
||||||
import com.alibaba.nacos.consistency.cp.CPProtocol;
|
|
||||||
import com.alibaba.nacos.consistency.cp.MetadataKey;
|
|
||||||
import com.alibaba.nacos.consistency.entity.ReadRequest;
|
|
||||||
import com.alibaba.nacos.consistency.entity.Response;
|
|
||||||
import com.alibaba.nacos.consistency.entity.WriteRequest;
|
|
||||||
import com.alibaba.nacos.core.distributed.ProtocolManager;
|
|
||||||
import com.alibaba.nacos.core.exception.ErrorCode;
|
|
||||||
import com.alibaba.nacos.naming.consistency.Datum;
|
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
|
||||||
import com.alibaba.nacos.naming.constants.Constants;
|
|
||||||
import com.alibaba.nacos.naming.misc.Loggers;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
import com.alibaba.nacos.sys.env.EnvUtil;
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In cluster mode, start the Raft protocol.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
|
|
||||||
public class PersistentServiceProcessor extends BasePersistentServiceProcessor {
|
|
||||||
|
|
||||||
private final CPProtocol protocol;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is there a leader node currently.
|
|
||||||
*/
|
|
||||||
private volatile boolean hasLeader = false;
|
|
||||||
|
|
||||||
public PersistentServiceProcessor(ProtocolManager protocolManager) throws Exception {
|
|
||||||
this.protocol = protocolManager.getCpProtocol();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterConstruct() {
|
|
||||||
super.afterConstruct();
|
|
||||||
String raftGroup = Constants.NAMING_PERSISTENT_SERVICE_GROUP;
|
|
||||||
this.protocol.protocolMetaData().subscribe(raftGroup, MetadataKey.LEADER_META_DATA, o -> {
|
|
||||||
if (!(o instanceof ProtocolMetaData.ValueItem)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object leader = ((ProtocolMetaData.ValueItem) o).getData();
|
|
||||||
hasLeader = StringUtils.isNotBlank(String.valueOf(leader));
|
|
||||||
Loggers.RAFT.info("Raft group {} has leader {}", raftGroup, leader);
|
|
||||||
});
|
|
||||||
this.protocol.addRequestProcessors(Collections.singletonList(this));
|
|
||||||
// If you choose to use the new RAFT protocol directly, there will be no compatible logical execution
|
|
||||||
if (EnvUtil.getProperty(Constants.NACOS_NAMING_USE_NEW_RAFT_FIRST, Boolean.class, false)) {
|
|
||||||
NotifyCenter.registerSubscriber(notifier);
|
|
||||||
waitLeader();
|
|
||||||
startNotify = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitLeader() {
|
|
||||||
while (!hasLeader && !hasError) {
|
|
||||||
Loggers.RAFT.info("Waiting Jraft leader vote ...");
|
|
||||||
try {
|
|
||||||
TimeUnit.MILLISECONDS.sleep(500);
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(String key, Record value) throws NacosException {
|
|
||||||
final BatchWriteRequest req = new BatchWriteRequest();
|
|
||||||
Datum datum = Datum.createDatum(key, value);
|
|
||||||
req.append(ByteUtils.toBytes(key), serializer.serialize(datum));
|
|
||||||
final WriteRequest request = WriteRequest.newBuilder().setData(ByteString.copyFrom(serializer.serialize(req)))
|
|
||||||
.setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP).setOperation(Op.Write.desc).build();
|
|
||||||
try {
|
|
||||||
protocol.write(request);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new NacosException(ErrorCode.ProtoSubmitError.getCode(), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove(String key) throws NacosException {
|
|
||||||
final BatchWriteRequest req = new BatchWriteRequest();
|
|
||||||
req.append(ByteUtils.toBytes(key), ByteUtils.EMPTY);
|
|
||||||
final WriteRequest request = WriteRequest.newBuilder().setData(ByteString.copyFrom(serializer.serialize(req)))
|
|
||||||
.setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP).setOperation(Op.Delete.desc).build();
|
|
||||||
try {
|
|
||||||
protocol.write(request);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new NacosException(ErrorCode.ProtoSubmitError.getCode(), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Datum get(String key) throws NacosException {
|
|
||||||
final List<byte[]> keys = new ArrayList<>(1);
|
|
||||||
keys.add(ByteUtils.toBytes(key));
|
|
||||||
final ReadRequest req = ReadRequest.newBuilder().setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP)
|
|
||||||
.setData(ByteString.copyFrom(serializer.serialize(keys))).build();
|
|
||||||
try {
|
|
||||||
Response resp = protocol.getData(req);
|
|
||||||
if (resp.getSuccess()) {
|
|
||||||
BatchReadResponse response = serializer
|
|
||||||
.deserialize(resp.getData().toByteArray(), BatchReadResponse.class);
|
|
||||||
final List<byte[]> rValues = response.getValues();
|
|
||||||
return rValues.isEmpty() ? null : serializer.deserialize(rValues.get(0), getDatumTypeFromKey(key));
|
|
||||||
}
|
|
||||||
throw new NacosException(ErrorCode.ProtoReadError.getCode(), resp.getErrMsg());
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new NacosException(ErrorCode.ProtoReadError.getCode(), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void listen(String key, RecordListener listener) throws NacosException {
|
|
||||||
notifier.registerListener(key, listener);
|
|
||||||
if (startNotify) {
|
|
||||||
notifierDatumIfAbsent(key, listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unListen(String key, RecordListener listener) throws NacosException {
|
|
||||||
notifier.deregisterListener(key, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return hasLeader && !hasError;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<String> getErrorMsg() {
|
|
||||||
String errorMsg;
|
|
||||||
if (hasLeader && hasError) {
|
|
||||||
errorMsg = "The raft peer is in error: " + jRaftErrorMsg;
|
|
||||||
} else if (hasLeader && !hasError) {
|
|
||||||
errorMsg = null;
|
|
||||||
} else if (!hasLeader && hasError) {
|
|
||||||
errorMsg = "Could not find leader! And the raft peer is in error: " + jRaftErrorMsg;
|
|
||||||
} else {
|
|
||||||
errorMsg = "Could not find leader!";
|
|
||||||
}
|
|
||||||
return Optional.ofNullable(errorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent.impl;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
|
||||||
import com.alibaba.nacos.common.utils.ByteUtils;
|
|
||||||
import com.alibaba.nacos.consistency.entity.ReadRequest;
|
|
||||||
import com.alibaba.nacos.consistency.entity.Response;
|
|
||||||
import com.alibaba.nacos.consistency.entity.WriteRequest;
|
|
||||||
import com.alibaba.nacos.core.exception.ErrorCode;
|
|
||||||
import com.alibaba.nacos.naming.consistency.Datum;
|
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
|
||||||
import com.alibaba.nacos.naming.constants.Constants;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persistent service manipulation layer in stand-alone mode.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
|
|
||||||
public class StandalonePersistentServiceProcessor extends BasePersistentServiceProcessor {
|
|
||||||
|
|
||||||
public StandalonePersistentServiceProcessor() throws Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(String key, Record value) throws NacosException {
|
|
||||||
final BatchWriteRequest req = new BatchWriteRequest();
|
|
||||||
Datum datum = Datum.createDatum(key, value);
|
|
||||||
req.append(ByteUtils.toBytes(key), serializer.serialize(datum));
|
|
||||||
final WriteRequest request = WriteRequest.newBuilder().setData(ByteString.copyFrom(serializer.serialize(req)))
|
|
||||||
.setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP).setOperation(Op.Write.desc).build();
|
|
||||||
try {
|
|
||||||
onApply(request);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new NacosException(ErrorCode.ProtoSubmitError.getCode(), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove(String key) throws NacosException {
|
|
||||||
final BatchWriteRequest req = new BatchWriteRequest();
|
|
||||||
req.append(ByteUtils.toBytes(key), ByteUtils.EMPTY);
|
|
||||||
final WriteRequest request = WriteRequest.newBuilder().setData(ByteString.copyFrom(serializer.serialize(req)))
|
|
||||||
.setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP).setOperation(Op.Delete.desc).build();
|
|
||||||
try {
|
|
||||||
onApply(request);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new NacosException(ErrorCode.ProtoSubmitError.getCode(), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Datum get(String key) throws NacosException {
|
|
||||||
final List<byte[]> keys = Collections.singletonList(ByteUtils.toBytes(key));
|
|
||||||
final ReadRequest req = ReadRequest.newBuilder().setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP)
|
|
||||||
.setData(ByteString.copyFrom(serializer.serialize(keys))).build();
|
|
||||||
try {
|
|
||||||
final Response resp = onRequest(req);
|
|
||||||
if (resp.getSuccess()) {
|
|
||||||
BatchReadResponse response = serializer
|
|
||||||
.deserialize(resp.getData().toByteArray(), BatchReadResponse.class);
|
|
||||||
final List<byte[]> rValues = response.getValues();
|
|
||||||
return rValues.isEmpty() ? null : serializer.deserialize(rValues.get(0), getDatumTypeFromKey(key));
|
|
||||||
}
|
|
||||||
throw new NacosException(ErrorCode.ProtoReadError.getCode(), resp.getErrMsg());
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new NacosException(ErrorCode.ProtoReadError.getCode(), e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void listen(String key, RecordListener listener) throws NacosException {
|
|
||||||
notifier.registerListener(key, listener);
|
|
||||||
if (startNotify) {
|
|
||||||
notifierDatumIfAbsent(key, listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unListen(String key, RecordListener listener) throws NacosException {
|
|
||||||
notifier.deregisterListener(key, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return !hasError;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<String> getErrorMsg() {
|
|
||||||
String errorMsg;
|
|
||||||
if (hasError) {
|
|
||||||
errorMsg = "The raft peer is in error: " + jRaftErrorMsg;
|
|
||||||
} else {
|
|
||||||
errorMsg = null;
|
|
||||||
}
|
|
||||||
return Optional.ofNullable(errorMsg);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
* Copyright 1999-2023 Alibaba Group Holding Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,56 +14,40 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.alibaba.nacos.naming.consistency.persistent.impl;
|
package com.alibaba.nacos.naming.misc;
|
||||||
|
|
||||||
import com.alibaba.nacos.common.notify.NotifyCenter;
|
|
||||||
import com.alibaba.nacos.common.utils.TypeUtils;
|
|
||||||
import com.alibaba.nacos.consistency.DataOperation;
|
|
||||||
import com.alibaba.nacos.consistency.Serializer;
|
import com.alibaba.nacos.consistency.Serializer;
|
||||||
import com.alibaba.nacos.consistency.snapshot.LocalFileMeta;
|
import com.alibaba.nacos.consistency.snapshot.LocalFileMeta;
|
||||||
import com.alibaba.nacos.consistency.snapshot.Reader;
|
import com.alibaba.nacos.consistency.snapshot.Reader;
|
||||||
import com.alibaba.nacos.consistency.snapshot.Writer;
|
import com.alibaba.nacos.consistency.snapshot.Writer;
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
import com.alibaba.nacos.naming.consistency.persistent.impl.AbstractSnapshotOperation;
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import com.alibaba.nacos.naming.consistency.Datum;
|
|
||||||
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
|
||||||
import com.alibaba.nacos.naming.consistency.ValueChangeEvent;
|
|
||||||
import com.alibaba.nacos.naming.misc.Loggers;
|
|
||||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
|
||||||
import com.alibaba.nacos.sys.utils.DiskUtils;
|
import com.alibaba.nacos.sys.utils.DiskUtils;
|
||||||
import com.alipay.sofa.jraft.util.CRC64;
|
import com.alipay.sofa.jraft.util.CRC64;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.zip.Checksum;
|
import java.util.zip.Checksum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Snapshot processing of persistent service data for accelerated Raft protocol recovery and data synchronization.
|
* Switch Domain snapshot operation.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
|
||||||
* @author xiweng.yy
|
* @author xiweng.yy
|
||||||
*/
|
*/
|
||||||
public class NamingSnapshotOperation extends AbstractSnapshotOperation {
|
public class SwitchDomainSnapshotOperation extends AbstractSnapshotOperation {
|
||||||
|
|
||||||
private static final String NAMING_SNAPSHOT_SAVE = NamingSnapshotOperation.class.getSimpleName() + ".SAVE";
|
|
||||||
|
|
||||||
private static final String NAMING_SNAPSHOT_LOAD = NamingSnapshotOperation.class.getSimpleName() + ".LOAD";
|
|
||||||
|
|
||||||
private final String snapshotDir = "naming_persistent";
|
private final String snapshotDir = "naming_persistent";
|
||||||
|
|
||||||
private final String snapshotArchive = "naming_persistent.zip";
|
private final String snapshotArchive = "naming_persistent.zip";
|
||||||
|
|
||||||
private final KvStorage storage;
|
private final SwitchManager switchManager;
|
||||||
|
|
||||||
private final Serializer serializer;
|
private final Serializer serializer;
|
||||||
|
|
||||||
public NamingSnapshotOperation(KvStorage storage, ReentrantReadWriteLock lock, Serializer serializer) {
|
public SwitchDomainSnapshotOperation(ReentrantReadWriteLock lock, SwitchManager switchManager,
|
||||||
|
Serializer serializer) {
|
||||||
super(lock);
|
super(lock);
|
||||||
this.storage = storage;
|
this.switchManager = switchManager;
|
||||||
this.serializer = serializer;
|
this.serializer = serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +58,7 @@ public class NamingSnapshotOperation extends AbstractSnapshotOperation {
|
|||||||
DiskUtils.deleteDirectory(parentPath);
|
DiskUtils.deleteDirectory(parentPath);
|
||||||
DiskUtils.forceMkdir(parentPath);
|
DiskUtils.forceMkdir(parentPath);
|
||||||
|
|
||||||
storage.doSnapshot(parentPath);
|
this.switchManager.dumpSnapshot(parentPath);
|
||||||
final String outputFile = Paths.get(writePath, snapshotArchive).toString();
|
final String outputFile = Paths.get(writePath, snapshotArchive).toString();
|
||||||
final Checksum checksum = new CRC64();
|
final Checksum checksum = new CRC64();
|
||||||
DiskUtils.compress(writePath, snapshotDir, outputFile, checksum);
|
DiskUtils.compress(writePath, snapshotDir, outputFile, checksum);
|
||||||
@ -98,56 +82,19 @@ public class NamingSnapshotOperation extends AbstractSnapshotOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
final String loadPath = Paths.get(readerPath, snapshotDir).toString();
|
final String loadPath = Paths.get(readerPath, snapshotDir).toString();
|
||||||
storage.snapshotLoad(loadPath);
|
|
||||||
// publish value change
|
|
||||||
publishValueChangeEvent();
|
|
||||||
Loggers.RAFT.info("snapshot load from : {}", loadPath);
|
Loggers.RAFT.info("snapshot load from : {}", loadPath);
|
||||||
|
this.switchManager.loadSnapshot(loadPath);
|
||||||
DiskUtils.deleteDirectory(loadPath);
|
DiskUtils.deleteDirectory(loadPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* publish value change event.
|
|
||||||
*
|
|
||||||
* @throws KvStorageException throw to invoker
|
|
||||||
*/
|
|
||||||
private void publishValueChangeEvent() throws KvStorageException {
|
|
||||||
List<byte[]> keys = storage.allKeys();
|
|
||||||
for (int i = 0; i < keys.size(); i++) {
|
|
||||||
String key = new String(keys.get(i));
|
|
||||||
// Ignore old 1.x version data
|
|
||||||
if (!KeyBuilder.matchSwitchKey(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Datum datum = serializer.deserialize(storage.get(keys.get(i)), getDatumTypeFromKey(key));
|
|
||||||
Record value = (datum != null) ? datum.value : null;
|
|
||||||
// report for refreshing <code>SwitchDomain</code> message
|
|
||||||
if (value != null) {
|
|
||||||
ValueChangeEvent event = ValueChangeEvent.builder().key(key).value(value)
|
|
||||||
.action(DataOperation.CHANGE).build();
|
|
||||||
NotifyCenter.publishEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type getDatumTypeFromKey(String key) {
|
|
||||||
return TypeUtils.parameterize(Datum.class, getClassOfRecordFromKey(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<? extends Record> getClassOfRecordFromKey(String key) {
|
|
||||||
if (KeyBuilder.matchSwitchKey(key)) {
|
|
||||||
return SwitchDomain.class;
|
|
||||||
}
|
|
||||||
return Record.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getSnapshotSaveTag() {
|
protected String getSnapshotSaveTag() {
|
||||||
return NAMING_SNAPSHOT_SAVE;
|
return SwitchDomainSnapshotOperation.class.getSimpleName() + ".SAVE";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getSnapshotLoadTag() {
|
protected String getSnapshotLoadTag() {
|
||||||
return NAMING_SNAPSHOT_LOAD;
|
return SwitchDomainSnapshotOperation.class.getSimpleName() + ".LOAD";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,23 +18,43 @@ package com.alibaba.nacos.naming.misc;
|
|||||||
|
|
||||||
import com.alibaba.nacos.api.common.Constants;
|
import com.alibaba.nacos.api.common.Constants;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
|
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||||
|
import com.alibaba.nacos.common.utils.ByteUtils;
|
||||||
import com.alibaba.nacos.common.utils.ConvertUtils;
|
import com.alibaba.nacos.common.utils.ConvertUtils;
|
||||||
import com.alibaba.nacos.common.utils.JacksonUtils;
|
import com.alibaba.nacos.common.utils.JacksonUtils;
|
||||||
import com.alibaba.nacos.common.utils.StringUtils;
|
import com.alibaba.nacos.common.utils.StringUtils;
|
||||||
import com.alibaba.nacos.naming.consistency.ConsistencyService;
|
import com.alibaba.nacos.common.utils.TypeUtils;
|
||||||
|
import com.alibaba.nacos.consistency.SerializeFactory;
|
||||||
|
import com.alibaba.nacos.consistency.Serializer;
|
||||||
|
import com.alibaba.nacos.consistency.cp.RequestProcessor4CP;
|
||||||
|
import com.alibaba.nacos.consistency.entity.ReadRequest;
|
||||||
|
import com.alibaba.nacos.consistency.entity.Response;
|
||||||
|
import com.alibaba.nacos.consistency.entity.WriteRequest;
|
||||||
|
import com.alibaba.nacos.consistency.snapshot.SnapshotOperation;
|
||||||
|
import com.alibaba.nacos.core.distributed.ProtocolManager;
|
||||||
|
import com.alibaba.nacos.core.exception.ErrorCode;
|
||||||
import com.alibaba.nacos.naming.consistency.Datum;
|
import com.alibaba.nacos.naming.consistency.Datum;
|
||||||
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
import com.alibaba.nacos.naming.consistency.persistent.impl.BatchReadResponse;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import com.alibaba.nacos.naming.consistency.persistent.impl.BatchWriteRequest;
|
||||||
|
import com.alibaba.nacos.naming.consistency.persistent.impl.OldDataOperation;
|
||||||
|
import com.alibaba.nacos.naming.pojo.Record;
|
||||||
|
import com.alibaba.nacos.sys.utils.DiskUtils;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import java.io.File;
|
||||||
import javax.annotation.Resource;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch manager.
|
* Switch manager.
|
||||||
@ -43,27 +63,36 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class SwitchManager implements RecordListener<SwitchDomain> {
|
public class SwitchManager extends RequestProcessor4CP {
|
||||||
|
|
||||||
@Autowired
|
private final SwitchDomain switchDomain;
|
||||||
private SwitchDomain switchDomain;
|
|
||||||
|
|
||||||
@Resource(name = "persistentConsistencyServiceDelegate")
|
private final ProtocolManager protocolManager;
|
||||||
private ConsistencyService consistencyService;
|
|
||||||
|
|
||||||
ReentrantLock lock = new ReentrantLock();
|
private final ReentrantReadWriteLock raftLock;
|
||||||
|
|
||||||
/**
|
private final ReentrantLock requestLock;
|
||||||
* Init switch manager.
|
|
||||||
*/
|
private final Serializer serializer;
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
private final SwitchDomainSnapshotOperation snapshotOperation;
|
||||||
|
|
||||||
|
private final File dataFile;
|
||||||
|
|
||||||
|
public SwitchManager(SwitchDomain switchDomain, ProtocolManager protocolManager) {
|
||||||
|
this.switchDomain = switchDomain;
|
||||||
|
this.protocolManager = protocolManager;
|
||||||
|
this.raftLock = new ReentrantReadWriteLock();
|
||||||
|
this.requestLock = new ReentrantLock();
|
||||||
|
this.serializer = SerializeFactory.getSerializer("JSON");
|
||||||
|
this.snapshotOperation = new SwitchDomainSnapshotOperation(this.raftLock, this, this.serializer);
|
||||||
|
this.dataFile = Paths.get(UtilsAndCommons.DATA_BASE_DIR, "data", KeyBuilder.getSwitchDomainKey()).toFile();
|
||||||
try {
|
try {
|
||||||
consistencyService.listen(KeyBuilder.getSwitchDomainKey(), this);
|
DiskUtils.forceMkdir(this.dataFile.getParent());
|
||||||
} catch (NacosException e) {
|
} catch (IOException e) {
|
||||||
Loggers.SRV_LOG.error("listen switch service failed.", e);
|
Loggers.RAFT.error("Init Switch Domain directory failed: ", e);
|
||||||
}
|
}
|
||||||
|
protocolManager.getCpProtocol().addRequestProcessors(Collections.singletonList(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,22 +105,15 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
*/
|
*/
|
||||||
public void update(String entry, String value, boolean debug) throws Exception {
|
public void update(String entry, String value, boolean debug) throws Exception {
|
||||||
|
|
||||||
lock.lock();
|
this.requestLock.lock();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Datum datum = consistencyService.get(KeyBuilder.getSwitchDomainKey());
|
SwitchDomain tempSwitchDomain = this.switchDomain.clone();
|
||||||
SwitchDomain switchDomain;
|
|
||||||
|
|
||||||
if (datum != null && datum.value != null) {
|
|
||||||
switchDomain = (SwitchDomain) datum.value;
|
|
||||||
} else {
|
|
||||||
switchDomain = this.switchDomain.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SwitchEntry.BATCH.equals(entry)) {
|
if (SwitchEntry.BATCH.equals(entry)) {
|
||||||
//batch update
|
//batch update
|
||||||
SwitchDomain dom = JacksonUtils.toObj(value, SwitchDomain.class);
|
SwitchDomain dom = JacksonUtils.toObj(value, SwitchDomain.class);
|
||||||
dom.setEnableStandalone(switchDomain.isEnableStandalone());
|
dom.setEnableStandalone(tempSwitchDomain.isEnableStandalone());
|
||||||
if (dom.getHttpHealthParams().getMin() < SwitchDomain.HttpHealthParams.MIN_MIN
|
if (dom.getHttpHealthParams().getMin() < SwitchDomain.HttpHealthParams.MIN_MIN
|
||||||
|| dom.getTcpHealthParams().getMin() < SwitchDomain.HttpHealthParams.MIN_MIN) {
|
|| dom.getTcpHealthParams().getMin() < SwitchDomain.HttpHealthParams.MIN_MIN) {
|
||||||
|
|
||||||
@ -110,7 +132,7 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
throw new IllegalArgumentException("malformed factor");
|
throw new IllegalArgumentException("malformed factor");
|
||||||
}
|
}
|
||||||
|
|
||||||
switchDomain = dom;
|
tempSwitchDomain = dom;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.DISTRO_THRESHOLD)) {
|
if (entry.equals(SwitchEntry.DISTRO_THRESHOLD)) {
|
||||||
@ -118,12 +140,12 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
if (threshold <= 0) {
|
if (threshold <= 0) {
|
||||||
throw new IllegalArgumentException("distroThreshold can not be zero or negative: " + threshold);
|
throw new IllegalArgumentException("distroThreshold can not be zero or negative: " + threshold);
|
||||||
}
|
}
|
||||||
switchDomain.setDistroThreshold(threshold);
|
tempSwitchDomain.setDistroThreshold(threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.CLIENT_BEAT_INTERVAL)) {
|
if (entry.equals(SwitchEntry.CLIENT_BEAT_INTERVAL)) {
|
||||||
long clientBeatInterval = Long.parseLong(value);
|
long clientBeatInterval = Long.parseLong(value);
|
||||||
switchDomain.setClientBeatInterval(clientBeatInterval);
|
tempSwitchDomain.setClientBeatInterval(clientBeatInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.PUSH_VERSION)) {
|
if (entry.equals(SwitchEntry.PUSH_VERSION)) {
|
||||||
@ -137,13 +159,13 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.equals(SwitchEntry.CLIENT_JAVA, type)) {
|
if (StringUtils.equals(SwitchEntry.CLIENT_JAVA, type)) {
|
||||||
switchDomain.setPushJavaVersion(version);
|
tempSwitchDomain.setPushJavaVersion(version);
|
||||||
} else if (StringUtils.equals(SwitchEntry.CLIENT_PYTHON, type)) {
|
} else if (StringUtils.equals(SwitchEntry.CLIENT_PYTHON, type)) {
|
||||||
switchDomain.setPushPythonVersion(version);
|
tempSwitchDomain.setPushPythonVersion(version);
|
||||||
} else if (StringUtils.equals(SwitchEntry.CLIENT_C, type)) {
|
} else if (StringUtils.equals(SwitchEntry.CLIENT_C, type)) {
|
||||||
switchDomain.setPushCVersion(version);
|
tempSwitchDomain.setPushCVersion(version);
|
||||||
} else if (StringUtils.equals(SwitchEntry.CLIENT_GO, type)) {
|
} else if (StringUtils.equals(SwitchEntry.CLIENT_GO, type)) {
|
||||||
switchDomain.setPushGoVersion(version);
|
tempSwitchDomain.setPushGoVersion(version);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("unsupported client type: " + type);
|
throw new IllegalArgumentException("unsupported client type: " + type);
|
||||||
}
|
}
|
||||||
@ -156,7 +178,7 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
throw new IllegalArgumentException("min cache time for http or tcp is too small(<10000)");
|
throw new IllegalArgumentException("min cache time for http or tcp is too small(<10000)");
|
||||||
}
|
}
|
||||||
|
|
||||||
switchDomain.setDefaultPushCacheMillis(cacheMillis);
|
tempSwitchDomain.setDefaultPushCacheMillis(cacheMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extremely careful while modifying this, cause it will affect all clients without pushing enabled
|
// extremely careful while modifying this, cause it will affect all clients without pushing enabled
|
||||||
@ -167,27 +189,27 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
throw new IllegalArgumentException("min default cache time is too small(<1000)");
|
throw new IllegalArgumentException("min default cache time is too small(<1000)");
|
||||||
}
|
}
|
||||||
|
|
||||||
switchDomain.setDefaultCacheMillis(cacheMillis);
|
tempSwitchDomain.setDefaultCacheMillis(cacheMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.MASTERS)) {
|
if (entry.equals(SwitchEntry.MASTERS)) {
|
||||||
List<String> masters = Arrays.asList(value.split(","));
|
List<String> masters = Arrays.asList(value.split(","));
|
||||||
switchDomain.setMasters(masters);
|
tempSwitchDomain.setMasters(masters);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.DISTRO)) {
|
if (entry.equals(SwitchEntry.DISTRO)) {
|
||||||
boolean enabled = Boolean.parseBoolean(value);
|
boolean enabled = Boolean.parseBoolean(value);
|
||||||
switchDomain.setDistroEnabled(enabled);
|
tempSwitchDomain.setDistroEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.CHECK)) {
|
if (entry.equals(SwitchEntry.CHECK)) {
|
||||||
boolean enabled = Boolean.parseBoolean(value);
|
boolean enabled = Boolean.parseBoolean(value);
|
||||||
switchDomain.setHealthCheckEnabled(enabled);
|
tempSwitchDomain.setHealthCheckEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.PUSH_ENABLED)) {
|
if (entry.equals(SwitchEntry.PUSH_ENABLED)) {
|
||||||
boolean enabled = Boolean.parseBoolean(value);
|
boolean enabled = Boolean.parseBoolean(value);
|
||||||
switchDomain.setPushEnabled(enabled);
|
tempSwitchDomain.setPushEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.SERVICE_STATUS_SYNC_PERIOD)) {
|
if (entry.equals(SwitchEntry.SERVICE_STATUS_SYNC_PERIOD)) {
|
||||||
@ -197,7 +219,7 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
throw new IllegalArgumentException("serviceStatusSynchronizationPeriodMillis is too small(<5000)");
|
throw new IllegalArgumentException("serviceStatusSynchronizationPeriodMillis is too small(<5000)");
|
||||||
}
|
}
|
||||||
|
|
||||||
switchDomain.setServiceStatusSynchronizationPeriodMillis(millis);
|
tempSwitchDomain.setServiceStatusSynchronizationPeriodMillis(millis);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.SERVER_STATUS_SYNC_PERIOD)) {
|
if (entry.equals(SwitchEntry.SERVER_STATUS_SYNC_PERIOD)) {
|
||||||
@ -207,25 +229,25 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
throw new IllegalArgumentException("serverStatusSynchronizationPeriodMillis is too small(<15000)");
|
throw new IllegalArgumentException("serverStatusSynchronizationPeriodMillis is too small(<15000)");
|
||||||
}
|
}
|
||||||
|
|
||||||
switchDomain.setServerStatusSynchronizationPeriodMillis(millis);
|
tempSwitchDomain.setServerStatusSynchronizationPeriodMillis(millis);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.HEALTH_CHECK_TIMES)) {
|
if (entry.equals(SwitchEntry.HEALTH_CHECK_TIMES)) {
|
||||||
int times = Integer.parseInt(value);
|
int times = Integer.parseInt(value);
|
||||||
|
|
||||||
switchDomain.setCheckTimes(times);
|
tempSwitchDomain.setCheckTimes(times);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.DISABLE_ADD_IP)) {
|
if (entry.equals(SwitchEntry.DISABLE_ADD_IP)) {
|
||||||
boolean disableAddIp = Boolean.parseBoolean(value);
|
boolean disableAddIp = Boolean.parseBoolean(value);
|
||||||
|
|
||||||
switchDomain.setDisableAddIP(disableAddIp);
|
tempSwitchDomain.setDisableAddIP(disableAddIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.SEND_BEAT_ONLY)) {
|
if (entry.equals(SwitchEntry.SEND_BEAT_ONLY)) {
|
||||||
boolean sendBeatOnly = Boolean.parseBoolean(value);
|
boolean sendBeatOnly = Boolean.parseBoolean(value);
|
||||||
|
|
||||||
switchDomain.setSendBeatOnly(sendBeatOnly);
|
tempSwitchDomain.setSendBeatOnly(sendBeatOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.LIMITED_URL_MAP)) {
|
if (entry.equals(SwitchEntry.LIMITED_URL_MAP)) {
|
||||||
@ -253,14 +275,14 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switchDomain.setLimitedUrlMap(limitedUrlMap);
|
tempSwitchDomain.setLimitedUrlMap(limitedUrlMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.ENABLE_STANDALONE)) {
|
if (entry.equals(SwitchEntry.ENABLE_STANDALONE)) {
|
||||||
|
|
||||||
if (!StringUtils.isNotEmpty(value)) {
|
if (!StringUtils.isNotEmpty(value)) {
|
||||||
switchDomain.setEnableStandalone(Boolean.parseBoolean(value));
|
tempSwitchDomain.setEnableStandalone(Boolean.parseBoolean(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,33 +291,33 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
if (Constants.NULL_STRING.equals(status)) {
|
if (Constants.NULL_STRING.equals(status)) {
|
||||||
status = StringUtils.EMPTY;
|
status = StringUtils.EMPTY;
|
||||||
}
|
}
|
||||||
switchDomain.setOverriddenServerStatus(status);
|
tempSwitchDomain.setOverriddenServerStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.DEFAULT_INSTANCE_EPHEMERAL)) {
|
if (entry.equals(SwitchEntry.DEFAULT_INSTANCE_EPHEMERAL)) {
|
||||||
switchDomain.setDefaultInstanceEphemeral(Boolean.parseBoolean(value));
|
tempSwitchDomain.setDefaultInstanceEphemeral(Boolean.parseBoolean(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.DISTRO_SERVER_EXPIRED_MILLIS)) {
|
if (entry.equals(SwitchEntry.DISTRO_SERVER_EXPIRED_MILLIS)) {
|
||||||
switchDomain.setDistroServerExpiredMillis(Long.parseLong(value));
|
tempSwitchDomain.setDistroServerExpiredMillis(Long.parseLong(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.LIGHT_BEAT_ENABLED)) {
|
if (entry.equals(SwitchEntry.LIGHT_BEAT_ENABLED)) {
|
||||||
switchDomain.setLightBeatEnabled(ConvertUtils.toBoolean(value));
|
tempSwitchDomain.setLightBeatEnabled(ConvertUtils.toBoolean(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.equals(SwitchEntry.AUTO_CHANGE_HEALTH_CHECK_ENABLED)) {
|
if (entry.equals(SwitchEntry.AUTO_CHANGE_HEALTH_CHECK_ENABLED)) {
|
||||||
switchDomain.setAutoChangeHealthCheckEnabled(ConvertUtils.toBoolean(value));
|
tempSwitchDomain.setAutoChangeHealthCheckEnabled(ConvertUtils.toBoolean(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
update(switchDomain);
|
update(tempSwitchDomain);
|
||||||
} else {
|
} else {
|
||||||
consistencyService.put(KeyBuilder.getSwitchDomainKey(), switchDomain);
|
updateWithConsistency(tempSwitchDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
this.requestLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -340,27 +362,145 @@ public class SwitchManager implements RecordListener<SwitchDomain> {
|
|||||||
switchDomain.setLightBeatEnabled(newSwitchDomain.isLightBeatEnabled());
|
switchDomain.setLightBeatEnabled(newSwitchDomain.isLightBeatEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateWithConsistency(SwitchDomain tempSwitchDomain) throws NacosException {
|
||||||
|
try {
|
||||||
|
final BatchWriteRequest req = new BatchWriteRequest();
|
||||||
|
String switchDomainKey = KeyBuilder.getSwitchDomainKey();
|
||||||
|
Datum datum = Datum.createDatum(switchDomainKey, tempSwitchDomain);
|
||||||
|
req.append(ByteUtils.toBytes(switchDomainKey), serializer.serialize(datum));
|
||||||
|
WriteRequest operationLog = WriteRequest.newBuilder().setGroup(group())
|
||||||
|
.setOperation(OldDataOperation.Write.getDesc()).setData(ByteString.copyFrom(serializer.serialize(req)))
|
||||||
|
.build();
|
||||||
|
protocolManager.getCpProtocol().write(operationLog);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Loggers.RAFT.error("Submit switch domain failed: ", e);
|
||||||
|
throw new NacosException(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SwitchDomain getSwitchDomain() {
|
public SwitchDomain getSwitchDomain() {
|
||||||
return switchDomain;
|
return switchDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean interests(String key) {
|
public List<SnapshotOperation> loadSnapshotOperate() {
|
||||||
return KeyBuilder.matchSwitchKey(key);
|
return Collections.singletonList(snapshotOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load Snapshot from snapshot dir.
|
||||||
|
*
|
||||||
|
* @param snapshotPath snapshot dir
|
||||||
|
*/
|
||||||
|
public void loadSnapshot(String snapshotPath) {
|
||||||
|
this.raftLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
File srcDir = Paths.get(snapshotPath).toFile();
|
||||||
|
// If snapshot path is non-exist, means snapshot is empty
|
||||||
|
if (srcDir.exists()) {
|
||||||
|
// First clean up the local file information, before the file copy
|
||||||
|
String baseDir = this.dataFile.getParent();
|
||||||
|
DiskUtils.deleteDirThenMkdir(baseDir);
|
||||||
|
File descDir = Paths.get(baseDir).toFile();
|
||||||
|
DiskUtils.copyDirectory(srcDir, descDir);
|
||||||
|
if (!this.dataFile.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
byte[] snapshotData = DiskUtils.readFileBytes(this.dataFile);
|
||||||
|
final Datum datum = serializer.deserialize(snapshotData, getDatumType());
|
||||||
|
final Record value = null != datum ? datum.value : null;
|
||||||
|
if (!(value instanceof SwitchDomain)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
update((SwitchDomain) value);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new NacosRuntimeException(ErrorCode.IOCopyDirError.getCode(), e);
|
||||||
|
} finally {
|
||||||
|
this.raftLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump data from data dir to snapshot dir.
|
||||||
|
*
|
||||||
|
* @param backupPath snapshot dir
|
||||||
|
*/
|
||||||
|
public void dumpSnapshot(String backupPath) {
|
||||||
|
this.raftLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
File srcDir = Paths.get(this.dataFile.getParent()).toFile();
|
||||||
|
File descDir = Paths.get(backupPath).toFile();
|
||||||
|
DiskUtils.copyDirectory(srcDir, descDir);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new NacosRuntimeException(ErrorCode.IOCopyDirError.getCode(), e);
|
||||||
|
} finally {
|
||||||
|
this.raftLock.writeLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matchUnlistenKey(String key) {
|
public Response onRequest(ReadRequest request) {
|
||||||
return KeyBuilder.matchSwitchKey(key);
|
this.raftLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
final List<byte[]> keys = serializer.deserialize(request.getData().toByteArray(),
|
||||||
|
TypeUtils.parameterize(List.class, byte[].class));
|
||||||
|
if (isNotSwitchDomainKey(keys)) {
|
||||||
|
return Response.newBuilder().setSuccess(false).setErrMsg("not switch domain key").build();
|
||||||
|
}
|
||||||
|
Datum datum = Datum.createDatum(KeyBuilder.getSwitchDomainKey(), switchDomain);
|
||||||
|
final BatchReadResponse response = new BatchReadResponse();
|
||||||
|
response.append(ByteUtils.toBytes(KeyBuilder.getSwitchDomainKey()), serializer.serialize(datum));
|
||||||
|
return Response.newBuilder().setSuccess(true).setData(ByteString.copyFrom(serializer.serialize(response)))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Loggers.RAFT.warn("On read switch domain failed, ", e);
|
||||||
|
return Response.newBuilder().setSuccess(false).setErrMsg(e.getMessage()).build();
|
||||||
|
} finally {
|
||||||
|
this.raftLock.readLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChange(String key, SwitchDomain domain) throws Exception {
|
public Response onApply(WriteRequest log) {
|
||||||
update(domain);
|
this.raftLock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
BatchWriteRequest bwRequest = serializer.deserialize(log.getData().toByteArray(), BatchWriteRequest.class);
|
||||||
|
if (isNotSwitchDomainKey(bwRequest.getKeys())) {
|
||||||
|
return Response.newBuilder().setSuccess(false).setErrMsg("not switch domain key").build();
|
||||||
|
}
|
||||||
|
final Datum datum = serializer.deserialize(bwRequest.getValues().get(0), getDatumType());
|
||||||
|
final Record value = null != datum ? datum.value : null;
|
||||||
|
if (!(value instanceof SwitchDomain)) {
|
||||||
|
return Response.newBuilder().setSuccess(false).setErrMsg("datum is not switch domain").build();
|
||||||
|
}
|
||||||
|
DiskUtils.touch(dataFile);
|
||||||
|
DiskUtils.writeFile(dataFile, bwRequest.getValues().get(0), false);
|
||||||
|
SwitchDomain switchDomain = (SwitchDomain) value;
|
||||||
|
update(switchDomain);
|
||||||
|
return Response.newBuilder().setSuccess(true).build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Loggers.RAFT.warn("On apply switch domain failed, ", e);
|
||||||
|
return Response.newBuilder().setSuccess(false).setErrMsg(e.getMessage()).build();
|
||||||
|
} finally {
|
||||||
|
this.raftLock.writeLock().unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDelete(String key) throws Exception {
|
public String group() {
|
||||||
|
return com.alibaba.nacos.naming.constants.Constants.NAMING_PERSISTENT_SERVICE_GROUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNotSwitchDomainKey(List<byte[]> keys) {
|
||||||
|
if (1 != keys.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String keyString = new String(keys.get(0));
|
||||||
|
return !KeyBuilder.getSwitchDomainKey().equals(keyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type getDatumType() {
|
||||||
|
return TypeUtils.parameterize(Datum.class, SwitchDomain.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,27 +18,44 @@
|
|||||||
|
|
||||||
package com.alibaba.nacos.naming.cluster;
|
package com.alibaba.nacos.naming.cluster;
|
||||||
|
|
||||||
import com.alibaba.nacos.naming.consistency.ConsistencyService;
|
import com.alibaba.nacos.consistency.cp.CPProtocol;
|
||||||
|
import com.alibaba.nacos.core.distributed.ProtocolManager;
|
||||||
|
import com.alibaba.nacos.naming.constants.Constants;
|
||||||
import com.alibaba.nacos.naming.misc.GlobalExecutor;
|
import com.alibaba.nacos.naming.misc.GlobalExecutor;
|
||||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||||
import com.alibaba.nacos.sys.env.EnvUtil;
|
import com.alibaba.nacos.sys.env.EnvUtil;
|
||||||
|
import com.alipay.sofa.jraft.RouteTable;
|
||||||
|
import com.alipay.sofa.jraft.entity.PeerId;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
class ServerStatusManagerTest {
|
class ServerStatusManagerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
SwitchDomain switchDomain;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ProtocolManager protocolManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
CPProtocol cpProtocol;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
EnvUtil.setEnvironment(new MockEnvironment());
|
EnvUtil.setEnvironment(new MockEnvironment());
|
||||||
@ -47,7 +64,7 @@ class ServerStatusManagerTest {
|
|||||||
@Test
|
@Test
|
||||||
void testInit() {
|
void testInit() {
|
||||||
try (MockedStatic mocked = mockStatic(GlobalExecutor.class)) {
|
try (MockedStatic mocked = mockStatic(GlobalExecutor.class)) {
|
||||||
ServerStatusManager serverStatusManager = new ServerStatusManager(mock(SwitchDomain.class));
|
ServerStatusManager serverStatusManager = new ServerStatusManager(protocolManager, switchDomain);
|
||||||
serverStatusManager.init();
|
serverStatusManager.init();
|
||||||
mocked.verify(() -> GlobalExecutor.registerServerStatusUpdater(any()));
|
mocked.verify(() -> GlobalExecutor.registerServerStatusUpdater(any()));
|
||||||
}
|
}
|
||||||
@ -55,30 +72,24 @@ class ServerStatusManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGetServerStatus() {
|
void testGetServerStatus() {
|
||||||
ServerStatusManager serverStatusManager = new ServerStatusManager(mock(SwitchDomain.class));
|
ServerStatusManager serverStatusManager = new ServerStatusManager(protocolManager, switchDomain);
|
||||||
ServerStatus serverStatus = serverStatusManager.getServerStatus();
|
ServerStatus serverStatus = serverStatusManager.getServerStatus();
|
||||||
assertEquals(ServerStatus.STARTING, serverStatus);
|
assertEquals(ServerStatus.STARTING, serverStatus);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGetErrorMsg() throws NoSuchFieldException, IllegalAccessException {
|
void testGetErrorMsg() {
|
||||||
ServerStatusManager serverStatusManager = new ServerStatusManager(mock(SwitchDomain.class));
|
ServerStatusManager serverStatusManager = new ServerStatusManager(protocolManager, switchDomain);
|
||||||
Field field = ServerStatusManager.class.getDeclaredField("consistencyService");
|
|
||||||
field.setAccessible(true);
|
|
||||||
ConsistencyService consistencyService = mock(ConsistencyService.class);
|
|
||||||
when(consistencyService.getErrorMsg()).thenReturn(Optional.empty());
|
|
||||||
field.set(serverStatusManager, consistencyService);
|
|
||||||
Optional<String> errorMsg = serverStatusManager.getErrorMsg();
|
Optional<String> errorMsg = serverStatusManager.getErrorMsg();
|
||||||
assertFalse(errorMsg.isPresent());
|
assertTrue(errorMsg.isPresent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdaterFromSwitch() {
|
void testUpdaterFromSwitch() {
|
||||||
SwitchDomain switchDomain = mock(SwitchDomain.class);
|
|
||||||
String expect = ServerStatus.DOWN.toString();
|
String expect = ServerStatus.DOWN.toString();
|
||||||
when(switchDomain.getOverriddenServerStatus()).thenReturn(expect);
|
when(switchDomain.getOverriddenServerStatus()).thenReturn(expect);
|
||||||
ServerStatusManager serverStatusManager = new ServerStatusManager(switchDomain);
|
ServerStatusManager serverStatusManager = new ServerStatusManager(protocolManager, switchDomain);
|
||||||
ServerStatusManager.ServerStatusUpdater updater = serverStatusManager.new ServerStatusUpdater();
|
ServerStatusManager.ServerStatusUpdater updater = serverStatusManager.new ServerStatusUpdater();
|
||||||
//then
|
//then
|
||||||
updater.run();
|
updater.run();
|
||||||
@ -88,30 +99,36 @@ class ServerStatusManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdaterFromConsistency1() throws NoSuchFieldException, IllegalAccessException {
|
void testUpdaterFromConsistency1() {
|
||||||
SwitchDomain switchDomain = mock(SwitchDomain.class);
|
try (MockedStatic mocked = mockStatic(RouteTable.class)) {
|
||||||
ServerStatusManager serverStatusManager = new ServerStatusManager(switchDomain);
|
RouteTable mockTable = mock(RouteTable.class);
|
||||||
Field field = ServerStatusManager.class.getDeclaredField("consistencyService");
|
when(mockTable.selectLeader(Constants.NAMING_PERSISTENT_SERVICE_GROUP)).thenReturn(new PeerId());
|
||||||
field.setAccessible(true);
|
mocked.when(RouteTable::getInstance).thenReturn(mockTable);
|
||||||
ConsistencyService consistencyService = mock(ConsistencyService.class);
|
when(protocolManager.getCpProtocol()).thenReturn(cpProtocol);
|
||||||
when(consistencyService.isAvailable()).thenReturn(true);
|
ServerStatusManager serverStatusManager = new ServerStatusManager(protocolManager, switchDomain);
|
||||||
field.set(serverStatusManager, consistencyService);
|
ServerStatusManager.ServerStatusUpdater updater = serverStatusManager.new ServerStatusUpdater();
|
||||||
|
//then
|
||||||
|
updater.run();
|
||||||
|
//then
|
||||||
|
assertEquals(ServerStatus.UP, serverStatusManager.getServerStatus());
|
||||||
|
assertFalse(serverStatusManager.getErrorMsg().isPresent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdaterFromConsistency2() {
|
||||||
|
when(protocolManager.getCpProtocol()).thenReturn(cpProtocol);
|
||||||
|
ServerStatusManager serverStatusManager = new ServerStatusManager(protocolManager, switchDomain);
|
||||||
ServerStatusManager.ServerStatusUpdater updater = serverStatusManager.new ServerStatusUpdater();
|
ServerStatusManager.ServerStatusUpdater updater = serverStatusManager.new ServerStatusUpdater();
|
||||||
//then
|
//then
|
||||||
updater.run();
|
updater.run();
|
||||||
//then
|
//then
|
||||||
assertEquals(ServerStatus.UP, serverStatusManager.getServerStatus());
|
assertEquals(ServerStatus.DOWN, serverStatusManager.getServerStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdaterFromConsistency2() throws NoSuchFieldException, IllegalAccessException {
|
void testUpdaterFromConsistency3() {
|
||||||
SwitchDomain switchDomain = mock(SwitchDomain.class);
|
ServerStatusManager serverStatusManager = new ServerStatusManager(protocolManager, switchDomain);
|
||||||
ServerStatusManager serverStatusManager = new ServerStatusManager(switchDomain);
|
|
||||||
Field field = ServerStatusManager.class.getDeclaredField("consistencyService");
|
|
||||||
field.setAccessible(true);
|
|
||||||
ConsistencyService consistencyService = mock(ConsistencyService.class);
|
|
||||||
when(consistencyService.isAvailable()).thenReturn(false);
|
|
||||||
field.set(serverStatusManager, consistencyService);
|
|
||||||
ServerStatusManager.ServerStatusUpdater updater = serverStatusManager.new ServerStatusUpdater();
|
ServerStatusManager.ServerStatusUpdater updater = serverStatusManager.new ServerStatusUpdater();
|
||||||
//then
|
//then
|
||||||
updater.run();
|
updater.run();
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent.impl;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.core.exception.KvStorageException;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.FileKvStorage;
|
|
||||||
import com.alibaba.nacos.sys.utils.DiskUtils;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
|
||||||
import org.mockito.junit.jupiter.MockitoSettings;
|
|
||||||
import org.mockito.quality.Strictness;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
|
||||||
// todo remove this
|
|
||||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
|
||||||
class NamingKvStorageTest {
|
|
||||||
|
|
||||||
private final byte[] key = "fileName_test".getBytes();
|
|
||||||
|
|
||||||
private final String str = "str_test";
|
|
||||||
|
|
||||||
private NamingKvStorage namingKvStorage;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private FileKvStorage baseDirStorageMock;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() throws Exception {
|
|
||||||
namingKvStorage = new NamingKvStorage("baseDir_test");
|
|
||||||
|
|
||||||
Field baseDirStorageField = NamingKvStorage.class.getDeclaredField("baseDirStorage");
|
|
||||||
baseDirStorageField.setAccessible(true);
|
|
||||||
baseDirStorageField.set(namingKvStorage, baseDirStorageMock);
|
|
||||||
|
|
||||||
when(baseDirStorageMock.get(key)).thenReturn(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
void tearDown() throws IOException {
|
|
||||||
DiskUtils.deleteDirectory("baseDir_test");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGet() throws KvStorageException {
|
|
||||||
namingKvStorage.get(key);
|
|
||||||
verify(baseDirStorageMock).get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testPut() throws KvStorageException {
|
|
||||||
byte[] value = "value_test".getBytes();
|
|
||||||
namingKvStorage.put(key, value);
|
|
||||||
verify(baseDirStorageMock).put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testDelete() throws KvStorageException {
|
|
||||||
namingKvStorage.delete(key);
|
|
||||||
verify(baseDirStorageMock).delete(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testDoSnapshot() throws KvStorageException {
|
|
||||||
namingKvStorage.doSnapshot(str);
|
|
||||||
verify(baseDirStorageMock).doSnapshot(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSnapshotLoad() throws KvStorageException {
|
|
||||||
namingKvStorage.snapshotLoad(str);
|
|
||||||
verify(baseDirStorageMock).snapshotLoad(str);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.consistency.persistent.impl;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.consistency.Serializer;
|
|
||||||
import com.alibaba.nacos.consistency.snapshot.Reader;
|
|
||||||
import com.alibaba.nacos.consistency.snapshot.Writer;
|
|
||||||
import com.alibaba.nacos.core.distributed.raft.RaftConfig;
|
|
||||||
import com.alibaba.nacos.core.distributed.raft.utils.RaftExecutor;
|
|
||||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
|
||||||
import com.alibaba.nacos.sys.env.EnvUtil;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
|
||||||
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.doAnswer;
|
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
|
||||||
class NamingSnapshotOperationTest {
|
|
||||||
|
|
||||||
static {
|
|
||||||
RaftExecutor.init(new RaftConfig());
|
|
||||||
EnvUtil.setEnvironment(new MockEnvironment());
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String snapshotDir = Paths.get(EnvUtil.getNacosTmpDir(), "rocks_snapshot_test").toString();
|
|
||||||
|
|
||||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private KvStorage storage;
|
|
||||||
|
|
||||||
private boolean isSnapshoted = false;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void init() throws Exception {
|
|
||||||
doAnswer(invocationOnMock -> {
|
|
||||||
isSnapshoted = true;
|
|
||||||
return null;
|
|
||||||
}).when(storage).doSnapshot(any(String.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
void after() {
|
|
||||||
storage.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNamingSnapshot() throws InterruptedException {
|
|
||||||
AtomicBoolean result = new AtomicBoolean(false);
|
|
||||||
NamingSnapshotOperation operation = new NamingSnapshotOperation(storage, lock, Mockito.mock(Serializer.class));
|
|
||||||
final Writer writer = new Writer(snapshotDir);
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
operation.onSnapshotSave(writer, (isOk, throwable) -> {
|
|
||||||
result.set(isOk && throwable == null);
|
|
||||||
latch.countDown();
|
|
||||||
});
|
|
||||||
latch.await(10, TimeUnit.SECONDS);
|
|
||||||
assertTrue(isSnapshoted);
|
|
||||||
assertTrue(result.get());
|
|
||||||
|
|
||||||
final Reader reader = new Reader(snapshotDir, writer.listFiles());
|
|
||||||
boolean res = operation.onSnapshotLoad(reader);
|
|
||||||
assertTrue(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -29,27 +29,16 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
|
||||||
import org.springframework.boot.web.server.LocalServerPort;
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
import org.springframework.http.HttpEntity;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.alibaba.nacos.test.naming.NamingBase.NAMING_CONTROLLER_PATH;
|
|
||||||
import static com.alibaba.nacos.test.naming.NamingBase.TEST_GROUP_1;
|
|
||||||
import static com.alibaba.nacos.test.naming.NamingBase.TEST_NAMESPACE_1;
|
|
||||||
import static com.alibaba.nacos.test.naming.NamingBase.TEST_NAMESPACE_2;
|
|
||||||
import static com.alibaba.nacos.test.naming.NamingBase.TEST_PORT2_4_DOM_1;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@ -58,7 +47,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
*/
|
*/
|
||||||
@SpringBootTest(classes = Nacos.class, properties = {
|
@SpringBootTest(classes = Nacos.class, properties = {
|
||||||
"server.servlet.context-path=/nacos"}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
"server.servlet.context-path=/nacos"}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||||
class CPInstancesAPI_ITCase {
|
class CPInstancesAPI_ITCase extends NamingBase {
|
||||||
|
|
||||||
private NamingService naming;
|
private NamingService naming;
|
||||||
|
|
||||||
@ -69,11 +58,6 @@ class CPInstancesAPI_ITCase {
|
|||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private URL base;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TestRestTemplate restTemplate;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() throws Exception {
|
void setUp() throws Exception {
|
||||||
String url = String.format("http://localhost:%d/", port);
|
String url = String.format("http://localhost:%d/", port);
|
||||||
@ -90,6 +74,7 @@ class CPInstancesAPI_ITCase {
|
|||||||
properties.put(PropertyKeyConst.NAMESPACE, TEST_NAMESPACE_2);
|
properties.put(PropertyKeyConst.NAMESPACE, TEST_NAMESPACE_2);
|
||||||
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1" + ":" + port);
|
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1" + ":" + port);
|
||||||
naming2 = NamingFactory.createNamingService(properties);
|
naming2 = NamingFactory.createNamingService(properties);
|
||||||
|
isNamingServerReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -182,13 +167,14 @@ class CPInstancesAPI_ITCase {
|
|||||||
String serviceName = NamingBase.randomDomainName();
|
String serviceName = NamingBase.randomDomainName();
|
||||||
|
|
||||||
ResponseEntity<String> registerResponse = request(NamingBase.NAMING_CONTROLLER_PATH + "/instance",
|
ResponseEntity<String> registerResponse = request(NamingBase.NAMING_CONTROLLER_PATH + "/instance",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("ip", "11.11.11.11").appendParam("port", "80")
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("ip", "11.11.11.11")
|
||||||
.appendParam("namespaceId", TEST_NAMESPACE_1).done(), String.class, HttpMethod.POST);
|
.appendParam("port", "80").appendParam("namespaceId", TEST_NAMESPACE_1).done(), String.class,
|
||||||
|
HttpMethod.POST);
|
||||||
assertTrue(registerResponse.getStatusCode().is2xxSuccessful());
|
assertTrue(registerResponse.getStatusCode().is2xxSuccessful());
|
||||||
|
|
||||||
ResponseEntity<String> deleteServiceResponse = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
ResponseEntity<String> deleteServiceResponse = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("namespaceId", TEST_NAMESPACE_1).done(),
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("namespaceId", TEST_NAMESPACE_1)
|
||||||
String.class, HttpMethod.DELETE);
|
.done(), String.class, HttpMethod.DELETE);
|
||||||
assertTrue(deleteServiceResponse.getStatusCode().is4xxClientError());
|
assertTrue(deleteServiceResponse.getStatusCode().is4xxClientError());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,8 +196,8 @@ class CPInstancesAPI_ITCase {
|
|||||||
|
|
||||||
//get service
|
//get service
|
||||||
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("namespaceId", TEST_NAMESPACE_1).done(),
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("namespaceId", TEST_NAMESPACE_1)
|
||||||
String.class);
|
.done(), String.class);
|
||||||
|
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
|
|
||||||
@ -254,8 +240,8 @@ class CPInstancesAPI_ITCase {
|
|||||||
|
|
||||||
//get service
|
//get service
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service/list",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service/list",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("pageNo", "1").appendParam("pageSize", "150").done(),
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("pageNo", "1")
|
||||||
String.class);
|
.appendParam("pageSize", "150").done(), String.class);
|
||||||
|
|
||||||
System.out.println("json = " + response.getBody());
|
System.out.println("json = " + response.getBody());
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
@ -337,7 +323,8 @@ class CPInstancesAPI_ITCase {
|
|||||||
assertEquals(1, json.get("hosts").size());
|
assertEquals(1, json.get("hosts").size());
|
||||||
|
|
||||||
instanceDeregister(serviceName, Constants.DEFAULT_NAMESPACE_ID, "33.33.33.33", TEST_PORT2_4_DOM_1);
|
instanceDeregister(serviceName, Constants.DEFAULT_NAMESPACE_ID, "33.33.33.33", TEST_PORT2_4_DOM_1);
|
||||||
instanceDeregister(serviceName, Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_1, "22.22.22.22", TEST_PORT2_4_DOM_1);
|
instanceDeregister(serviceName, Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_1, "22.22.22.22",
|
||||||
|
TEST_PORT2_4_DOM_1);
|
||||||
|
|
||||||
namingServiceDelete(serviceName, Constants.DEFAULT_NAMESPACE_ID);
|
namingServiceDelete(serviceName, Constants.DEFAULT_NAMESPACE_ID);
|
||||||
namingServiceDelete(serviceName, Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_1);
|
namingServiceDelete(serviceName, Constants.DEFAULT_NAMESPACE_ID, TEST_GROUP_1);
|
||||||
@ -349,17 +336,19 @@ class CPInstancesAPI_ITCase {
|
|||||||
|
|
||||||
private void instanceDeregister(String serviceName, String namespace, String groupName, String ip, String port) {
|
private void instanceDeregister(String serviceName, String namespace, String groupName, String ip, String port) {
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/instance",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/instance",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("ip", ip).appendParam("port", port)
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("ip", ip)
|
||||||
.appendParam("namespaceId", namespace).appendParam("groupName", groupName).appendParam("ephemeral", "false").done(),
|
.appendParam("port", port).appendParam("namespaceId", namespace)
|
||||||
String.class, HttpMethod.DELETE);
|
.appendParam("groupName", groupName).appendParam("ephemeral", "false").done(), String.class,
|
||||||
|
HttpMethod.DELETE);
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void instanceRegister(String serviceName, String namespace, String groupName, String ip, String port) {
|
private void instanceRegister(String serviceName, String namespace, String groupName, String ip, String port) {
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/instance",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/instance",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("ip", ip).appendParam("port", port)
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("ip", ip)
|
||||||
.appendParam("namespaceId", namespace).appendParam("groupName", groupName).appendParam("ephemeral", "false").done(),
|
.appendParam("port", port).appendParam("namespaceId", namespace)
|
||||||
String.class, HttpMethod.POST);
|
.appendParam("groupName", groupName).appendParam("ephemeral", "false").done(), String.class,
|
||||||
|
HttpMethod.POST);
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +363,8 @@ class CPInstancesAPI_ITCase {
|
|||||||
private void namingServiceCreate(String serviceName, String namespace, String groupName) {
|
private void namingServiceCreate(String serviceName, String namespace, String groupName) {
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3")
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3")
|
||||||
.appendParam("namespaceId", namespace).appendParam("groupName", groupName).done(), String.class, HttpMethod.POST);
|
.appendParam("namespaceId", namespace).appendParam("groupName", groupName).done(), String.class,
|
||||||
|
HttpMethod.POST);
|
||||||
System.out.println(response);
|
System.out.println(response);
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
assertEquals("ok", response.getBody());
|
assertEquals("ok", response.getBody());
|
||||||
@ -393,26 +383,4 @@ class CPInstancesAPI_ITCase {
|
|||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
assertEquals("ok", response.getBody());
|
assertEquals("ok", response.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> ResponseEntity<T> request(String path, MultiValueMap<String, String> params, Class<T> clazz) {
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
|
|
||||||
HttpEntity<?> entity = new HttpEntity<T>(headers);
|
|
||||||
|
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(this.base.toString() + path).queryParams(params);
|
|
||||||
|
|
||||||
return this.restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity, clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> ResponseEntity<T> request(String path, MultiValueMap<String, String> params, Class<T> clazz, HttpMethod httpMethod) {
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
|
|
||||||
HttpEntity<?> entity = new HttpEntity<T>(headers);
|
|
||||||
|
|
||||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(this.base.toString() + path).queryParams(params);
|
|
||||||
|
|
||||||
return this.restTemplate.exchange(builder.toUriString(), httpMethod, entity, clazz);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,14 @@ import com.alibaba.nacos.common.http.client.NacosRestTemplate;
|
|||||||
import com.alibaba.nacos.common.http.param.Header;
|
import com.alibaba.nacos.common.http.param.Header;
|
||||||
import com.alibaba.nacos.common.utils.StringUtils;
|
import com.alibaba.nacos.common.utils.StringUtils;
|
||||||
import com.alibaba.nacos.test.base.HttpClient4Test;
|
import com.alibaba.nacos.test.base.HttpClient4Test;
|
||||||
|
import com.alibaba.nacos.test.base.Params;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
@ -232,4 +235,17 @@ public class NamingBase extends HttpClient4Test {
|
|||||||
}
|
}
|
||||||
return contextPath.startsWith("/") ? contextPath : "/" + contextPath;
|
return contextPath.startsWith("/") ? contextPath : "/" + contextPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void isNamingServerReady() throws InterruptedException {
|
||||||
|
int retry = 0;
|
||||||
|
while (retry < 3) {
|
||||||
|
ResponseEntity<String> response = request("/nacos/v1/ns/operator/metrics", Params.newParams().done(),
|
||||||
|
String.class);
|
||||||
|
if (response.getStatusCode().is2xxSuccessful() && response.getBody().contains("UP")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retry++;
|
||||||
|
TimeUnit.SECONDS.sleep(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ class RestAPI_ITCase extends NamingBase {
|
|||||||
void setUp() throws Exception {
|
void setUp() throws Exception {
|
||||||
String url = String.format("http://localhost:%d/", port);
|
String url = String.format("http://localhost:%d/", port);
|
||||||
this.base = new URL(url);
|
this.base = new URL(url);
|
||||||
|
isNamingServerReady();
|
||||||
//prepareData();
|
//prepareData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,8 +86,8 @@ class RestAPI_ITCase extends NamingBase {
|
|||||||
void createService() throws Exception {
|
void createService() throws Exception {
|
||||||
String serviceName = NamingBase.randomDomainName();
|
String serviceName = NamingBase.randomDomainName();
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3").done(), String.class,
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3")
|
||||||
HttpMethod.POST);
|
.done(), String.class, HttpMethod.POST);
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
assertEquals("ok", response.getBody());
|
assertEquals("ok", response.getBody());
|
||||||
|
|
||||||
@ -102,14 +103,15 @@ class RestAPI_ITCase extends NamingBase {
|
|||||||
void getService() throws Exception {
|
void getService() throws Exception {
|
||||||
String serviceName = NamingBase.randomDomainName();
|
String serviceName = NamingBase.randomDomainName();
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3").done(), String.class,
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3")
|
||||||
HttpMethod.POST);
|
.done(), String.class, HttpMethod.POST);
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
assertEquals("ok", response.getBody());
|
assertEquals("ok", response.getBody());
|
||||||
|
|
||||||
//get service
|
//get service
|
||||||
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3").done(), String.class);
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3")
|
||||||
|
.done(), String.class);
|
||||||
|
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
|
|
||||||
@ -129,8 +131,8 @@ class RestAPI_ITCase extends NamingBase {
|
|||||||
String serviceName = NamingBase.randomDomainName();
|
String serviceName = NamingBase.randomDomainName();
|
||||||
//get service
|
//get service
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service/list",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service/list",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("pageNo", "1").appendParam("pageSize", "150").done(),
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("pageNo", "1")
|
||||||
String.class);
|
.appendParam("pageSize", "150").done(), String.class);
|
||||||
|
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
JsonNode json = JacksonUtils.toObj(response.getBody());
|
JsonNode json = JacksonUtils.toObj(response.getBody());
|
||||||
@ -138,14 +140,14 @@ class RestAPI_ITCase extends NamingBase {
|
|||||||
assertTrue(count >= 0);
|
assertTrue(count >= 0);
|
||||||
|
|
||||||
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3").done(), String.class,
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3")
|
||||||
HttpMethod.POST);
|
.done(), String.class, HttpMethod.POST);
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
assertEquals("ok", response.getBody());
|
assertEquals("ok", response.getBody());
|
||||||
|
|
||||||
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service/list",
|
response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service/list",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("pageNo", "1").appendParam("pageSize", "150").done(),
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("pageNo", "1")
|
||||||
String.class);
|
.appendParam("pageSize", "150").done(), String.class);
|
||||||
|
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
json = JacksonUtils.toObj(response.getBody());
|
json = JacksonUtils.toObj(response.getBody());
|
||||||
@ -163,8 +165,8 @@ class RestAPI_ITCase extends NamingBase {
|
|||||||
void updateService() throws Exception {
|
void updateService() throws Exception {
|
||||||
String serviceName = NamingBase.randomDomainName();
|
String serviceName = NamingBase.randomDomainName();
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.6").done(), String.class,
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.6")
|
||||||
HttpMethod.POST);
|
.done(), String.class, HttpMethod.POST);
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
assertEquals("ok", response.getBody());
|
assertEquals("ok", response.getBody());
|
||||||
|
|
||||||
@ -208,8 +210,8 @@ class RestAPI_ITCase extends NamingBase {
|
|||||||
private void namingServiceDelete(String serviceName) {
|
private void namingServiceDelete(String serviceName) {
|
||||||
//delete service
|
//delete service
|
||||||
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
ResponseEntity<String> response = request(NamingBase.NAMING_CONTROLLER_PATH + "/service",
|
||||||
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3").done(), String.class,
|
Params.newParams().appendParam("serviceName", serviceName).appendParam("protectThreshold", "0.3")
|
||||||
HttpMethod.DELETE);
|
.done(), String.class, HttpMethod.DELETE);
|
||||||
|
|
||||||
assertTrue(response.getStatusCode().is2xxSuccessful());
|
assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||||
assertEquals("ok", response.getBody());
|
assertEquals("ok", response.getBody());
|
||||||
|
Loading…
Reference in New Issue
Block a user