Merge branch 'upsteam-develop' into feature_support_grpc_core
# Conflicts: # api/src/main/java/com/alibaba/nacos/api/common/Constants.java # client/src/main/java/com/alibaba/nacos/client/naming/remote/http/HttpClient.java # common/src/main/java/com/alibaba/nacos/common/utils/VersionUtils.java # core/src/main/java/com/alibaba/nacos/core/distributed/distro/DistroProtocol.java # naming/src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.java # naming/src/main/java/com/alibaba/nacos/naming/controllers/ServiceController.java # naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalExecutor.java
This commit is contained in:
commit
9cbc0bfeea
23
.github/workflows/maven.yml
vendored
Normal file
23
.github/workflows/maven.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: Java CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04]
|
||||
java: [8, 8.0.192, 11, 11.0.9]
|
||||
fail-fast: false
|
||||
max-parallel: 2
|
||||
name: Test JDK ${{ matrix.java }}, ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- name: Test with Maven
|
||||
run: mvn test -B --file pom.xml
|
@ -28,9 +28,13 @@ before_install:
|
||||
|
||||
script:
|
||||
- mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true
|
||||
- mvn clean -Premove-test-data
|
||||
- mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
|
||||
- mvn clean -Premove-test-data
|
||||
- mvn clean install -Pcit-test
|
||||
- mvn clean -Premove-test-data
|
||||
- mvn clean package -Pit-test
|
||||
- mvn clean -Premove-test-data
|
||||
after_success:
|
||||
- mvn clean package -Pit-test
|
||||
- mvn sonar:sonar -Psonar-apache
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -188,6 +188,8 @@ public class Constants {
|
||||
|
||||
public static final String HTTP_PREFIX = "http";
|
||||
|
||||
public static final String ALL_PATTERN = "*";
|
||||
|
||||
public static final String COLON = ":";
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package com.alibaba.nacos.api.common;
|
||||
* <li> Global and common code starts with 10001.
|
||||
* <li> Naming module code starts with 20001.
|
||||
* <li> Config module code starts with 30001.
|
||||
* <li> Core module code starts with 40001.
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.2.0
|
||||
|
@ -19,6 +19,7 @@ package com.alibaba.nacos.api.naming.pojo;
|
||||
import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker;
|
||||
import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -31,7 +32,9 @@ import java.util.Map;
|
||||
* @author nkorange
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:abbreviationaswordinname")
|
||||
public class Cluster {
|
||||
public class Cluster implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7196138840047197271L;
|
||||
|
||||
/**
|
||||
* Name of belonging service.
|
||||
|
@ -22,6 +22,7 @@ import com.alibaba.nacos.api.utils.StringUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -33,7 +34,9 @@ import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN;
|
||||
* @author nkorange
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public class Instance {
|
||||
public class Instance implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -742906310567291979L;
|
||||
|
||||
/**
|
||||
* unique id of this instance.
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.alibaba.nacos.api.naming.pojo;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -29,7 +30,9 @@ import java.util.Map;
|
||||
*
|
||||
* @author nkorange
|
||||
*/
|
||||
public class Service {
|
||||
public class Service implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3470985546826874460L;
|
||||
|
||||
/**
|
||||
* service name.
|
||||
|
@ -25,6 +25,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Abstract health checker.
|
||||
*
|
||||
@ -34,7 +36,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
@JsonSubTypes({@JsonSubTypes.Type(name = Http.TYPE, value = Http.class),
|
||||
@JsonSubTypes.Type(name = Mysql.TYPE, value = Mysql.class),
|
||||
@JsonSubTypes.Type(name = Tcp.TYPE, value = Tcp.class)})
|
||||
public abstract class AbstractHealthChecker implements Cloneable {
|
||||
public abstract class AbstractHealthChecker implements Cloneable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 3848305577423336421L;
|
||||
|
||||
@JsonIgnore
|
||||
protected final String type;
|
||||
|
@ -54,4 +54,25 @@ public class NamingUtils {
|
||||
}
|
||||
return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* check combineServiceName format. the serviceName can't be blank. some relational logic in {@link
|
||||
* com.alibaba.nacos.naming.web.DistroFilter#doFilter}, it will handle combineServiceName in some case, you should
|
||||
* know it.
|
||||
* <pre>
|
||||
* serviceName = "@@"; the length = 0; illegal
|
||||
* serviceName = "group@@"; the length = 1; illegal
|
||||
* serviceName = "@@serviceName"; the length = 2; legal
|
||||
* serviceName = "group@@serviceName"; the length = 2; legal
|
||||
* </pre>
|
||||
*
|
||||
* @param combineServiceName such as: groupName@@serviceName
|
||||
*/
|
||||
public static void checkServiceNameFormat(String combineServiceName) {
|
||||
String[] split = combineServiceName.split(Constants.SERVICE_INFO_SPLITER);
|
||||
if (split.length <= 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Param 'serviceName' is illegal, it should be format as 'groupName@@serviceName'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Abstract selector that only contains a type.
|
||||
*
|
||||
@ -27,7 +29,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
* @since 0.7.0
|
||||
*/
|
||||
@JsonTypeInfo(use = Id.NAME, property = "type", defaultImpl = NoneSelector.class)
|
||||
public abstract class AbstractSelector {
|
||||
public abstract class AbstractSelector implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4530233098102379229L;
|
||||
|
||||
/**
|
||||
* The type of this selector, each child class should announce its own unique type.
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -84,4 +84,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -1,305 +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.client.config.impl;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.client.utils.ParamUtil;
|
||||
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
|
||||
import com.alibaba.nacos.common.http.client.NacosRestTemplate;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import com.alibaba.nacos.common.utils.MD5Utils;
|
||||
import com.alibaba.nacos.common.utils.UuidUtils;
|
||||
import com.alibaba.nacos.common.utils.VersionUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Http tool.
|
||||
*
|
||||
* @author Nacos
|
||||
* @deprecated Use NacosRestTemplate{@link NacosRestTemplate} unified http client
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpSimpleClient {
|
||||
|
||||
/**
|
||||
* Get method.
|
||||
*
|
||||
* @param url url
|
||||
* @param headers headers
|
||||
* @param paramValues paramValues
|
||||
* @param encoding encoding
|
||||
* @param readTimeoutMs readTimeoutMs
|
||||
* @param isSsl isSsl
|
||||
* @return result
|
||||
* @throws IOException io exception
|
||||
*/
|
||||
public static HttpResult httpGet(String url, List<String> headers, List<String> paramValues, String encoding,
|
||||
long readTimeoutMs, boolean isSsl) throws IOException {
|
||||
String encodedContent = encodingParams(paramValues, encoding);
|
||||
url += (null == encodedContent) ? "" : ("?" + encodedContent);
|
||||
if (Limiter
|
||||
.isLimit(MD5Utils.md5Hex(new StringBuilder(url).append(encodedContent).toString(), Constants.ENCODE))) {
|
||||
return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD,
|
||||
"More than client-side current limit threshold");
|
||||
}
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
try {
|
||||
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100);
|
||||
conn.setReadTimeout((int) readTimeoutMs);
|
||||
List<String> newHeaders = getHeaders(url, headers, paramValues);
|
||||
setHeaders(conn, newHeaders, encoding);
|
||||
|
||||
conn.connect();
|
||||
|
||||
int respCode = conn.getResponseCode();
|
||||
String resp = null;
|
||||
|
||||
if (HttpURLConnection.HTTP_OK == respCode) {
|
||||
resp = IoUtils.toString(conn.getInputStream(), encoding);
|
||||
} else {
|
||||
resp = IoUtils.toString(conn.getErrorStream(), encoding);
|
||||
}
|
||||
return new HttpResult(respCode, conn.getHeaderFields(), resp);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送GET请求.
|
||||
*/
|
||||
public static HttpResult httpGet(String url, List<String> headers, List<String> paramValues, String encoding,
|
||||
long readTimeoutMs) throws IOException {
|
||||
return httpGet(url, headers, paramValues, encoding, readTimeoutMs, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送POST请求.
|
||||
*
|
||||
* @param url url
|
||||
* @param headers 请求Header,可以为null
|
||||
* @param paramValues 参数,可以为null
|
||||
* @param encoding URL编码使用的字符集
|
||||
* @param readTimeoutMs 响应超时
|
||||
* @param isSsl 是否https
|
||||
* @return result
|
||||
* @throws IOException io exception
|
||||
*/
|
||||
public static HttpResult httpPost(String url, List<String> headers, List<String> paramValues, String encoding,
|
||||
long readTimeoutMs, boolean isSsl) throws IOException {
|
||||
String encodedContent = encodingParams(paramValues, encoding);
|
||||
encodedContent = (null == encodedContent) ? "" : encodedContent;
|
||||
if (Limiter
|
||||
.isLimit(MD5Utils.md5Hex(new StringBuilder(url).append(encodedContent).toString(), Constants.ENCODE))) {
|
||||
return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD,
|
||||
"More than client-side current limit threshold");
|
||||
}
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 3000 ? ParamUtil.getConnectTimeout() : 3000);
|
||||
conn.setReadTimeout((int) readTimeoutMs);
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
List<String> newHeaders = getHeaders(url, headers, paramValues);
|
||||
setHeaders(conn, newHeaders, encoding);
|
||||
|
||||
conn.getOutputStream().write(encodedContent.getBytes(encoding));
|
||||
|
||||
int respCode = conn.getResponseCode();
|
||||
String resp = null;
|
||||
|
||||
if (HttpURLConnection.HTTP_OK == respCode) {
|
||||
resp = IoUtils.toString(conn.getInputStream(), encoding);
|
||||
} else {
|
||||
resp = IoUtils.toString(conn.getErrorStream(), encoding);
|
||||
}
|
||||
return new HttpResult(respCode, conn.getHeaderFields(), resp);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送POST请求.
|
||||
*
|
||||
* @param url url
|
||||
* @param headers 请求Header,可以为null
|
||||
* @param paramValues 参数,可以为null
|
||||
* @param encoding URL编码使用的字符集
|
||||
* @param readTimeoutMs 响应超时
|
||||
* @return result
|
||||
* @throws IOException io exception
|
||||
*/
|
||||
public static HttpResult httpPost(String url, List<String> headers, List<String> paramValues, String encoding,
|
||||
long readTimeoutMs) throws IOException {
|
||||
return httpPost(url, headers, paramValues, encoding, readTimeoutMs, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete method.
|
||||
*
|
||||
* @param url url
|
||||
* @param headers 请求Header,可以为null
|
||||
* @param paramValues 参数,可以为null
|
||||
* @param encoding URL编码使用的字符集
|
||||
* @param readTimeoutMs 响应超时
|
||||
* @param isSsl 是否https
|
||||
* @return result
|
||||
* @throws IOException io exception
|
||||
*/
|
||||
public static HttpResult httpDelete(String url, List<String> headers, List<String> paramValues, String encoding,
|
||||
long readTimeoutMs, boolean isSsl) throws IOException {
|
||||
String encodedContent = encodingParams(paramValues, encoding);
|
||||
url += (null == encodedContent) ? "" : ("?" + encodedContent);
|
||||
if (Limiter
|
||||
.isLimit(MD5Utils.md5Hex(new StringBuilder(url).append(encodedContent).toString(), Constants.ENCODE))) {
|
||||
return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD,
|
||||
"More than client-side current limit threshold");
|
||||
}
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
try {
|
||||
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setRequestMethod("DELETE");
|
||||
conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100);
|
||||
conn.setReadTimeout((int) readTimeoutMs);
|
||||
List<String> newHeaders = getHeaders(url, headers, paramValues);
|
||||
setHeaders(conn, newHeaders, encoding);
|
||||
|
||||
conn.connect();
|
||||
|
||||
int respCode = conn.getResponseCode();
|
||||
String resp = null;
|
||||
|
||||
if (HttpURLConnection.HTTP_OK == respCode) {
|
||||
resp = IoUtils.toString(conn.getInputStream(), encoding);
|
||||
} else {
|
||||
resp = IoUtils.toString(conn.getErrorStream(), encoding);
|
||||
}
|
||||
return new HttpResult(respCode, conn.getHeaderFields(), resp);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete method.
|
||||
*
|
||||
* @param url url
|
||||
* @param headers 请求Header,可以为null
|
||||
* @param paramValues 参数,可以为null
|
||||
* @param encoding URL编码使用的字符集
|
||||
* @param readTimeoutMs 响应超时
|
||||
* @return result
|
||||
* @throws IOException io exception
|
||||
*/
|
||||
public static HttpResult httpDelete(String url, List<String> headers, List<String> paramValues, String encoding,
|
||||
long readTimeoutMs) throws IOException {
|
||||
return httpGet(url, headers, paramValues, encoding, readTimeoutMs, false);
|
||||
}
|
||||
|
||||
private static void setHeaders(HttpURLConnection conn, List<String> headers, String encoding) {
|
||||
if (null != headers) {
|
||||
for (Iterator<String> iter = headers.iterator(); iter.hasNext(); ) {
|
||||
conn.addRequestProperty(iter.next(), iter.next());
|
||||
}
|
||||
}
|
||||
conn.addRequestProperty(HttpHeaderConsts.CLIENT_VERSION_HEADER, VersionUtils.version);
|
||||
conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + encoding);
|
||||
|
||||
String ts = String.valueOf(System.currentTimeMillis());
|
||||
String token = MD5Utils.md5Hex(ts + ParamUtil.getAppKey(), Constants.ENCODE);
|
||||
|
||||
conn.addRequestProperty(Constants.CLIENT_APPNAME_HEADER, ParamUtil.getAppName());
|
||||
conn.addRequestProperty(Constants.CLIENT_REQUEST_TS_HEADER, ts);
|
||||
conn.addRequestProperty(Constants.CLIENT_REQUEST_TOKEN_HEADER, token);
|
||||
}
|
||||
|
||||
private static List<String> getHeaders(String url, List<String> headers, List<String> paramValues)
|
||||
throws IOException {
|
||||
List<String> newHeaders = new ArrayList<String>();
|
||||
newHeaders.add("exConfigInfo");
|
||||
newHeaders.add("true");
|
||||
newHeaders.add("RequestId");
|
||||
newHeaders.add(UuidUtils.generateUuid());
|
||||
if (headers != null) {
|
||||
newHeaders.addAll(headers);
|
||||
}
|
||||
return newHeaders;
|
||||
}
|
||||
|
||||
private static String encodingParams(List<String> paramValues, String encoding)
|
||||
throws UnsupportedEncodingException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (null == paramValues) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Iterator<String> iter = paramValues.iterator(); iter.hasNext(); ) {
|
||||
sb.append(iter.next()).append("=");
|
||||
sb.append(URLEncoder.encode(iter.next(), encoding));
|
||||
if (iter.hasNext()) {
|
||||
sb.append("&");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class HttpResult {
|
||||
|
||||
public final int code;
|
||||
|
||||
public final Map<String, List<String>> headers;
|
||||
|
||||
public final String content;
|
||||
|
||||
public HttpResult(int code, String content) {
|
||||
this.code = code;
|
||||
this.headers = null;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public HttpResult(int code, Map<String, List<String>> headers, String content) {
|
||||
this.code = code;
|
||||
this.headers = headers;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HttpResult{" + "code=" + code + ", headers=" + headers + ", content='" + content + '\'' + '}';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,248 +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.client.naming.remote.http;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.common.http.client.NacosRestTemplate;
|
||||
import com.alibaba.nacos.common.tls.TlsSystemConfig;
|
||||
import com.alibaba.nacos.common.utils.HttpMethod;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;
|
||||
|
||||
/**
|
||||
* Http client.
|
||||
*
|
||||
* @author nkorange
|
||||
* @deprecated Use NacosRestTemplate{@link NacosRestTemplate} unified http client
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpClient {
|
||||
|
||||
public static final int READ_TIME_OUT_MILLIS = Integer
|
||||
.getInteger("com.alibaba.nacos.client.naming.rtimeout", 50000);
|
||||
|
||||
public static final int CON_TIME_OUT_MILLIS = Integer.getInteger("com.alibaba.nacos.client.naming.ctimeout", 3000);
|
||||
|
||||
private static final boolean ENABLE_HTTPS = Boolean.getBoolean(TlsSystemConfig.TLS_ENABLE);
|
||||
|
||||
static {
|
||||
// limit max redirection
|
||||
System.setProperty("http.maxRedirects", "5");
|
||||
}
|
||||
|
||||
public static String getPrefix() {
|
||||
if (ENABLE_HTTPS) {
|
||||
return "https://";
|
||||
}
|
||||
|
||||
return "http://";
|
||||
|
||||
}
|
||||
|
||||
public static HttpResult httpGet(String url, List<String> headers, Map<String, String> paramValues,
|
||||
String encoding) {
|
||||
return request(url, headers, paramValues, StringUtils.EMPTY, encoding, HttpMethod.GET);
|
||||
}
|
||||
|
||||
/**
|
||||
* request.
|
||||
*
|
||||
* @param url url
|
||||
* @param headers headers
|
||||
* @param paramValues paramValues
|
||||
* @param body body
|
||||
* @param encoding encoding
|
||||
* @param method method
|
||||
* @return result
|
||||
*/
|
||||
public static HttpResult request(String url, List<String> headers, Map<String, String> paramValues, String body,
|
||||
String encoding, String method) {
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
String encodedContent = encodingParams(paramValues, encoding);
|
||||
url += (StringUtils.isEmpty(encodedContent)) ? "" : ("?" + encodedContent);
|
||||
|
||||
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
|
||||
setHeaders(conn, headers, encoding);
|
||||
conn.setConnectTimeout(CON_TIME_OUT_MILLIS);
|
||||
conn.setReadTimeout(READ_TIME_OUT_MILLIS);
|
||||
conn.setRequestMethod(method);
|
||||
conn.setDoOutput(true);
|
||||
if (StringUtils.isNotBlank(body)) {
|
||||
byte[] b = body.getBytes();
|
||||
conn.setRequestProperty("Content-Length", String.valueOf(b.length));
|
||||
conn.getOutputStream().write(b, 0, b.length);
|
||||
conn.getOutputStream().flush();
|
||||
conn.getOutputStream().close();
|
||||
}
|
||||
conn.connect();
|
||||
if (NAMING_LOGGER.isDebugEnabled()) {
|
||||
NAMING_LOGGER.debug("Request from server: " + url);
|
||||
}
|
||||
return getResult(conn);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
if (conn != null) {
|
||||
NAMING_LOGGER.warn("failed to request " + conn.getURL() + " from " + InetAddress
|
||||
.getByName(conn.getURL().getHost()).getHostAddress());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
NAMING_LOGGER.error("[NA] failed to request ", ex);
|
||||
//ignore
|
||||
}
|
||||
|
||||
NAMING_LOGGER.error("[NA] failed to request ", e);
|
||||
|
||||
return new HttpResult(500, e.toString(), Collections.<String, String>emptyMap());
|
||||
} finally {
|
||||
IoUtils.closeQuietly(conn);
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpResult getResult(HttpURLConnection conn) throws IOException {
|
||||
int respCode = conn.getResponseCode();
|
||||
|
||||
InputStream inputStream;
|
||||
if (HttpURLConnection.HTTP_OK == respCode || HttpURLConnection.HTTP_NOT_MODIFIED == respCode
|
||||
|| Constants.WRITE_REDIRECT_CODE == respCode) {
|
||||
inputStream = conn.getInputStream();
|
||||
} else {
|
||||
inputStream = conn.getErrorStream();
|
||||
}
|
||||
|
||||
Map<String, String> respHeaders = new HashMap<String, String>(conn.getHeaderFields().size());
|
||||
for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
|
||||
respHeaders.put(entry.getKey(), entry.getValue().get(0));
|
||||
}
|
||||
|
||||
String encodingGzip = "gzip";
|
||||
|
||||
if (encodingGzip.equals(respHeaders.get(HttpHeaders.CONTENT_ENCODING))) {
|
||||
inputStream = new GZIPInputStream(inputStream);
|
||||
}
|
||||
HttpResult httpResult = new HttpResult(respCode, IoUtils.toString(inputStream, getCharset(conn)), respHeaders);
|
||||
|
||||
//InputStream from HttpURLConnection can be closed automatically,but new GZIPInputStream can't be closed automatically
|
||||
//so needs to close it manually
|
||||
if (inputStream instanceof GZIPInputStream) {
|
||||
inputStream.close();
|
||||
}
|
||||
return httpResult;
|
||||
}
|
||||
|
||||
private static String getCharset(HttpURLConnection conn) {
|
||||
String contentType = conn.getContentType();
|
||||
if (StringUtils.isEmpty(contentType)) {
|
||||
return "UTF-8";
|
||||
}
|
||||
|
||||
String[] values = contentType.split(";");
|
||||
if (values.length == 0) {
|
||||
return "UTF-8";
|
||||
}
|
||||
|
||||
String charset = "UTF-8";
|
||||
for (String value : values) {
|
||||
value = value.trim();
|
||||
|
||||
if (value.toLowerCase().startsWith("charset=")) {
|
||||
charset = value.substring("charset=".length());
|
||||
}
|
||||
}
|
||||
|
||||
return charset;
|
||||
}
|
||||
|
||||
private static void setHeaders(HttpURLConnection conn, List<String> headers, String encoding) {
|
||||
if (null != headers) {
|
||||
for (Iterator<String> iter = headers.iterator(); iter.hasNext(); ) {
|
||||
conn.addRequestProperty(iter.next(), iter.next());
|
||||
}
|
||||
}
|
||||
|
||||
conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + encoding);
|
||||
conn.addRequestProperty("Accept-Charset", encoding);
|
||||
}
|
||||
|
||||
private static String encodingParams(Map<String, String> params, String encoding)
|
||||
throws UnsupportedEncodingException {
|
||||
if (null == params || params.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
params.put("encoding", encoding);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
if (StringUtils.isEmpty(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.append(entry.getKey()).append("=");
|
||||
sb.append(URLEncoder.encode(entry.getValue(), encoding));
|
||||
sb.append("&");
|
||||
}
|
||||
|
||||
if (sb.length() > 0) {
|
||||
sb = sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class HttpResult {
|
||||
|
||||
public final int code;
|
||||
|
||||
public final String content;
|
||||
|
||||
private final Map<String, String> respHeaders;
|
||||
|
||||
public HttpResult(int code, String content, Map<String, String> respHeaders) {
|
||||
this.code = code;
|
||||
this.content = content;
|
||||
this.respHeaders = respHeaders;
|
||||
}
|
||||
|
||||
public String getHeader(String name) {
|
||||
return respHeaders.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HttpResult{" + "code=" + code + ", content='" + content + '\'' + ", respHeaders=" + respHeaders
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -1,124 +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.common.http;
|
||||
|
||||
import com.alibaba.nacos.common.http.handler.ResponseHandler;
|
||||
import com.alibaba.nacos.common.http.param.Header;
|
||||
import com.alibaba.nacos.common.http.param.Query;
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.utils.HttpClientUtils;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Base http client.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
* @deprecated Refer to the new {@link com.alibaba.nacos.common.http.client.request.HttpClientRequest}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class BaseHttpClient {
|
||||
|
||||
protected <T> RestResult<T> execute(CloseableHttpClient httpClient, final Type type, HttpUriRequest request)
|
||||
throws Exception {
|
||||
CloseableHttpResponse response = httpClient.execute(request);
|
||||
try {
|
||||
final String body = EntityUtils.toString(response.getEntity());
|
||||
RestResult<T> data = ResponseHandler.convert(body, type);
|
||||
return data;
|
||||
} finally {
|
||||
HttpClientUtils.closeQuietly(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> void execute(CloseableHttpAsyncClient httpAsyncClient, final Type type, final Callback<T> callback,
|
||||
final HttpUriRequest request) {
|
||||
if (!httpAsyncClient.isRunning()) {
|
||||
throw new IllegalArgumentException("httpAsyncClient already shutdown");
|
||||
}
|
||||
httpAsyncClient.execute(request, new FutureCallback<HttpResponse>() {
|
||||
|
||||
@Override
|
||||
public void completed(HttpResponse response) {
|
||||
try {
|
||||
final String body = EntityUtils.toString(response.getEntity());
|
||||
RestResult<T> data = ResponseHandler.convert(body, type);
|
||||
data.setCode(response.getStatusLine().getStatusCode());
|
||||
callback.onReceive(data);
|
||||
} catch (Throwable e) {
|
||||
callback.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Exception ex) {
|
||||
callback.onError(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
callback.onCancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected String buildUrl(String baseUrl, Query query) {
|
||||
if (query.isEmpty()) {
|
||||
return baseUrl;
|
||||
}
|
||||
return baseUrl + "?" + query.toQueryUrl();
|
||||
}
|
||||
|
||||
protected HttpRequestBase build(String url, Header header, String method) throws Exception {
|
||||
return build(url, header, null, method);
|
||||
}
|
||||
|
||||
protected HttpRequestBase build(String url, Header header, Object body, String method) throws Exception {
|
||||
|
||||
final BaseHttpMethod httpMethod = BaseHttpMethod.sourceOf(method);
|
||||
final HttpRequestBase httpRequestBase = httpMethod.init(url);
|
||||
HttpUtils.initRequestHeader(httpRequestBase, header);
|
||||
HttpUtils.initRequestEntity(httpRequestBase, body, header);
|
||||
return httpRequestBase;
|
||||
}
|
||||
|
||||
public static class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
|
||||
|
||||
public static final String METHOD_NAME = "GET";
|
||||
|
||||
public HttpGetWithEntity(String url) {
|
||||
super();
|
||||
setURI(URI.create(url));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod() {
|
||||
return METHOD_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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.common.http;
|
||||
|
||||
import com.alibaba.nacos.common.utils.ExceptionUtil;
|
||||
import com.alibaba.nacos.common.utils.ThreadUtils;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.nio.client.HttpAsyncClients;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Use the same HttpClient object in the same space.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
* @deprecated Refer to the new {@link HttpClientBeanHolder}
|
||||
*/
|
||||
@Deprecated
|
||||
public class HttpClientManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientManager.class);
|
||||
|
||||
private static final int TIMEOUT = Integer.getInteger("nacos.http.timeout", 5000);
|
||||
|
||||
private static final RequestConfig DEFAULT_CONFIG = RequestConfig.custom().setConnectTimeout(TIMEOUT)
|
||||
.setSocketTimeout(TIMEOUT << 1).build();
|
||||
|
||||
private static final NSyncHttpClient SYNC_HTTP_CLIENT = new NacosSyncHttpClient(
|
||||
HttpClients.custom().setDefaultRequestConfig(DEFAULT_CONFIG).build());
|
||||
|
||||
private static final NAsyncHttpClient ASYNC_HTTP_CLIENT = new NacosAsyncHttpClient(
|
||||
HttpAsyncClients.custom().setDefaultRequestConfig(DEFAULT_CONFIG).build());
|
||||
|
||||
private static final AtomicBoolean ALREADY_SHUTDOWN = new AtomicBoolean(false);
|
||||
|
||||
static {
|
||||
ThreadUtils.addShutdownHook(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
shutdown();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static NSyncHttpClient getSyncHttpClient() {
|
||||
return SYNC_HTTP_CLIENT;
|
||||
}
|
||||
|
||||
public static NAsyncHttpClient getAsyncHttpClient() {
|
||||
return ASYNC_HTTP_CLIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown http client manager and close http client.
|
||||
*/
|
||||
public static void shutdown() {
|
||||
if (!ALREADY_SHUTDOWN.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
LOGGER.warn("[HttpClientManager] Start destroying HttpClient");
|
||||
try {
|
||||
SYNC_HTTP_CLIENT.close();
|
||||
ASYNC_HTTP_CLIENT.close();
|
||||
} catch (Exception ex) {
|
||||
LOGGER.error("An exception occurred when the HTTP client was closed : {}", ExceptionUtil.getStackTrace(ex));
|
||||
}
|
||||
LOGGER.warn("[HttpClientManager] Destruction of the end");
|
||||
}
|
||||
|
||||
}
|
@ -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.common.http;
|
||||
|
||||
import com.alibaba.nacos.common.http.param.Header;
|
||||
import com.alibaba.nacos.common.http.param.Query;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Nacos async http client interface.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
* @deprecated Refer to the new {@link com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("all")
|
||||
public interface NAsyncHttpClient extends NHttpClient {
|
||||
|
||||
/**
|
||||
* http get
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param token return type
|
||||
* @param callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)}
|
||||
*/
|
||||
<T> void get(String url, Header header, Query query, Type token, Callback<T> callback) throws Exception;
|
||||
|
||||
/**
|
||||
* get request, may be pulling a lot of data
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param body get with body
|
||||
* @param token return type
|
||||
* @param callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)}
|
||||
*/
|
||||
<T> void getLarge(String url, Header header, Query query, Object body, Type token, Callback<T> callback)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* http delete
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param token return type
|
||||
* @param callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)}
|
||||
*/
|
||||
<T> void delete(String url, Header header, Query query, Type token, Callback<T> callback) throws Exception;
|
||||
|
||||
/**
|
||||
* http put
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param body http body param
|
||||
* @param token return type
|
||||
* @param callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)}
|
||||
*/
|
||||
<T> void put(String url, Header header, Query query, Object body, Type token, Callback<T> callback)
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* http post
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param body http body param
|
||||
* @param token return type
|
||||
* @param callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)}
|
||||
*/
|
||||
<T> void post(String url, Header header, Query query, Object body, Type token, Callback<T> callback)
|
||||
throws Exception;
|
||||
|
||||
}
|
@ -1,98 +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.common.http;
|
||||
|
||||
import com.alibaba.nacos.common.http.param.Header;
|
||||
import com.alibaba.nacos.common.http.param.Query;
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Nacos sync http client.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
* @deprecated Refer to the new {@link com.alibaba.nacos.common.http.client.NacosRestTemplate}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("all")
|
||||
public interface NSyncHttpClient extends NHttpClient {
|
||||
|
||||
/**
|
||||
* http get
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param token return type
|
||||
* @return {@link RestResult <T>}
|
||||
* @throws Exception
|
||||
*/
|
||||
<T> RestResult<T> get(String url, Header header, Query query, Type token) throws Exception;
|
||||
|
||||
/**
|
||||
* get request, may be pulling a lot of data
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param body get with body
|
||||
* @param token return type
|
||||
* @return {@link RestResult <T>}
|
||||
* @throws Exception
|
||||
*/
|
||||
<T> RestResult<T> getLarge(String url, Header header, Query query, Object body, Type token) throws Exception;
|
||||
|
||||
/**
|
||||
* http delete
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param token return type
|
||||
* @return {@link RestResult <T>}
|
||||
* @throws Exception
|
||||
*/
|
||||
<T> RestResult<T> delete(String url, Header header, Query query, Type token) throws Exception;
|
||||
|
||||
/**
|
||||
* http put
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param body http body param
|
||||
* @param token return type
|
||||
* @return {@link RestResult}
|
||||
* @throws Exception
|
||||
*/
|
||||
<T> RestResult<T> put(String url, Header header, Query query, Object body, Type token) throws Exception;
|
||||
|
||||
/**
|
||||
* http post
|
||||
*
|
||||
* @param url url
|
||||
* @param header http header param
|
||||
* @param query http query param
|
||||
* @param body http body param
|
||||
* @param token return type
|
||||
* @return {@link RestResult}
|
||||
* @throws Exception
|
||||
*/
|
||||
<T> RestResult<T> post(String url, Header header, Query query, Object body, Type token) throws Exception;
|
||||
|
||||
}
|
@ -1,83 +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.common.http;
|
||||
|
||||
import com.alibaba.nacos.common.http.param.Header;
|
||||
import com.alibaba.nacos.common.http.param.Query;
|
||||
import com.alibaba.nacos.common.utils.HttpMethod;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Nacos async http client.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
* @deprecated Refer to the new {@link com.alibaba.nacos.common.http.client.request.DefaultAsyncHttpClientRequest}
|
||||
*/
|
||||
@Deprecated
|
||||
class NacosAsyncHttpClient extends BaseHttpClient implements NAsyncHttpClient {
|
||||
|
||||
private CloseableHttpAsyncClient asyncClient;
|
||||
|
||||
NacosAsyncHttpClient(CloseableHttpAsyncClient asyncClient) {
|
||||
this.asyncClient = asyncClient;
|
||||
this.asyncClient.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void get(final String url, final Header header, final Query query, final Type token,
|
||||
final Callback<T> callback) throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, HttpMethod.GET);
|
||||
execute(asyncClient, token, callback, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void getLarge(final String url, final Header header, final Query query, final Object body,
|
||||
final Type token, final Callback<T> callback) throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, body, HttpMethod.GET_LARGE);
|
||||
execute(asyncClient, token, callback, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void delete(final String url, final Header header, final Query query, final Type token,
|
||||
final Callback<T> callback) throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, HttpMethod.DELETE);
|
||||
execute(asyncClient, token, callback, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void put(final String url, final Header header, final Query query, final Object body, final Type token,
|
||||
final Callback<T> callback) throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, body, HttpMethod.PUT);
|
||||
execute(asyncClient, token, callback, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void post(final String url, final Header header, final Query query, final Object body, final Type token,
|
||||
final Callback<T> callback) throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, body, HttpMethod.POST);
|
||||
execute(asyncClient, token, callback, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
asyncClient.close();
|
||||
}
|
||||
}
|
@ -1,83 +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.common.http;
|
||||
|
||||
import com.alibaba.nacos.common.http.param.Header;
|
||||
import com.alibaba.nacos.common.http.param.Query;
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
import com.alibaba.nacos.common.utils.HttpMethod;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Nacos sync http client.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
* @deprecated Refer to the new {@link com.alibaba.nacos.common.http.client.request.JdkHttpClientRequest}
|
||||
*/
|
||||
@Deprecated
|
||||
class NacosSyncHttpClient extends BaseHttpClient implements NSyncHttpClient {
|
||||
|
||||
private CloseableHttpClient client;
|
||||
|
||||
NacosSyncHttpClient(CloseableHttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> RestResult<T> get(final String url, final Header header, final Query query, final Type token)
|
||||
throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, HttpMethod.GET);
|
||||
return execute(client, token, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> RestResult<T> getLarge(String url, Header header, Query query, Object body, Type token)
|
||||
throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, body, HttpMethod.GET_LARGE);
|
||||
return execute(client, token, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> RestResult<T> delete(final String url, final Header header, final Query query, final Type token)
|
||||
throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, HttpMethod.DELETE);
|
||||
return execute(client, token, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> RestResult<T> put(final String url, final Header header, final Query query, final Object body,
|
||||
final Type token) throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, body, HttpMethod.PUT);
|
||||
return execute(client, token, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> RestResult<T> post(final String url, final Header header, final Query query, final Object body,
|
||||
final Type token) throws Exception {
|
||||
HttpRequestBase requestBase = build(buildUrl(url, query), header, body, HttpMethod.POST);
|
||||
return execute(client, token, requestBase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
client.close();
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ public class DefaultPublisher extends Thread implements EventPublisher {
|
||||
|
||||
protected volatile Long lastEventSequence = -1L;
|
||||
|
||||
private final AtomicReferenceFieldUpdater<DefaultPublisher, Long> updater = AtomicReferenceFieldUpdater
|
||||
private static final AtomicReferenceFieldUpdater<DefaultPublisher, Long> UPDATER = AtomicReferenceFieldUpdater
|
||||
.newUpdater(DefaultPublisher.class, Long.class, "lastEventSequence");
|
||||
|
||||
@Override
|
||||
@ -116,7 +116,7 @@ public class DefaultPublisher extends Thread implements EventPublisher {
|
||||
}
|
||||
final Event event = queue.take();
|
||||
receiveEvent(event);
|
||||
updater.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
|
||||
UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.error("Event listener exception : {}", ex);
|
||||
|
@ -79,7 +79,7 @@ public class DefaultSharePublisher extends DefaultPublisher {
|
||||
try {
|
||||
Set<Subscriber> sets = subMappings.get(subSlowEventType);
|
||||
|
||||
if (sets != null && sets.contains(subscriber)) {
|
||||
if (sets != null) {
|
||||
sets.remove(subscriber);
|
||||
}
|
||||
} finally {
|
||||
|
@ -129,21 +129,22 @@ public class MapUtils {
|
||||
/**
|
||||
* ComputeIfAbsent lazy load.
|
||||
*
|
||||
* @param target target Map data.
|
||||
* @param key map key.
|
||||
* @param mappingFunction funtion which is need to be executed.
|
||||
* @param param1 function's parameter value1.
|
||||
* @param param2 function's parameter value1.
|
||||
* @param target target Map data.
|
||||
* @param key map key.
|
||||
* @param mappingFunction function which is need to be executed.
|
||||
* @param param1 function's parameter value1.
|
||||
* @param param2 function's parameter value1.
|
||||
* @return
|
||||
*/
|
||||
@NotThreadSafe
|
||||
public static Object computeIfAbsent(Map target, Object key, BiFunction mappingFunction, Object param1,
|
||||
Object param2) {
|
||||
|
||||
Objects.requireNonNull(target, "target");
|
||||
Objects.requireNonNull(key, "key");
|
||||
Objects.requireNonNull(key, "mappingFunction");
|
||||
Objects.requireNonNull(key, "param1");
|
||||
Objects.requireNonNull(key, "param2");
|
||||
Objects.requireNonNull(mappingFunction, "mappingFunction");
|
||||
Objects.requireNonNull(param1, "param1");
|
||||
Objects.requireNonNull(param2, "param2");
|
||||
|
||||
Object val = target.get(key);
|
||||
if (val == null) {
|
||||
@ -153,5 +154,4 @@ public class MapUtils {
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.alibaba.nacos.common.utils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Comparator;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
@ -59,6 +60,38 @@ public class VersionUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Comparator<String> STRING_COMPARATOR = new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* compare two version who is latest.
|
||||
*
|
||||
* @param versionA version A, like x.y.z(-beta)
|
||||
* @param versionB version B, like x.y.z(-beta)
|
||||
* @return compare result
|
||||
*/
|
||||
public static int compareVersion(final String versionA, final String versionB) {
|
||||
final String[] sA = versionA.split("\\.");
|
||||
final String[] sB = versionB.split("\\.");
|
||||
int expectSize = 3;
|
||||
if (sA.length != expectSize || sB.length != expectSize) {
|
||||
throw new IllegalArgumentException("version must be like x.y.z(-beta)");
|
||||
}
|
||||
int first = Objects.compare(sA[0], sB[0], STRING_COMPARATOR);
|
||||
if (first != 0) {
|
||||
return first;
|
||||
}
|
||||
int second = Objects.compare(sA[1], sB[1], STRING_COMPARATOR);
|
||||
if (second != 0) {
|
||||
return second;
|
||||
}
|
||||
return Objects.compare(sA[2].split("-")[0], sB[2].split("-")[0], STRING_COMPARATOR);
|
||||
}
|
||||
|
||||
public static String getFullClientVersion() {
|
||||
return clientVersion;
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@ -48,8 +50,9 @@ public class NacosExecuteTaskExecuteEngineTest {
|
||||
private AbstractExecuteTask task;
|
||||
|
||||
@Test
|
||||
public void testAddTask() {
|
||||
public void testAddTask() throws InterruptedException {
|
||||
executeTaskExecuteEngine.addTask("test", task);
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
verify(task).run();
|
||||
assertTrue(executeTaskExecuteEngine.isEmpty());
|
||||
assertEquals(0, executeTaskExecuteEngine.size());
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.common.utils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class VersionUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testVersionCompareLt() {
|
||||
final String versionA = "1.2.0";
|
||||
final String versionB = "1.2.1";
|
||||
Assert.assertTrue(VersionUtils.compareVersion(versionA, versionB) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersionCompareGt() {
|
||||
final String versionA = "1.2.2";
|
||||
final String versionB = "1.2.1";
|
||||
Assert.assertTrue(VersionUtils.compareVersion(versionA, versionB) > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersionCompareEt() {
|
||||
final String versionA = "1.2.1";
|
||||
final String versionB = "1.2.1";
|
||||
Assert.assertEquals(0, VersionUtils.compareVersion(versionA, versionB));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersionCompareLtWithChar() {
|
||||
final String versionA = "1.2.0-beta";
|
||||
final String versionB = "1.2.1";
|
||||
Assert.assertTrue(VersionUtils.compareVersion(versionA, versionB) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersionCompareGtWithChar() {
|
||||
final String versionA = "1.2.2-beta";
|
||||
final String versionB = "1.2.1-beta";
|
||||
Assert.assertTrue(VersionUtils.compareVersion(versionA, versionB) > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersionCompareEtWithChar() {
|
||||
final String versionA = "1.2.1";
|
||||
final String versionB = "1.2.1-beta";
|
||||
Assert.assertEquals(0, VersionUtils.compareVersion(versionA, versionB));
|
||||
}
|
||||
|
||||
}
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -28,9 +28,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* History management controller.
|
||||
*
|
||||
@ -46,11 +43,11 @@ public class HistoryController {
|
||||
/**
|
||||
* Query the list history config.
|
||||
*
|
||||
* @param dataId dataId string value.
|
||||
* @param group group string value.
|
||||
* @param tenant tenant string value.
|
||||
* @param appName appName string value.
|
||||
* @param pageNo pageNo string value.
|
||||
* @param dataId dataId string value.
|
||||
* @param group group string value.
|
||||
* @param tenant tenant string value.
|
||||
* @param appName appName string value.
|
||||
* @param pageNo pageNo string value.
|
||||
* @param pageSize pageSize string value.
|
||||
* @param modelMap modeMap.
|
||||
* @return
|
||||
@ -72,12 +69,26 @@ public class HistoryController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the detailed configuration history informations.
|
||||
* Query the detailed configuration history information.
|
||||
*
|
||||
* @param nid history_config_info nid
|
||||
* @return history config info
|
||||
*/
|
||||
@GetMapping
|
||||
public ConfigHistoryInfo getConfigHistoryInfo(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam("nid") Long nid, ModelMap modelMap) {
|
||||
public ConfigHistoryInfo getConfigHistoryInfo(@RequestParam("nid") Long nid) {
|
||||
return persistService.detailConfigHistory(nid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query previous config history information.
|
||||
*
|
||||
* @param id config_info id
|
||||
* @return history config info
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@GetMapping(value = "/previous")
|
||||
public ConfigHistoryInfo getPreviousConfigHistoryInfo(@RequestParam("id") Long id) {
|
||||
return persistService.detailPreviousConfigHistory(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -133,7 +133,8 @@ public abstract class DumpService {
|
||||
|
||||
protected void dumpOperate(DumpProcessor processor, DumpAllProcessor dumpAllProcessor,
|
||||
DumpAllBetaProcessor dumpAllBetaProcessor, DumpAllTagProcessor dumpAllTagProcessor) throws NacosException {
|
||||
TimerContext.start("CONFIG_DUMP_TO_FILE");
|
||||
String dumpFileContext = "CONFIG_DUMP_TO_FILE";
|
||||
TimerContext.start(dumpFileContext);
|
||||
try {
|
||||
LogUtil.DEFAULT_LOG.warn("DumpService start");
|
||||
|
||||
@ -229,7 +230,7 @@ public abstract class DumpService {
|
||||
|
||||
ConfigExecutor.scheduleConfigTask(clearConfigHistory, 10, 10, TimeUnit.MINUTES);
|
||||
} finally {
|
||||
TimerContext.end(LogUtil.DUMP_LOG);
|
||||
TimerContext.end(dumpFileContext, LogUtil.DUMP_LOG);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1077,6 +1077,15 @@ public interface PersistService {
|
||||
*/
|
||||
ConfigHistoryInfo detailConfigHistory(Long nid);
|
||||
|
||||
/**
|
||||
* Get previous config detail.
|
||||
*
|
||||
* @param id id
|
||||
* @return {@link ConfigHistoryInfo}
|
||||
*/
|
||||
ConfigHistoryInfo detailPreviousConfigHistory(Long id);
|
||||
|
||||
|
||||
/**
|
||||
* insert tenant info.
|
||||
*
|
||||
@ -1229,4 +1238,5 @@ public interface PersistService {
|
||||
* @return count by tenantId
|
||||
*/
|
||||
int tenantInfoCountByTenantId(String tenantId);
|
||||
|
||||
}
|
@ -51,6 +51,10 @@ import javax.sql.DataSource;
|
||||
*/
|
||||
public class DerbySnapshotOperation implements SnapshotOperation {
|
||||
|
||||
private static final String DERBY_SNAPSHOT_SAVE = DerbySnapshotOperation.class.getSimpleName() + ".SAVE";
|
||||
|
||||
private static final String DERBY_SNAPSHOT_LOAD = DerbySnapshotOperation.class.getSimpleName() + ".LOAD";
|
||||
|
||||
private final String backupSql = "CALL SYSCS_UTIL.SYSCS_BACKUP_DATABASE(?)";
|
||||
|
||||
private final String snapshotDir = "derby_data";
|
||||
@ -72,8 +76,7 @@ public class DerbySnapshotOperation implements SnapshotOperation {
|
||||
@Override
|
||||
public void onSnapshotSave(Writer writer, BiConsumer<Boolean, Throwable> callFinally) {
|
||||
RaftExecutor.doSnapshot(() -> {
|
||||
|
||||
TimerContext.start("CONFIG_DERBY_SNAPSHOT_SAVE");
|
||||
TimerContext.start(DERBY_SNAPSHOT_SAVE);
|
||||
|
||||
final Lock lock = writeLock;
|
||||
lock.lock();
|
||||
@ -100,7 +103,7 @@ public class DerbySnapshotOperation implements SnapshotOperation {
|
||||
callFinally.accept(false, t);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
TimerContext.end(LogUtil.FATAL_LOG);
|
||||
TimerContext.end(DERBY_SNAPSHOT_SAVE, LogUtil.FATAL_LOG);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -109,8 +112,7 @@ public class DerbySnapshotOperation implements SnapshotOperation {
|
||||
public boolean onSnapshotLoad(Reader reader) {
|
||||
final String readerPath = reader.getPath();
|
||||
final String sourceFile = Paths.get(readerPath, snapshotArchive).toString();
|
||||
|
||||
TimerContext.start("CONFIG_DERBY_SNAPSHOT_LOAD");
|
||||
TimerContext.start(DERBY_SNAPSHOT_LOAD);
|
||||
final Lock lock = writeLock;
|
||||
lock.lock();
|
||||
try {
|
||||
@ -145,7 +147,7 @@ public class DerbySnapshotOperation implements SnapshotOperation {
|
||||
return false;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
TimerContext.end(LogUtil.FATAL_LOG);
|
||||
TimerContext.end(DERBY_SNAPSHOT_LOAD, LogUtil.FATAL_LOG);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2059,6 +2059,12 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService {
|
||||
return databaseOperate.queryOne(sqlFetchRows, new Object[] {nid}, HISTORY_DETAIL_ROW_MAPPER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigHistoryInfo detailPreviousConfigHistory(Long id) {
|
||||
String sqlFetchRows = "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified FROM his_config_info WHERE nid = (select max(nid) from his_config_info where id = ?)";
|
||||
return databaseOperate.queryOne(sqlFetchRows, new Object[] {id}, HISTORY_DETAIL_ROW_MAPPER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertTenantInfoAtomic(String kp, String tenantId, String tenantName, String tenantDesc,
|
||||
String createResoure, final long time) {
|
||||
@ -2299,7 +2305,7 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService {
|
||||
int skipCount = 0;
|
||||
List<Map<String, String>> failData = null;
|
||||
List<Map<String, String>> skipData = null;
|
||||
|
||||
|
||||
final BiConsumer<Boolean, Throwable> callFinally = (result, t) -> {
|
||||
if (t != null) {
|
||||
throw new NacosRuntimeException(0, t);
|
||||
|
@ -2297,6 +2297,19 @@ public class ExternalStoragePersistServiceImpl implements PersistService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigHistoryInfo detailPreviousConfigHistory(Long id) {
|
||||
String sqlFetchRows = "SELECT nid,data_id,group_id,tenant_id,app_name,content,md5,src_user,src_ip,op_type,gmt_create,gmt_modified FROM his_config_info WHERE nid = (select max(nid) from his_config_info where id = ?) ";
|
||||
try {
|
||||
ConfigHistoryInfo historyInfo = jt
|
||||
.queryForObject(sqlFetchRows, new Object[] {id}, HISTORY_DETAIL_ROW_MAPPER);
|
||||
return historyInfo;
|
||||
} catch (DataAccessException e) {
|
||||
LogUtil.FATAL_LOG.error("[detail-previous-config-history] error, id:{}", new Object[] {id}, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertTenantInfoAtomic(String kp, String tenantId, String tenantName, String tenantDesc,
|
||||
String createResoure, final long time) {
|
||||
|
@ -85,7 +85,7 @@
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@ -48,4 +48,7 @@ public class SerializeFactory {
|
||||
return SERIALIZER_MAP.get(defaultSerializer);
|
||||
}
|
||||
|
||||
public static Serializer getSerializer(String type) {
|
||||
return SERIALIZER_MAP.get(type.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.alibaba.nacos.consistency;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -28,6 +29,15 @@ public interface Serializer {
|
||||
|
||||
Map<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>(8);
|
||||
|
||||
/**
|
||||
* Deserialize the data.
|
||||
*
|
||||
* @param data byte[]
|
||||
* @param <T> class type
|
||||
* @return target object instance
|
||||
*/
|
||||
<T> T deserialize(byte[] data);
|
||||
|
||||
/**
|
||||
* Deserialize the data.
|
||||
*
|
||||
@ -36,7 +46,17 @@ public interface Serializer {
|
||||
* @param <T> class type
|
||||
* @return target object instance
|
||||
*/
|
||||
<T> T deserialize(byte[] data, Class cls);
|
||||
<T> T deserialize(byte[] data, Class<T> cls);
|
||||
|
||||
/**
|
||||
* Deserialize the data.
|
||||
*
|
||||
* @param data byte[]
|
||||
* @param type data type
|
||||
* @param <T> class type
|
||||
* @return target object instance
|
||||
*/
|
||||
<T> T deserialize(byte[] data, Type type);
|
||||
|
||||
/**
|
||||
* Deserialize the data.
|
||||
|
@ -29,4 +29,6 @@ public class MetadataKey {
|
||||
|
||||
public static final String RAFT_GROUP_MEMBER = "raftGroupMember";
|
||||
|
||||
public static final String ERR_MSG = "errMsg";
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import com.caucho.hessian.io.SerializerFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* hessian serializer.
|
||||
@ -40,12 +41,25 @@ public class HessianSerializer implements Serializer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(byte[] data, Class cls) {
|
||||
|
||||
public <T> T deserialize(byte[] data) {
|
||||
return deseiralize0(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(byte[] data, Class<T> cls) {
|
||||
return deserialize(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(byte[] data, Type type) {
|
||||
return deserialize(data);
|
||||
}
|
||||
|
||||
private <T> T deseiralize0(byte[] data) {
|
||||
if (ByteUtils.isEmpty(data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(data));
|
||||
input.setSerializerFactory(serializerFactory);
|
||||
Object resultObject;
|
||||
@ -78,4 +92,4 @@ public class HessianSerializer implements Serializer {
|
||||
return "Hessian";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.consistency.serialize;
|
||||
|
||||
import com.alibaba.nacos.common.utils.ByteUtils;
|
||||
import com.alibaba.nacos.common.utils.JacksonUtils;
|
||||
import com.alibaba.nacos.consistency.Serializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Serializer implement by jackson.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class JacksonSerializer implements Serializer {
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(byte[] data) {
|
||||
throw new UnsupportedOperationException("Jackson serializer can't support deserialize json without type");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(byte[] data, Class<T> cls) {
|
||||
if (ByteUtils.isEmpty(data)) {
|
||||
return null;
|
||||
}
|
||||
return JacksonUtils.toObj(data, cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(byte[] data, Type type) {
|
||||
if (ByteUtils.isEmpty(data)) {
|
||||
return null;
|
||||
}
|
||||
return JacksonUtils.toObj(data, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> byte[] serialize(T obj) {
|
||||
return JacksonUtils.toJsonBytes(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "JSON";
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
com.alibaba.nacos.consistency.serialize.JacksonSerializer
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nacos-console</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
@ -32,6 +32,9 @@ class DiffEditorDialog extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
publishConfig: PropTypes.func,
|
||||
title: PropTypes.string,
|
||||
currentArea: PropTypes.string,
|
||||
originalArea: PropTypes.string,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -71,6 +74,7 @@ class DiffEditorDialog extends React.Component {
|
||||
highlightDifferences: true,
|
||||
connect: 'align',
|
||||
collapseIdentical: false,
|
||||
revertButtons: typeof this.props.publishConfig === 'function',
|
||||
});
|
||||
}
|
||||
|
||||
@ -80,19 +84,29 @@ class DiffEditorDialog extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const { locale = {}, title, currentArea, originalArea } = this.props;
|
||||
const publishButton = (
|
||||
<Button type="primary" onClick={this.confirmPub.bind(this)}>
|
||||
{locale.publish}
|
||||
</Button>
|
||||
);
|
||||
const footer = (
|
||||
<div>
|
||||
{' '}
|
||||
<Button type="primary" onClick={this.confirmPub.bind(this)}>
|
||||
{locale.publish}
|
||||
</Button>
|
||||
{typeof this.props.publishConfig === 'function' ? (
|
||||
publishButton
|
||||
) : (
|
||||
<Button type="primary" onClick={this.closeDialog.bind(this)}>
|
||||
{locale.back}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
console.log(footer);
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
title={locale.contents}
|
||||
title={title}
|
||||
style={{ width: '80%' }}
|
||||
visible={this.state.dialogvisible}
|
||||
footer={footer}
|
||||
@ -101,8 +115,8 @@ class DiffEditorDialog extends React.Component {
|
||||
<div style={{ height: 400 }}>
|
||||
<div>
|
||||
<Row>
|
||||
<Col style={{ textAlign: 'center' }}>{locale.currentArea}</Col>
|
||||
<Col style={{ textAlign: 'center' }}>{locale.originalValue}</Col>
|
||||
<Col style={{ textAlign: 'center' }}>{currentArea}</Col>
|
||||
<Col style={{ textAlign: 'center' }}>{originalArea}</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div style={{ clear: 'both', height: 480 }} ref={this.diffeditor} />
|
||||
|
@ -378,9 +378,7 @@ const I18N_CONF = {
|
||||
},
|
||||
DiffEditorDialog: {
|
||||
publish: 'Publish',
|
||||
contents: 'Content Comparison',
|
||||
currentArea: 'Current Value',
|
||||
originalValue: 'Original Value',
|
||||
back: 'Back',
|
||||
},
|
||||
ConfigEditor: {
|
||||
official: 'Official',
|
||||
@ -411,6 +409,9 @@ const I18N_CONF = {
|
||||
publish: 'Publish',
|
||||
back: 'Back',
|
||||
codeValErrorPrompt: 'Configuration information may have syntax errors. Are you sure to submit?',
|
||||
dialogTitle: 'Content Comparison',
|
||||
dialogCurrentArea: 'Current Value',
|
||||
dialogOriginalArea: 'Original Value',
|
||||
},
|
||||
EditorNameSpace: {
|
||||
notice: 'Notice',
|
||||
@ -498,6 +499,9 @@ const I18N_CONF = {
|
||||
betaRelease: 'Beta Publish:',
|
||||
configuration: 'Configuration Content:',
|
||||
back: 'Back',
|
||||
versionComparison: 'Version Comparison',
|
||||
dialogCurrentArea: 'Current Version',
|
||||
dialogOriginalArea: 'Previous Version',
|
||||
},
|
||||
ConfigRollback: {
|
||||
rollBack: 'Roll Back',
|
||||
|
@ -376,9 +376,7 @@ const I18N_CONF = {
|
||||
},
|
||||
DiffEditorDialog: {
|
||||
publish: '确认发布',
|
||||
contents: '内容比较',
|
||||
currentArea: '当前值',
|
||||
originalValue: '原始值',
|
||||
back: '返回',
|
||||
},
|
||||
ConfigEditor: {
|
||||
official: '正式',
|
||||
@ -409,6 +407,9 @@ const I18N_CONF = {
|
||||
publish: '发布',
|
||||
back: '返回',
|
||||
codeValErrorPrompt: '配置信息可能有语法错误, 确定提交吗?',
|
||||
dialogTitle: '内容比较',
|
||||
dialogCurrentArea: '当前值',
|
||||
dialogOriginalArea: '原始值',
|
||||
},
|
||||
EditorNameSpace: {
|
||||
notice: '提示',
|
||||
@ -495,6 +496,9 @@ const I18N_CONF = {
|
||||
betaRelease: 'Beta发布:',
|
||||
configuration: '配置内容:',
|
||||
back: '返回',
|
||||
versionComparison: '版本对比',
|
||||
dialogCurrentArea: '当前版本',
|
||||
dialogOriginalArea: '上一版本',
|
||||
},
|
||||
ConfigRollback: {
|
||||
rollBack: '回滚配置',
|
||||
|
@ -15,15 +15,27 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, ConfigProvider, Dialog, Field, Form, Input, Loading, Tab } from '@alifd/next';
|
||||
import {
|
||||
Button,
|
||||
ConfigProvider,
|
||||
Dialog,
|
||||
Field,
|
||||
Form,
|
||||
Input,
|
||||
Loading,
|
||||
Tab,
|
||||
Grid,
|
||||
} from '@alifd/next';
|
||||
import { getParams, request } from '../../../globalLib';
|
||||
import { generateUrl } from '../../../utils/nacosutil';
|
||||
import DiffEditorDialog from '../../../components/DiffEditorDialog';
|
||||
|
||||
import './index.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const TabPane = Tab.Item;
|
||||
const FormItem = Form.Item;
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
@ConfigProvider.config
|
||||
class ConfigDetail extends React.Component {
|
||||
@ -56,6 +68,7 @@ class ConfigDetail extends React.Component {
|
||||
this.searchGroup = getParams('searchGroup') || '';
|
||||
this.pageSize = getParams('pageSize');
|
||||
this.pageNo = getParams('pageNo');
|
||||
this.diffEditorDialog = React.createRef();
|
||||
// this.params = window.location.hash.split('?')[1]||'';
|
||||
}
|
||||
|
||||
@ -188,6 +201,32 @@ class ConfigDetail extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
openDiff() {
|
||||
let self = this;
|
||||
const { locale = {} } = this.props;
|
||||
let leftvalue = this.monacoEditor.getValue();
|
||||
let url = `v1/cs/history/previous?id=${this.valueMap.normal.id}`;
|
||||
request({
|
||||
url,
|
||||
beforeSend() {
|
||||
self.openLoading();
|
||||
},
|
||||
success(result) {
|
||||
if (result != null) {
|
||||
let rightvalue = result.content;
|
||||
leftvalue = leftvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
rightvalue = rightvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
self.diffEditorDialog.current.getInstance().openDialog(leftvalue, rightvalue);
|
||||
} else {
|
||||
Dialog.alert({ title: locale.error, content: result.message });
|
||||
}
|
||||
},
|
||||
complete() {
|
||||
self.closeLoading();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const { init } = this.field;
|
||||
@ -276,12 +315,23 @@ class ConfigDetail extends React.Component {
|
||||
<FormItem label={locale.configuration} required {...formItemLayout}>
|
||||
<div style={{ clear: 'both', height: 300 }} id="container" />
|
||||
</FormItem>
|
||||
<FormItem label={' '} {...formItemLayout}>
|
||||
<Button type={'primary'} onClick={this.goList.bind(this)}>
|
||||
</Form>
|
||||
<Row>
|
||||
<Col span="24" className="button-list">
|
||||
<Button size="large" type="primary" onClick={this.openDiff.bind(this)}>
|
||||
{locale.versionComparison}
|
||||
</Button>{' '}
|
||||
<Button size="large" type="normal" onClick={this.goList.bind(this)}>
|
||||
{locale.back}
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
<DiffEditorDialog
|
||||
ref={this.diffEditorDialog}
|
||||
title={locale.versionComparison}
|
||||
currentArea={locale.dialogCurrentArea}
|
||||
originalArea={locale.dialogOriginalArea}
|
||||
/>
|
||||
</Loading>
|
||||
</div>
|
||||
);
|
||||
|
@ -13,3 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.button-list {
|
||||
text-align: right;
|
||||
button {
|
||||
margin-left: 1em;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
@ -605,6 +605,9 @@ class ConfigEditor extends React.Component {
|
||||
});
|
||||
});
|
||||
}}
|
||||
title={locale.dialogTitle}
|
||||
currentArea={locale.dialogCurrentArea}
|
||||
originalArea={locale.dialogOriginalArea}
|
||||
/>
|
||||
<SuccessDialog ref={this.successDialog} />
|
||||
</Loading>
|
||||
|
@ -0,0 +1,428 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chai = require('chai');
|
||||
const should = chai.should();
|
||||
const JWebDriver = require('jwebdriver');
|
||||
chai.use(JWebDriver.chaiSupportChainPromise);
|
||||
const resemble = require('resemblejs-node');
|
||||
resemble.outputSettings({
|
||||
errorType: 'flatDifferenceIntensity',
|
||||
});
|
||||
|
||||
const rootPath = getRootPath();
|
||||
|
||||
module.exports = function() {
|
||||
let driver, testVars;
|
||||
|
||||
before(function() {
|
||||
let self = this;
|
||||
driver = self.driver;
|
||||
testVars = self.testVars;
|
||||
});
|
||||
|
||||
it('url: http://localhost:8000', async function() {
|
||||
await driver.url(_(`http://localhost:8000`));
|
||||
});
|
||||
|
||||
it('waitBody: ', async function() {
|
||||
await driver
|
||||
.sleep(500)
|
||||
.wait('body', 30000)
|
||||
.html()
|
||||
.then(function(code) {
|
||||
isPageError(code).should.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
it('click: #username, 126, 37, 0', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('#username', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(126, 37)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('sendKeys: nacos{TAB}nacos{ENTER}', async function() {
|
||||
await driver.sendKeys('nacos{TAB}nacos{ENTER}');
|
||||
});
|
||||
|
||||
it('click: 详情 ( #root tr.first > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1), 11, 3, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait(
|
||||
'#root tr.first > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1)',
|
||||
30000
|
||||
)
|
||||
.sleep(300)
|
||||
.mouseMove(11, 3)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: 版本对比 ( //span[text()="版本对比"], 41, 0, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('//span[text()="版本对比"]', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(41, 0)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: i.next-icon-close, 1, 2, 0', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('i.next-icon-close', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(1, 2)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: 返回 ( #root button.next-btn-normal, 36, 9, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('#root button.next-btn-normal', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(36, 9)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: 详情 ( #root tr.first > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1), 10, 2, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait(
|
||||
'#root tr.first > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1)',
|
||||
30000
|
||||
)
|
||||
.sleep(300)
|
||||
.mouseMove(10, 2)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: 版本对比 ( //span[text()="版本对比"], 20, -1, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('//span[text()="版本对比"]', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(20, -1)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: i.next-icon-close, 5, 5, 0', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('i.next-icon-close', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(5, 5)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: En ( //span[text()="En"], 13, 13, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('//span[text()="En"]', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(13, 13)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: Version Comparison ( #root button.next-btn-primary > span.next-btn-helper, 67, 4, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('#root button.next-btn-primary > span.next-btn-helper', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(67, 4)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: Back ( button.next-medium, 11, 21, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('button.next-medium', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(11, 21)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: Version Comparison ( #root button.next-btn-primary > span.next-btn-helper, 128, 0, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('#root button.next-btn-primary > span.next-btn-helper', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(128, 0)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: i.next-icon-close, 7, 9, 0', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('i.next-icon-close', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(7, 9)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: Back ( //span[text()="Back"], 11, -1, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('//span[text()="Back"]', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(11, -1)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: Details ( #root tr.first > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1), 25, 8, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait(
|
||||
'#root tr.first > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1)',
|
||||
30000
|
||||
)
|
||||
.sleep(300)
|
||||
.mouseMove(25, 8)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
it('click: Version Comparison ( #root button.next-btn-primary > span.next-btn-helper, 104, 3, 0 )', async function() {
|
||||
await driver
|
||||
.sleep(300)
|
||||
.wait('#root button.next-btn-primary > span.next-btn-helper', 30000)
|
||||
.sleep(300)
|
||||
.mouseMove(104, 3)
|
||||
.click(0);
|
||||
});
|
||||
|
||||
function _(str) {
|
||||
if (typeof str === 'string') {
|
||||
return str.replace(/\{\{(.+?)\}\}/g, function(all, key) {
|
||||
return testVars[key] || '';
|
||||
});
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (module.parent && /mocha\.js/.test(module.parent.id)) {
|
||||
runThisSpec();
|
||||
}
|
||||
|
||||
function runThisSpec() {
|
||||
// read config
|
||||
let webdriver = process.env['webdriver'] || '';
|
||||
let proxy = process.env['wdproxy'] || '';
|
||||
let config = require(rootPath + '/config.json');
|
||||
let webdriverConfig = Object.assign({}, config.webdriver);
|
||||
let host = webdriverConfig.host;
|
||||
let port = webdriverConfig.port || 4444;
|
||||
let group = webdriverConfig.group || 'default';
|
||||
let match = webdriver.match(/([^\:]+)(?:\:(\d+))?/);
|
||||
if (match) {
|
||||
host = match[1] || host;
|
||||
port = match[2] || port;
|
||||
}
|
||||
let testVars = config.vars;
|
||||
let browsers = webdriverConfig.browsers;
|
||||
browsers = browsers.replace(/^\s+|\s+$/g, '');
|
||||
delete webdriverConfig.host;
|
||||
delete webdriverConfig.port;
|
||||
delete webdriverConfig.group;
|
||||
delete webdriverConfig.browsers;
|
||||
|
||||
// read hosts
|
||||
let hostsPath = rootPath + '/hosts';
|
||||
let hosts = '';
|
||||
if (fs.existsSync(hostsPath)) {
|
||||
hosts = fs.readFileSync(hostsPath).toString();
|
||||
}
|
||||
let specName = path
|
||||
.relative(rootPath, __filename)
|
||||
.replace(/\\/g, '/')
|
||||
.replace(/\.js$/, '');
|
||||
|
||||
browsers.split(/\s*,\s*/).forEach(function(browserName) {
|
||||
let caseName = specName + ' : ' + browserName;
|
||||
|
||||
let browserInfo = browserName.split(' ');
|
||||
browserName = browserInfo[0];
|
||||
let browserVersion = browserInfo[1];
|
||||
|
||||
describe(caseName, function() {
|
||||
this.timeout(600000);
|
||||
this.slow(1000);
|
||||
|
||||
let driver;
|
||||
before(function() {
|
||||
let self = this;
|
||||
let driver = new JWebDriver({
|
||||
host: host,
|
||||
port: port,
|
||||
});
|
||||
let sessionConfig = Object.assign({}, webdriverConfig, {
|
||||
group: group,
|
||||
browserName: browserName,
|
||||
version: browserVersion,
|
||||
'ie.ensureCleanSession': true,
|
||||
});
|
||||
if (proxy) {
|
||||
sessionConfig.proxy = {
|
||||
proxyType: 'manual',
|
||||
httpProxy: proxy,
|
||||
sslProxy: proxy,
|
||||
};
|
||||
} else if (hosts) {
|
||||
sessionConfig.hosts = hosts;
|
||||
}
|
||||
|
||||
try {
|
||||
self.driver = driver
|
||||
.session(sessionConfig)
|
||||
.windowSize(1024, 768)
|
||||
.config({
|
||||
pageloadTimeout: 30000, // page onload timeout
|
||||
scriptTimeout: 5000, // sync script timeout
|
||||
asyncScriptTimeout: 10000, // async script timeout
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
self.testVars = testVars;
|
||||
let casePath = path.dirname(caseName);
|
||||
if (config.reporter && config.reporter.distDir) {
|
||||
self.screenshotPath = config.reporter.distDir + '/reports/screenshots/' + casePath;
|
||||
self.diffbasePath = config.reporter.distDir + '/reports/diffbase/' + casePath;
|
||||
} else {
|
||||
self.screenshotPath = rootPath + '/reports/screenshots/' + casePath;
|
||||
self.diffbasePath = rootPath + '/reports/diffbase/' + casePath;
|
||||
}
|
||||
self.caseName = caseName.replace(/.*\//g, '').replace(/\s*[:\.\:\-\s]\s*/g, '_');
|
||||
mkdirs(self.screenshotPath);
|
||||
mkdirs(self.diffbasePath);
|
||||
self.stepId = 0;
|
||||
return self.driver;
|
||||
});
|
||||
|
||||
module.exports();
|
||||
|
||||
beforeEach(function() {
|
||||
let self = this;
|
||||
self.stepId++;
|
||||
if (self.skipAll) {
|
||||
self.skip();
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async function() {
|
||||
let self = this;
|
||||
let currentTest = self.currentTest;
|
||||
let title = currentTest.title;
|
||||
if (
|
||||
currentTest.state === 'failed' &&
|
||||
/^(url|waitBody|switchWindow|switchFrame):/.test(title)
|
||||
) {
|
||||
self.skipAll = true;
|
||||
}
|
||||
|
||||
if (
|
||||
(config.screenshots && config.screenshots.captureAll && !/^(closeWindow):/.test(title)) ||
|
||||
currentTest.state === 'failed'
|
||||
) {
|
||||
const casePath = path.dirname(caseName);
|
||||
const filepath = `${self.screenshotPath}/${self.caseName}_${self.stepId}`;
|
||||
const relativeFilePath = `./screenshots/${casePath}/${self.caseName}_${self.stepId}`;
|
||||
let driver = self.driver;
|
||||
try {
|
||||
// catch error when get alert msg
|
||||
await driver.getScreenshot(filepath + '.png');
|
||||
let url = await driver.url();
|
||||
let html = await driver.source();
|
||||
html = '<!--url: ' + url + ' -->\n' + html;
|
||||
fs.writeFileSync(filepath + '.html', html);
|
||||
let cookies = await driver.cookies();
|
||||
fs.writeFileSync(filepath + '.cookie', JSON.stringify(cookies));
|
||||
appendToContext(self, relativeFilePath + '.png');
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
|
||||
after(function() {
|
||||
return this.driver.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getRootPath() {
|
||||
let rootPath = path.resolve(__dirname);
|
||||
while (rootPath) {
|
||||
if (fs.existsSync(rootPath + '/config.json')) {
|
||||
break;
|
||||
}
|
||||
rootPath = rootPath.substring(0, rootPath.lastIndexOf(path.sep));
|
||||
}
|
||||
return rootPath;
|
||||
}
|
||||
|
||||
function mkdirs(dirname) {
|
||||
if (fs.existsSync(dirname)) {
|
||||
return true;
|
||||
} else {
|
||||
if (mkdirs(path.dirname(dirname))) {
|
||||
fs.mkdirSync(dirname);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function callSpec(name) {
|
||||
try {
|
||||
require(rootPath + '/' + name)();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function isPageError(code) {
|
||||
return (
|
||||
code == '' ||
|
||||
/ jscontent="errorCode" jstcache="\d+"|diagnoseConnectionAndRefresh|dnserror_unavailable_header|id="reportCertificateErrorRetry"|400 Bad Request|403 Forbidden|404 Not Found|500 Internal Server Error|502 Bad Gateway|503 Service Temporarily Unavailable|504 Gateway Time-out/i.test(
|
||||
code
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function appendToContext(mocha, content) {
|
||||
try {
|
||||
const test = mocha.currentTest || mocha.test;
|
||||
|
||||
if (!test.context) {
|
||||
test.context = content;
|
||||
} else if (Array.isArray(test.context)) {
|
||||
test.context.push(content);
|
||||
} else {
|
||||
test.context = [test.context];
|
||||
test.context.push(content);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error', e);
|
||||
}
|
||||
}
|
||||
|
||||
function catchError(error) {}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -108,6 +108,10 @@ public class Member implements Comparable<Member>, Cloneable {
|
||||
extendInfo.put(key, value);
|
||||
}
|
||||
|
||||
public void delExtendVal(String key) {
|
||||
extendInfo.remove(key);
|
||||
}
|
||||
|
||||
public boolean check() {
|
||||
return StringUtils.isNoneBlank(ip, address) && port != -1;
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ package com.alibaba.nacos.core.code;
|
||||
import com.alibaba.nacos.common.executor.ExecutorFactory;
|
||||
import com.alibaba.nacos.common.executor.NameThreadFactory;
|
||||
import com.alibaba.nacos.common.executor.ThreadPoolManager;
|
||||
import com.alibaba.nacos.common.http.HttpClientManager;
|
||||
import com.alibaba.nacos.common.notify.NotifyCenter;
|
||||
import com.alibaba.nacos.sys.file.WatchFileCenter;
|
||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||
@ -142,8 +141,6 @@ public class StartingSpringApplicationRunListener implements SpringApplicationRu
|
||||
logFilePath();
|
||||
|
||||
LOGGER.error("Startup errors : {}", exception);
|
||||
|
||||
HttpClientManager.shutdown();
|
||||
ThreadPoolManager.shutdown();
|
||||
WatchFileCenter.shutdown();
|
||||
NotifyCenter.shutdown();
|
||||
|
@ -17,9 +17,9 @@
|
||||
package com.alibaba.nacos.core.controller;
|
||||
|
||||
import com.alibaba.nacos.common.http.Callback;
|
||||
import com.alibaba.nacos.common.http.HttpClientManager;
|
||||
import com.alibaba.nacos.common.http.HttpClientBeanHolder;
|
||||
import com.alibaba.nacos.common.http.HttpUtils;
|
||||
import com.alibaba.nacos.common.http.NAsyncHttpClient;
|
||||
import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate;
|
||||
import com.alibaba.nacos.common.http.param.Header;
|
||||
import com.alibaba.nacos.common.http.param.Query;
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
@ -152,7 +152,7 @@ public class NacosClusterController {
|
||||
public RestResult<String> leave(@RequestBody Collection<String> params) throws Exception {
|
||||
Collection<Member> memberList = MemberUtils.multiParse(params);
|
||||
memberManager.memberLeave(memberList);
|
||||
final NAsyncHttpClient asyncHttpClient = HttpClientManager.getAsyncHttpClient();
|
||||
final NacosAsyncRestTemplate nacosAsyncRestTemplate = HttpClientBeanHolder.getNacosAsyncRestTemplate(Loggers.CLUSTER);
|
||||
final GenericType<RestResult<String>> genericType = new GenericType<RestResult<String>>() {
|
||||
};
|
||||
final Collection<Member> notifyList = memberManager.allMembersWithoutSelf();
|
||||
@ -162,7 +162,7 @@ public class NacosClusterController {
|
||||
final String url = HttpUtils
|
||||
.buildUrl(false, member.getAddress(), ApplicationUtils.getContextPath(), Commons.NACOS_CORE_CONTEXT,
|
||||
"/cluster/server/leave");
|
||||
asyncHttpClient.post(url, Header.EMPTY, Query.EMPTY, params, genericType.getType(), new Callback<String>() {
|
||||
nacosAsyncRestTemplate.post(url, Header.EMPTY, Query.EMPTY, params, genericType.getType(), new Callback<String>() {
|
||||
@Override
|
||||
public void onReceive(RestResult<String> result) {
|
||||
try {
|
||||
|
@ -36,6 +36,7 @@ import org.springframework.context.event.ContextStartedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
@ -108,6 +109,7 @@ public class ProtocolManager extends MemberChangeListener implements Application
|
||||
return apProtocol;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
if (Objects.nonNull(apProtocol)) {
|
||||
apProtocol.shutdown();
|
||||
|
@ -51,7 +51,7 @@ public class DistroProtocol {
|
||||
|
||||
private final DistroConfig distroConfig;
|
||||
|
||||
private volatile boolean loadCompleted = false;
|
||||
private volatile boolean isInitialized = false;
|
||||
|
||||
public DistroProtocol(ServerMemberManager memberManager, DistroComponentHolder distroComponentHolder,
|
||||
DistroTaskEngineHolder distroTaskEngineHolder, DistroConfig distroConfig) {
|
||||
@ -59,32 +59,41 @@ public class DistroProtocol {
|
||||
this.distroComponentHolder = distroComponentHolder;
|
||||
this.distroTaskEngineHolder = distroTaskEngineHolder;
|
||||
this.distroConfig = distroConfig;
|
||||
startVerifyTask();
|
||||
startDistroTask();
|
||||
}
|
||||
|
||||
private void startVerifyTask() {
|
||||
private void startDistroTask() {
|
||||
if (ApplicationUtils.getStandaloneMode()) {
|
||||
isInitialized = true;
|
||||
return;
|
||||
}
|
||||
startVerifyTask();
|
||||
startLoadTask();
|
||||
}
|
||||
|
||||
private void startLoadTask() {
|
||||
DistroCallback loadCallback = new DistroCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
loadCompleted = true;
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(Throwable throwable) {
|
||||
loadCompleted = false;
|
||||
isInitialized = false;
|
||||
}
|
||||
};
|
||||
GlobalExecutor.schedulePartitionDataTimedSync(new DistroVerifyTask(memberManager, distroComponentHolder),
|
||||
distroConfig.getVerifyIntervalMillis());
|
||||
GlobalExecutor.submitLoadDataTask(
|
||||
new DistroLoadDataTask(memberManager, distroComponentHolder, distroConfig, loadCallback));
|
||||
}
|
||||
|
||||
public boolean isLoadCompleted() {
|
||||
return loadCompleted;
|
||||
private void startVerifyTask() {
|
||||
GlobalExecutor.schedulePartitionDataTimedSync(new DistroVerifyTask(memberManager, distroComponentHolder),
|
||||
distroConfig.getVerifyIntervalMillis());
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,6 +18,7 @@ package com.alibaba.nacos.core.distributed.id;
|
||||
|
||||
import com.alibaba.nacos.consistency.IdGenerator;
|
||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||
import com.alibaba.nacos.sys.utils.InetUtils;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -86,7 +87,7 @@ public class SnowFlowerIdGenerator implements IdGenerator {
|
||||
} else {
|
||||
InetAddress address;
|
||||
try {
|
||||
address = InetAddress.getLocalHost();
|
||||
address = InetAddress.getByName(InetUtils.getSelfIP());
|
||||
} catch (final UnknownHostException e) {
|
||||
throw new IllegalStateException("Cannot get LocalHost InetAddress, please check your network!", e);
|
||||
}
|
||||
|
@ -133,12 +133,14 @@ public class JRaftProtocol extends AbstractConsistencyProtocol<RaftConfig, LogPr
|
||||
final String leader = event.getLeader();
|
||||
final Long term = event.getTerm();
|
||||
final List<String> raftClusterInfo = event.getRaftClusterInfo();
|
||||
final String errMsg = event.getErrMsg();
|
||||
|
||||
// Leader information needs to be selectively updated. If it is valid data,
|
||||
// the information in the protocol metadata is updated.
|
||||
MapUtils.putIfValNoEmpty(properties, MetadataKey.LEADER_META_DATA, leader);
|
||||
MapUtils.putIfValNoNull(properties, MetadataKey.TERM_META_DATA, term);
|
||||
MapUtils.putIfValNoEmpty(properties, MetadataKey.RAFT_GROUP_MEMBER, raftClusterInfo);
|
||||
MapUtils.putIfValNoEmpty(properties, MetadataKey.ERR_MSG, errMsg);
|
||||
|
||||
value.put(groupId, properties);
|
||||
metaData.load(value);
|
||||
@ -198,6 +200,7 @@ public class JRaftProtocol extends AbstractConsistencyProtocol<RaftConfig, LogPr
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (initialized.get() && shutdowned.compareAndSet(false, true)) {
|
||||
Loggers.RAFT.info("shutdown jraft server");
|
||||
raftServer.shutdown();
|
||||
}
|
||||
}
|
||||
|
@ -497,17 +497,17 @@ public class JRaftServer {
|
||||
Configuration oldConf = instance.getConfiguration(groupName);
|
||||
String oldLeader = Optional.ofNullable(instance.selectLeader(groupName)).orElse(PeerId.emptyPeer())
|
||||
.getEndpoint().toString();
|
||||
status = instance.refreshConfiguration(this.cliClientService, groupName, rpcRequestTimeoutMs);
|
||||
if (!status.isOk()) {
|
||||
Loggers.RAFT
|
||||
.error("Fail to refresh route configuration for group : {}, status is : {}", groupName, status);
|
||||
}
|
||||
// fix issue #3661 https://github.com/alibaba/nacos/issues/3661
|
||||
status = instance.refreshLeader(this.cliClientService, groupName, rpcRequestTimeoutMs);
|
||||
if (!status.isOk()) {
|
||||
Loggers.RAFT
|
||||
.error("Fail to refresh leader for group : {}, status is : {}", groupName, status);
|
||||
}
|
||||
status = instance.refreshConfiguration(this.cliClientService, groupName, rpcRequestTimeoutMs);
|
||||
if (!status.isOk()) {
|
||||
Loggers.RAFT
|
||||
.error("Fail to refresh route configuration for group : {}, status is : {}", groupName, status);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Loggers.RAFT.error("Fail to refresh raft metadata info for group : {}, error is : {}", groupName, e);
|
||||
}
|
||||
|
@ -225,6 +225,10 @@ class NacosStateMachine extends StateMachineAdapter {
|
||||
public void onError(RaftException e) {
|
||||
super.onError(e);
|
||||
processor.onError(e);
|
||||
NotifyCenter.publishEvent(
|
||||
RaftEvent.builder().groupId(groupId).leader(leaderIp).term(term).raftClusterInfo(allPeers())
|
||||
.errMsg(e.toString())
|
||||
.build());
|
||||
}
|
||||
|
||||
public boolean isLeader() {
|
||||
|
@ -37,6 +37,8 @@ public class RaftEvent extends SlowEvent {
|
||||
|
||||
private Long term = null;
|
||||
|
||||
private String errMsg = "";
|
||||
|
||||
private List<String> raftClusterInfo = Collections.emptyList();
|
||||
|
||||
public static RaftEventBuilder builder() {
|
||||
@ -75,6 +77,14 @@ public class RaftEvent extends SlowEvent {
|
||||
this.raftClusterInfo = raftClusterInfo;
|
||||
}
|
||||
|
||||
public String getErrMsg() {
|
||||
return errMsg;
|
||||
}
|
||||
|
||||
public void setErrMsg(String errMsg) {
|
||||
this.errMsg = errMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RaftEvent{" + "groupId='" + groupId + '\'' + ", leader='" + leader + '\'' + ", term=" + term
|
||||
@ -91,6 +101,8 @@ public class RaftEvent extends SlowEvent {
|
||||
|
||||
private List<String> raftClusterInfo = Collections.emptyList();
|
||||
|
||||
private String errMsg = "";
|
||||
|
||||
private RaftEventBuilder() {
|
||||
}
|
||||
|
||||
@ -113,6 +125,11 @@ public class RaftEvent extends SlowEvent {
|
||||
this.raftClusterInfo = raftClusterInfo;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RaftEventBuilder errMsg(String errMsg) {
|
||||
this.errMsg = errMsg;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RaftEvent build() {
|
||||
RaftEvent raftEvent = new RaftEvent();
|
||||
@ -120,6 +137,7 @@ public class RaftEvent extends SlowEvent {
|
||||
raftEvent.setLeader(leader);
|
||||
raftEvent.setTerm(term);
|
||||
raftEvent.setRaftClusterInfo(raftClusterInfo);
|
||||
raftEvent.setErrMsg(errMsg);
|
||||
return raftEvent;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.exception;
|
||||
|
||||
/**
|
||||
* Core module code starts with 40001.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
public enum ErrorCode {
|
||||
|
||||
/**
|
||||
* unknow error.
|
||||
*/
|
||||
UnKnowError(40001),
|
||||
|
||||
// kv error
|
||||
|
||||
/**
|
||||
* KVStorage write error.
|
||||
*/
|
||||
KVStorageWriteError(40100),
|
||||
|
||||
/**
|
||||
* KVStorage read error.
|
||||
*/
|
||||
KVStorageReadError(40101),
|
||||
|
||||
/**
|
||||
* KVStorage delete error.
|
||||
*/
|
||||
KVStorageDeleteError(40102),
|
||||
|
||||
/**
|
||||
* KVStorage snapshot save error.
|
||||
*/
|
||||
KVStorageSnapshotSaveError(40103),
|
||||
|
||||
/**
|
||||
* KVStorage snapshot load error.
|
||||
*/
|
||||
KVStorageSnapshotLoadError(40104),
|
||||
|
||||
/**
|
||||
* KVStorage reset error.
|
||||
*/
|
||||
KVStorageResetError(40105),
|
||||
|
||||
/**
|
||||
* KVStorage create error.
|
||||
*/
|
||||
KVStorageCreateError(40106),
|
||||
|
||||
/**
|
||||
* KVStorage write error.
|
||||
*/
|
||||
KVStorageBatchWriteError(40107),
|
||||
|
||||
// disk error
|
||||
|
||||
/**
|
||||
* mkdir error.
|
||||
*/
|
||||
IOMakeDirError(40201),
|
||||
|
||||
/**
|
||||
* copy directory has error.
|
||||
*/
|
||||
IOCopyDirError(40202),
|
||||
|
||||
// consistency protocol error
|
||||
|
||||
/**
|
||||
* protocol write error.
|
||||
*/
|
||||
ProtoSubmitError(40301),
|
||||
|
||||
/**
|
||||
* protocol read error.
|
||||
*/
|
||||
ProtoReadError(40302);
|
||||
|
||||
private final int code;
|
||||
|
||||
ErrorCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.exception;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
|
||||
/**
|
||||
* RocksDB Exception.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
public class KvStorageException extends NacosException {
|
||||
|
||||
public KvStorageException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public KvStorageException(ErrorCode code, String errMsg) {
|
||||
super(code.getCode(), errMsg);
|
||||
}
|
||||
|
||||
public KvStorageException(ErrorCode errCode, Throwable throwable) {
|
||||
super(errCode.getCode(), throwable);
|
||||
}
|
||||
|
||||
public KvStorageException(ErrorCode errCode, String errMsg, Throwable throwable) {
|
||||
super(errCode.getCode(), errMsg, throwable);
|
||||
}
|
||||
|
||||
public KvStorageException(int errCode, String errMsg) {
|
||||
super(errCode, errMsg);
|
||||
}
|
||||
|
||||
public KvStorageException(int errCode, Throwable throwable) {
|
||||
super(errCode, throwable);
|
||||
}
|
||||
|
||||
public KvStorageException(int errCode, String errMsg, Throwable throwable) {
|
||||
super(errCode, errMsg, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getErrCode() {
|
||||
return super.getErrCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrMsg() {
|
||||
return super.getErrMsg();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrCode(int errCode) {
|
||||
super.setErrCode(errCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrMsg(String errMsg) {
|
||||
super.setErrMsg(errMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCauseThrowable(Throwable throwable) {
|
||||
super.setCauseThrowable(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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();
|
||||
case RocksDB:
|
||||
default:
|
||||
throw new IllegalArgumentException("this kv type : [" + type.name() + "] not support");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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() {
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 RocksStorageException
|
||||
*/
|
||||
Map<byte[], byte[]> batchGet(List<byte[]> keys) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* write data.
|
||||
*
|
||||
* @param key byte[]
|
||||
* @param value byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
*/
|
||||
void put(byte[] key, byte[] value) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* batch write.
|
||||
*
|
||||
* @param key List byte[]
|
||||
* @param values List byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
*/
|
||||
void batchPut(List<byte[]> key, List<byte[]> values) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* delete with key.
|
||||
*
|
||||
* @param key byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
*/
|
||||
void delete(byte[] key) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* batch delete with keys.
|
||||
*
|
||||
* @param key List byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
*/
|
||||
void batchDelete(List<byte[]> key) 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();
|
||||
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,10 +17,11 @@
|
||||
package com.alibaba.nacos.core.utils;
|
||||
|
||||
import com.alibaba.nacos.common.utils.LoggerUtils;
|
||||
import com.alibaba.nacos.common.utils.Pair;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
@ -33,47 +34,49 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public class TimerContext {
|
||||
|
||||
private static final ThreadLocal<Pair<String, Long>> TIME_RECORD = new ThreadLocal<>();
|
||||
private static final ThreadLocal<Map<String, Long>> TIME_RECORD = ThreadLocal.withInitial(() -> new HashMap<>(2));
|
||||
|
||||
/**
|
||||
* Record context start time.
|
||||
*
|
||||
* @param name context name
|
||||
*/
|
||||
public static void start(final String name) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
TIME_RECORD.set(Pair.with(name, startTime));
|
||||
TIME_RECORD.get().put(name, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static void end(final Logger logger) {
|
||||
end(logger, LoggerUtils.DEBUG);
|
||||
public static void end(final String name, final Logger logger) {
|
||||
end(name, logger, LoggerUtils.DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* End the task and print based on the log level.
|
||||
*
|
||||
* @param name context name
|
||||
* @param logger logger
|
||||
* @param level logger level
|
||||
*/
|
||||
public static void end(final Logger logger, final String level) {
|
||||
long endTime = System.currentTimeMillis();
|
||||
Pair<String, Long> record = TIME_RECORD.get();
|
||||
public static void end(final String name, final Logger logger, final String level) {
|
||||
Map<String, Long> record = TIME_RECORD.get();
|
||||
long contextTime = System.currentTimeMillis() - record.remove(name);
|
||||
if (StringUtils.equals(level, LoggerUtils.DEBUG)) {
|
||||
LoggerUtils.printIfDebugEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
||||
(endTime - record.getSecond()));
|
||||
LoggerUtils.printIfDebugEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||
}
|
||||
if (StringUtils.equals(level, LoggerUtils.INFO)) {
|
||||
LoggerUtils.printIfInfoEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
||||
(endTime - record.getSecond()));
|
||||
LoggerUtils.printIfInfoEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||
}
|
||||
if (StringUtils.equals(level, LoggerUtils.TRACE)) {
|
||||
LoggerUtils.printIfTraceEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
||||
(endTime - record.getSecond()));
|
||||
LoggerUtils.printIfTraceEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||
}
|
||||
if (StringUtils.equals(level, LoggerUtils.ERROR)) {
|
||||
LoggerUtils.printIfErrorEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
||||
(endTime - record.getSecond()));
|
||||
LoggerUtils.printIfErrorEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||
}
|
||||
if (StringUtils.equals(level, LoggerUtils.WARN)) {
|
||||
LoggerUtils.printIfWarnEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
||||
(endTime - record.getSecond()));
|
||||
LoggerUtils.printIfWarnEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||
}
|
||||
if (record.isEmpty()) {
|
||||
TIME_RECORD.remove();
|
||||
}
|
||||
TIME_RECORD.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,7 +91,7 @@ public class TimerContext {
|
||||
try {
|
||||
job.run();
|
||||
} finally {
|
||||
end(logger);
|
||||
end(name, logger);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +107,7 @@ public class TimerContext {
|
||||
try {
|
||||
return job.get();
|
||||
} finally {
|
||||
end(logger);
|
||||
end(name, logger);
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +124,7 @@ public class TimerContext {
|
||||
try {
|
||||
return job.apply(args);
|
||||
} finally {
|
||||
end(logger);
|
||||
end(name, logger);
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +141,7 @@ public class TimerContext {
|
||||
try {
|
||||
job.accept(args);
|
||||
} finally {
|
||||
end(logger);
|
||||
end(name, logger);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,26 @@ public class WebUtils {
|
||||
* @return Decoded data
|
||||
*/
|
||||
private static String resolveValue(String value, String encoding) {
|
||||
if (StringUtils.isEmpty(encoding)) {
|
||||
encoding = StandardCharsets.UTF_8.name();
|
||||
}
|
||||
try {
|
||||
value = new String(value.getBytes(StandardCharsets.UTF_8), encoding);
|
||||
} catch (UnsupportedEncodingException ignore) {
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* decode target value with UrlDecode.
|
||||
*
|
||||
* <p>Under Content-Type:application/x-www-form-urlencoded situation.
|
||||
*
|
||||
* @param value value
|
||||
* @param encoding encode
|
||||
* @return Decoded data
|
||||
*/
|
||||
private static String resolveValueWithUrlDecode(String value, String encoding) {
|
||||
if (StringUtils.isEmpty(encoding)) {
|
||||
encoding = StandardCharsets.UTF_8.name();
|
||||
}
|
||||
@ -114,7 +134,7 @@ public class WebUtils {
|
||||
*
|
||||
* @param request HttpServletRequest
|
||||
* @return the value of the request header "user-agent", or the value of the request header "client-version" if the
|
||||
* request does not have a header of "user-agent".
|
||||
* request does not have a header of "user-agent".
|
||||
*/
|
||||
public static String getUserAgent(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader(HttpHeaderConsts.USER_AGENT_HEADER);
|
||||
@ -144,8 +164,8 @@ public class WebUtils {
|
||||
* Handle file upload operations.
|
||||
*
|
||||
* @param multipartFile file
|
||||
* @param consumer post processor
|
||||
* @param response {@link DeferredResult}
|
||||
* @param consumer post processor
|
||||
* @param response {@link DeferredResult}
|
||||
*/
|
||||
public static void onFileUpload(MultipartFile multipartFile, Consumer<File> consumer,
|
||||
DeferredResult<RestResult<String>> response) {
|
||||
|
@ -22,7 +22,7 @@ rem removed the last 5 chars(which means \bin\) to get the base DIR.
|
||||
set BASE_DIR="%BASE_DIR:~0,-5%"
|
||||
|
||||
set DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/"
|
||||
set CUSTOM_SEARCH_LOCATIONS=%DEFAULT_SEARCH_LOCATIONS%,file:%BASE_DIR%/conf/
|
||||
set CUSTOM_SEARCH_LOCATIONS=file:%BASE_DIR%/conf/,%DEFAULT_SEARCH_LOCATIONS%
|
||||
|
||||
set MODE="cluster"
|
||||
set FUNCTION_MODE="all"
|
||||
@ -93,4 +93,4 @@ set "NACOS_LOG4J_OPTS=--logging.config=%BASE_DIR%/conf/nacos-logback.xml"
|
||||
set COMMAND="%JAVA%" %NACOS_JVM_OPTS% %NACOS_OPTS% %NACOS_CONFIG_OPTS% %NACOS_LOG4J_OPTS% nacos.nacos %*
|
||||
|
||||
rem start nacos command
|
||||
%COMMAND%
|
||||
%COMMAND%
|
||||
|
@ -79,7 +79,7 @@ export JAVA_HOME
|
||||
export JAVA="$JAVA_HOME/bin/java"
|
||||
export BASE_DIR=`cd $(dirname $0)/..; pwd`
|
||||
export DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/"
|
||||
export CUSTOM_SEARCH_LOCATIONS=${DEFAULT_SEARCH_LOCATIONS},file:${BASE_DIR}/conf/
|
||||
export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/,${DEFAULT_SEARCH_LOCATIONS}
|
||||
|
||||
#===========================================================================================
|
||||
# JVM Configuration
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.0-SNAPSHOT</version>
|
||||
<version>1.4.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -20,7 +20,6 @@ import com.alibaba.nacos.naming.consistency.ConsistencyService;
|
||||
import com.alibaba.nacos.naming.misc.GlobalExecutor;
|
||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@ -38,11 +37,14 @@ public class ServerStatusManager {
|
||||
@Resource(name = "consistencyDelegate")
|
||||
private ConsistencyService consistencyService;
|
||||
|
||||
@Autowired
|
||||
private SwitchDomain switchDomain;
|
||||
private final SwitchDomain switchDomain;
|
||||
|
||||
private ServerStatus serverStatus = ServerStatus.STARTING;
|
||||
|
||||
public ServerStatusManager(SwitchDomain switchDomain) {
|
||||
this.switchDomain = switchDomain;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
GlobalExecutor.registerServerStatusUpdater(new ServerStatusUpdater());
|
||||
|
@ -18,6 +18,7 @@ package com.alibaba.nacos.naming.consistency;
|
||||
|
||||
import com.alibaba.nacos.naming.pojo.Record;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@ -25,11 +26,28 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
*
|
||||
* @author nacos
|
||||
*/
|
||||
public class Datum<T extends Record> {
|
||||
public class Datum<T extends Record> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2525482315889753720L;
|
||||
|
||||
public String key;
|
||||
|
||||
public T value;
|
||||
|
||||
public AtomicLong timestamp = new AtomicLong(0L);
|
||||
|
||||
/**
|
||||
* Create datum.
|
||||
*
|
||||
* @param key key of datum
|
||||
* @param value value of datum
|
||||
* @param <T> Types of value
|
||||
* @return new datum
|
||||
*/
|
||||
public static <T extends Record> Datum createDatum(final String key, final T value) {
|
||||
Datum datum = new Datum();
|
||||
datum.key = key;
|
||||
datum.value = value;
|
||||
return datum;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package com.alibaba.nacos.naming.consistency;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.naming.consistency.ephemeral.EphemeralConsistencyService;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyServiceDelegateImpl;
|
||||
import com.alibaba.nacos.naming.pojo.Record;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -33,11 +33,11 @@ import org.springframework.stereotype.Service;
|
||||
@Service("consistencyDelegate")
|
||||
public class DelegateConsistencyServiceImpl implements ConsistencyService {
|
||||
|
||||
private final PersistentConsistencyService persistentConsistencyService;
|
||||
private final PersistentConsistencyServiceDelegateImpl persistentConsistencyService;
|
||||
|
||||
private final EphemeralConsistencyService ephemeralConsistencyService;
|
||||
|
||||
public DelegateConsistencyServiceImpl(PersistentConsistencyService persistentConsistencyService,
|
||||
public DelegateConsistencyServiceImpl(PersistentConsistencyServiceDelegateImpl persistentConsistencyService,
|
||||
EphemeralConsistencyService ephemeralConsistencyService) {
|
||||
this.persistentConsistencyService = persistentConsistencyService;
|
||||
this.ephemeralConsistencyService = ephemeralConsistencyService;
|
||||
|
@ -19,6 +19,8 @@ package com.alibaba.nacos.naming.consistency;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import static com.alibaba.nacos.naming.misc.UtilsAndCommons.RAFT_CACHE_FILE_PREFIX;
|
||||
|
||||
/**
|
||||
* Key operations for data.
|
||||
*
|
||||
@ -81,8 +83,7 @@ public class KeyBuilder {
|
||||
}
|
||||
|
||||
public static boolean matchSwitchKey(String key) {
|
||||
return key.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME) || key
|
||||
.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME + UtilsAndCommons.RAFT_CACHE_FILE_SUFFIX);
|
||||
return key.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME);
|
||||
}
|
||||
|
||||
public static boolean matchServiceName(String key, String namespaceId, String serviceName) {
|
||||
@ -141,4 +142,8 @@ public class KeyBuilder {
|
||||
public static String getServiceName(String key) {
|
||||
return key.split(NAMESPACE_KEY_CONNECTOR)[1];
|
||||
}
|
||||
|
||||
public static boolean isDatumCacheFile(String key) {
|
||||
return key.startsWith(RAFT_CACHE_FILE_PREFIX);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -351,7 +351,7 @@ public class DistroConsistencyServiceImpl implements EphemeralConsistencyService
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return distroProtocol.isLoadCompleted() || !globalConfig.isDataWarmup();
|
||||
return distroProtocol.isInitialized() || !globalConfig.isDataWarmup();
|
||||
}
|
||||
|
||||
public class Notifier implements Runnable {
|
||||
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.utils.StringUtils;
|
||||
import com.alibaba.nacos.common.utils.VersionUtils;
|
||||
import com.alibaba.nacos.core.cluster.Member;
|
||||
import com.alibaba.nacos.core.cluster.MemberMetaDataConstants;
|
||||
import com.alibaba.nacos.core.cluster.ServerMemberManager;
|
||||
import com.alibaba.nacos.naming.misc.GlobalExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* An automated task that determines whether all nodes in the current cluster meet the requirements of a particular
|
||||
* version.
|
||||
*
|
||||
* <p>This will be removed in a future release, just to smooth the transition.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
@Component
|
||||
public class ClusterVersionJudgement {
|
||||
|
||||
private volatile boolean allMemberIsNewVersion = false;
|
||||
|
||||
private final ServerMemberManager memberManager;
|
||||
|
||||
private final List<ConsumerWithPriority> observers = new CopyOnWriteArrayList<>();
|
||||
|
||||
public ClusterVersionJudgement(ServerMemberManager memberManager) {
|
||||
this.memberManager = memberManager;
|
||||
GlobalExecutor.submitClusterVersionJudge(this::runVersionListener, TimeUnit.SECONDS.toMillis(5));
|
||||
}
|
||||
|
||||
/**
|
||||
* register member version watcher.
|
||||
*
|
||||
* @param observer Listens for the latest version of all current nodes
|
||||
* @param priority The higher the priority, the first to be notified
|
||||
*/
|
||||
public void registerObserver(Consumer<Boolean> observer, int priority) {
|
||||
observers.add(new ConsumerWithPriority(observer, priority));
|
||||
}
|
||||
|
||||
protected void runVersionListener() {
|
||||
try {
|
||||
judge();
|
||||
} finally {
|
||||
GlobalExecutor.submitClusterVersionJudge(this::runVersionListener, TimeUnit.SECONDS.toMillis(5));
|
||||
}
|
||||
}
|
||||
|
||||
protected void judge() {
|
||||
Collection<Member> members = memberManager.allMembers();
|
||||
final String oldVersion = "1.4.0";
|
||||
boolean allMemberIsNewVersion = true;
|
||||
for (Member member : members) {
|
||||
final String curV = (String) member.getExtendVal(MemberMetaDataConstants.VERSION);
|
||||
if (StringUtils.isBlank(curV) || VersionUtils.compareVersion(oldVersion, curV) > 0) {
|
||||
allMemberIsNewVersion = false;
|
||||
}
|
||||
}
|
||||
// can only trigger once
|
||||
if (allMemberIsNewVersion && !this.allMemberIsNewVersion) {
|
||||
this.allMemberIsNewVersion = true;
|
||||
Collections.sort(observers);
|
||||
for (ConsumerWithPriority consumer : observers) {
|
||||
consumer.consumer.accept(true);
|
||||
}
|
||||
observers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean allMemberIsNewVersion() {
|
||||
return allMemberIsNewVersion;
|
||||
}
|
||||
|
||||
private static class ConsumerWithPriority implements Comparable<ConsumerWithPriority> {
|
||||
|
||||
private final Consumer<Boolean> consumer;
|
||||
|
||||
private final int priority;
|
||||
|
||||
public ConsumerWithPriority(Consumer<Boolean> consumer, int priority) {
|
||||
this.consumer = consumer;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ConsumerWithPriority o) {
|
||||
return o.priority - this.priority;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.naming.consistency.Datum;
|
||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.impl.PersistentServiceProcessor;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.raft.RaftConsistencyServiceImpl;
|
||||
import com.alibaba.nacos.naming.pojo.Record;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Persistent consistency service delegate.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
@Component("persistentConsistencyServiceDelegate")
|
||||
public class PersistentConsistencyServiceDelegateImpl implements PersistentConsistencyService {
|
||||
|
||||
private final ClusterVersionJudgement versionJudgement;
|
||||
|
||||
private final RaftConsistencyServiceImpl oldPersistentConsistencyService;
|
||||
|
||||
private final PersistentServiceProcessor newPersistentConsistencyService;
|
||||
|
||||
private volatile boolean switchNewPersistentService = false;
|
||||
|
||||
public PersistentConsistencyServiceDelegateImpl(ClusterVersionJudgement versionJudgement,
|
||||
RaftConsistencyServiceImpl oldPersistentConsistencyService,
|
||||
PersistentServiceProcessor newPersistentConsistencyService) {
|
||||
this.versionJudgement = versionJudgement;
|
||||
this.oldPersistentConsistencyService = oldPersistentConsistencyService;
|
||||
this.newPersistentConsistencyService = newPersistentConsistencyService;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.versionJudgement.registerObserver(isAllNewVersion -> switchNewPersistentService = isAllNewVersion, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Record value) throws NacosException {
|
||||
switchOne().put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String key) throws NacosException {
|
||||
switchOne().remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Datum get(String key) throws NacosException {
|
||||
return switchOne().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listen(String key, RecordListener listener) throws NacosException {
|
||||
oldPersistentConsistencyService.listen(key, listener);
|
||||
newPersistentConsistencyService.listen(key, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unListen(String key, RecordListener listener) throws NacosException {
|
||||
newPersistentConsistencyService.unListen(key, listener);
|
||||
oldPersistentConsistencyService.unListen(key, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return switchOne().isAvailable();
|
||||
}
|
||||
|
||||
private PersistentConsistencyService switchOne() {
|
||||
return switchNewPersistentService ? newPersistentConsistencyService : oldPersistentConsistencyService;
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.KeyBuilder;
|
||||
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 org.jboss.netty.util.internal.ConcurrentHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
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<>());
|
||||
listenerMap.get(key).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(KeyBuilder.SERVICE_META_KEY_PREFIX)) {
|
||||
if (KeyBuilder.matchServiceMetaKey(key) && !KeyBuilder.matchSwitchKey(key)) {
|
||||
for (RecordListener listener : listenerMap.get(KeyBuilder.SERVICE_META_KEY_PREFIX)) {
|
||||
try {
|
||||
if (action == DataOperation.CHANGE) {
|
||||
listener.onChange(key, value);
|
||||
}
|
||||
if (action == DataOperation.DELETE) {
|
||||
listener.onDelete(key);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Loggers.RAFT.error("[NACOS-RAFT] error while notifying listener of key: {}", key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Batch read response.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
public class BatchReadResponse implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5639813122389207205L;
|
||||
|
||||
private List<byte[]> keys = new ArrayList<>(16);
|
||||
|
||||
private List<byte[]> values = new ArrayList<>(16);
|
||||
|
||||
public List<byte[]> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public void setKeys(List<byte[]> keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public List<byte[]> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(List<byte[]> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public void append(byte[] key, byte[] value) {
|
||||
keys.add(key);
|
||||
values.add(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* batch write request.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
public class BatchWriteRequest implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5620748357962129879L;
|
||||
|
||||
private List<byte[]> keys = new ArrayList<>(16);
|
||||
|
||||
private List<byte[]> values = new ArrayList<>(16);
|
||||
|
||||
public List<byte[]> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public void setKeys(List<byte[]> keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public List<byte[]> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(List<byte[]> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public void append(byte[] key, byte[] value) {
|
||||
keys.add(key);
|
||||
values.add(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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.utils.StringUtils;
|
||||
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.core.utils.TimerContext;
|
||||
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
||||
import com.alibaba.nacos.naming.misc.Loggers;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Kv storage implementation for naming.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class NamingKvStorage extends MemoryKvStorage {
|
||||
|
||||
private static final String LOAD_SNAPSHOT = NamingKvStorage.class.getSimpleName() + ".snapshotLoad";
|
||||
|
||||
private final String baseDir;
|
||||
|
||||
private final KvStorage baseDirStorage;
|
||||
|
||||
private final Map<String, KvStorage> namespaceKvStorage;
|
||||
|
||||
public NamingKvStorage(final String baseDir) throws Exception {
|
||||
this.baseDir = baseDir;
|
||||
this.baseDirStorage = StorageFactory.createKvStorage(KvStorage.KvType.File, "naming-persistent", baseDir);
|
||||
this.namespaceKvStorage = new ConcurrentHashMap<>(16);
|
||||
}
|
||||
|
||||
@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 = createActualStorageIfAbsent(key);
|
||||
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), 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 = createActualStorageIfAbsent(key);
|
||||
storage.put(key, value);
|
||||
} catch (Exception e) {
|
||||
throw new KvStorageException(ErrorCode.KVStorageWriteError.getCode(),
|
||||
"Put data failed, key: " + new String(key), 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 = createActualStorageIfAbsent(key);
|
||||
if (null != storage) {
|
||||
storage.delete(key);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new KvStorageException(ErrorCode.KVStorageDeleteError.getCode(),
|
||||
"Delete data failed, key: " + new String(key), 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[]> key) throws KvStorageException {
|
||||
for (byte[] each : key) {
|
||||
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);
|
||||
loadNamespaceSnapshot();
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadNamespaceSnapshot() {
|
||||
for (String each : getAllNamespaceDirs()) {
|
||||
try {
|
||||
KvStorage kvStorage = createActualStorageIfAbsent(each);
|
||||
loadSnapshotFromActualStorage(kvStorage);
|
||||
} catch (Exception e) {
|
||||
Loggers.RAFT.error("load snapshot for namespace {} failed", each, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getAllNamespaceDirs() {
|
||||
File[] files = new File(baseDir).listFiles();
|
||||
List<String> result = Collections.emptyList();
|
||||
if (null != files) {
|
||||
result = new ArrayList<>(files.length);
|
||||
for (File each : files) {
|
||||
if (each.isDirectory()) {
|
||||
result.add(each.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<byte[]> allKeys() throws KvStorageException {
|
||||
return super.allKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
baseDirStorage.shutdown();
|
||||
for (KvStorage each : namespaceKvStorage.values()) {
|
||||
each.shutdown();
|
||||
}
|
||||
namespaceKvStorage.clear();
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
private KvStorage createActualStorageIfAbsent(byte[] key) throws Exception {
|
||||
String keyString = new String(key);
|
||||
String namespace = KeyBuilder.getNamespace(keyString);
|
||||
return createActualStorageIfAbsent(namespace);
|
||||
}
|
||||
|
||||
private KvStorage createActualStorageIfAbsent(String namespace) throws Exception {
|
||||
if (StringUtils.isBlank(namespace)) {
|
||||
return baseDirStorage;
|
||||
}
|
||||
|
||||
Function<String, KvStorage> kvStorageBuilder = key -> {
|
||||
try {
|
||||
String namespacePath = Paths.get(baseDir, key).toString();
|
||||
return StorageFactory.createKvStorage(KvType.File, "naming-persistent", namespacePath);
|
||||
} catch (Exception e) {
|
||||
throw new NacosRuntimeException(NacosException.SERVER_ERROR, e);
|
||||
}
|
||||
};
|
||||
namespaceKvStorage.computeIfAbsent(namespace, kvStorageBuilder);
|
||||
return namespaceKvStorage.get(namespace);
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.common.utils.Objects;
|
||||
import com.alibaba.nacos.consistency.snapshot.LocalFileMeta;
|
||||
import com.alibaba.nacos.consistency.snapshot.Reader;
|
||||
import com.alibaba.nacos.consistency.snapshot.SnapshotOperation;
|
||||
import com.alibaba.nacos.consistency.snapshot.Writer;
|
||||
import com.alibaba.nacos.core.distributed.raft.utils.RaftExecutor;
|
||||
import com.alibaba.nacos.core.storage.kv.KvStorage;
|
||||
import com.alibaba.nacos.core.utils.TimerContext;
|
||||
import com.alibaba.nacos.naming.misc.Loggers;
|
||||
import com.alibaba.nacos.sys.utils.DiskUtils;
|
||||
import com.alipay.sofa.jraft.util.CRC64;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
/**
|
||||
* Snapshot processing of persistent service data for accelerated Raft protocol recovery and data synchronization.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
public class NamingSnapshotOperation implements SnapshotOperation {
|
||||
|
||||
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 snapshotArchive = "naming_persistent.zip";
|
||||
|
||||
private final String checkSumKey = "checkSum";
|
||||
|
||||
private final KvStorage storage;
|
||||
|
||||
private final ReentrantReadWriteLock.WriteLock writeLock;
|
||||
|
||||
public NamingSnapshotOperation(KvStorage storage, ReentrantReadWriteLock lock) {
|
||||
this.storage = storage;
|
||||
this.writeLock = lock.writeLock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnapshotSave(Writer writer, BiConsumer<Boolean, Throwable> callFinally) {
|
||||
RaftExecutor.doSnapshot(() -> {
|
||||
TimerContext.start(NAMING_SNAPSHOT_SAVE);
|
||||
|
||||
final Lock lock = writeLock;
|
||||
lock.lock();
|
||||
try {
|
||||
final String writePath = writer.getPath();
|
||||
final String parentPath = Paths.get(writePath, snapshotDir).toString();
|
||||
DiskUtils.deleteDirectory(parentPath);
|
||||
DiskUtils.forceMkdir(parentPath);
|
||||
|
||||
storage.doSnapshot(parentPath);
|
||||
final String outputFile = Paths.get(writePath, snapshotArchive).toString();
|
||||
final Checksum checksum = new CRC64();
|
||||
DiskUtils.compress(writePath, snapshotDir, outputFile, checksum);
|
||||
DiskUtils.deleteDirectory(parentPath);
|
||||
|
||||
final LocalFileMeta meta = new LocalFileMeta();
|
||||
meta.append(checkSumKey, Long.toHexString(checksum.getValue()));
|
||||
|
||||
callFinally.accept(writer.addFile(snapshotArchive, meta), null);
|
||||
} catch (Throwable t) {
|
||||
Loggers.RAFT.error("Fail to compress snapshot, path={}, file list={}, {}.", writer.getPath(),
|
||||
writer.listFiles(), t);
|
||||
callFinally.accept(false, t);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
TimerContext.end(NAMING_SNAPSHOT_SAVE, Loggers.RAFT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSnapshotLoad(Reader reader) {
|
||||
final String readerPath = reader.getPath();
|
||||
final String sourceFile = Paths.get(readerPath, snapshotArchive).toString();
|
||||
|
||||
TimerContext.start(NAMING_SNAPSHOT_LOAD);
|
||||
final Lock lock = writeLock;
|
||||
lock.lock();
|
||||
try {
|
||||
final Checksum checksum = new CRC64();
|
||||
DiskUtils.decompress(sourceFile, readerPath, checksum);
|
||||
LocalFileMeta fileMeta = reader.getFileMeta(snapshotArchive);
|
||||
if (fileMeta.getFileMeta().containsKey(checkSumKey)) {
|
||||
if (!Objects.equals(Long.toHexString(checksum.getValue()), fileMeta.get(checkSumKey))) {
|
||||
throw new IllegalArgumentException("Snapshot checksum failed");
|
||||
}
|
||||
}
|
||||
|
||||
final String loadPath = Paths.get(readerPath, snapshotDir).toString();
|
||||
storage.snapshotLoad(loadPath);
|
||||
Loggers.RAFT.info("snapshot load from : {}", loadPath);
|
||||
DiskUtils.deleteDirectory(loadPath);
|
||||
return true;
|
||||
} catch (final Throwable t) {
|
||||
Loggers.RAFT.error("Fail to load snapshot, path={}, file list={}, {}.",
|
||||
Paths.get(readerPath, snapshotDir).toString(), reader.listFiles(), t);
|
||||
return false;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
TimerContext.end(NAMING_SNAPSHOT_LOAD, Loggers.RAFT);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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.StringUtils;
|
||||
import com.alibaba.nacos.consistency.DataOperation;
|
||||
import com.alibaba.nacos.consistency.SerializeFactory;
|
||||
import com.alibaba.nacos.consistency.Serializer;
|
||||
import com.alibaba.nacos.consistency.cp.CPProtocol;
|
||||
import com.alibaba.nacos.consistency.cp.LogProcessor4CP;
|
||||
import com.alibaba.nacos.consistency.cp.MetadataKey;
|
||||
import com.alibaba.nacos.consistency.entity.GetRequest;
|
||||
import com.alibaba.nacos.consistency.entity.Log;
|
||||
import com.alibaba.nacos.consistency.entity.Response;
|
||||
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.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.ClusterVersionJudgement;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentNotifier;
|
||||
import com.alibaba.nacos.naming.misc.Loggers;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import com.alibaba.nacos.naming.pojo.Record;
|
||||
import com.alibaba.nacos.naming.utils.Constants;
|
||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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>
|
||||
*/
|
||||
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
|
||||
@Service
|
||||
public class PersistentServiceProcessor extends LogProcessor4CP implements PersistentConsistencyService {
|
||||
|
||||
enum Op {
|
||||
/**
|
||||
* write ops.
|
||||
*/
|
||||
Write("Write"),
|
||||
|
||||
/**
|
||||
* read ops.
|
||||
*/
|
||||
Read("Read"),
|
||||
|
||||
/**
|
||||
* delete ops.
|
||||
*/
|
||||
Delete("Delete");
|
||||
|
||||
private final String desc;
|
||||
|
||||
Op(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
}
|
||||
|
||||
private final CPProtocol protocol;
|
||||
|
||||
private final KvStorage kvStorage;
|
||||
|
||||
private final ClusterVersionJudgement versionJudgement;
|
||||
|
||||
private final Serializer serializer;
|
||||
|
||||
/**
|
||||
* During snapshot processing, the processing of other requests needs to be paused.
|
||||
*/
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
|
||||
|
||||
private final PersistentNotifier notifier;
|
||||
|
||||
/**
|
||||
* Is there a leader node currently.
|
||||
*/
|
||||
private volatile boolean hasLeader = false;
|
||||
|
||||
/**
|
||||
* Whether an unrecoverable error occurred.
|
||||
*/
|
||||
private volatile boolean hasError = false;
|
||||
|
||||
/**
|
||||
* If use old raft, should not notify listener even new listener add.
|
||||
*/
|
||||
private volatile boolean startNotify = false;
|
||||
|
||||
public PersistentServiceProcessor(final ProtocolManager protocolManager,
|
||||
final ClusterVersionJudgement versionJudgement) throws Exception {
|
||||
this.protocol = protocolManager.getCpProtocol();
|
||||
this.versionJudgement = versionJudgement;
|
||||
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());
|
||||
}
|
||||
});
|
||||
init();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void init() {
|
||||
NotifyCenter.registerToPublisher(ValueChangeEvent.class, 16384);
|
||||
this.protocol.addLogProcessors(Collections.singletonList(this));
|
||||
this.protocol.protocolMetaData()
|
||||
.subscribe(Constants.NAMING_PERSISTENT_SERVICE_GROUP, MetadataKey.LEADER_META_DATA,
|
||||
(o, arg) -> hasLeader = StringUtils.isNotBlank(String.valueOf(arg)));
|
||||
// If you choose to use the new RAFT protocol directly, there will be no compatible logical execution
|
||||
if (ApplicationUtils.getProperty(Constants.NACOS_NAMING_USE_NEW_RAFT_FIRST, Boolean.class, false)) {
|
||||
NotifyCenter.registerSubscriber(notifier);
|
||||
waitLeader();
|
||||
startNotify = true;
|
||||
} else {
|
||||
this.versionJudgement.registerObserver(isNewVersion -> {
|
||||
if (isNewVersion) {
|
||||
NotifyCenter.registerSubscriber(notifier);
|
||||
startNotify = true;
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
private void waitLeader() {
|
||||
while (!hasLeader && !hasError) {
|
||||
Loggers.RAFT.info("Waiting Jraft leader vote ...");
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response onRequest(GetRequest request) {
|
||||
final List<byte[]> keys = serializer
|
||||
.deserialize(request.getData().toByteArray(), TypeUtils.parameterize(List.class, byte[].class));
|
||||
final Lock lock = readLock;
|
||||
lock.lock();
|
||||
try {
|
||||
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();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response onApply(Log log) {
|
||||
final byte[] data = log.getData().toByteArray();
|
||||
final BatchWriteRequest request = serializer.deserialize(data, BatchWriteRequest.class);
|
||||
final Op op = Op.valueOf(log.getOperation());
|
||||
final Lock lock = readLock;
|
||||
lock.lock();
|
||||
try {
|
||||
switch (op) {
|
||||
case Write:
|
||||
kvStorage.batchPut(request.getKeys(), request.getValues());
|
||||
break;
|
||||
case Delete:
|
||||
kvStorage.batchDelete(request.getKeys());
|
||||
break;
|
||||
default:
|
||||
return Response.newBuilder().setSuccess(false).setErrMsg("unsupport operation : " + op).build();
|
||||
}
|
||||
publishValueChangeEvent(op, request);
|
||||
return Response.newBuilder().setSuccess(true).build();
|
||||
} catch (KvStorageException e) {
|
||||
return Response.newBuilder().setSuccess(false).setErrMsg(e.getErrMsg()).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));
|
||||
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));
|
||||
}
|
||||
|
||||
@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 Log log = Log.newBuilder().setData(ByteString.copyFrom(serializer.serialize(req)))
|
||||
.setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP).setOperation(Op.Write.desc).build();
|
||||
try {
|
||||
protocol.submit(log);
|
||||
} 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 Log log = Log.newBuilder().setData(ByteString.copyFrom(serializer.serialize(req)))
|
||||
.setGroup(Constants.NAMING_PERSISTENT_SERVICE_GROUP).setOperation(Op.Delete.desc).build();
|
||||
try {
|
||||
protocol.submit(log);
|
||||
} 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 GetRequest req = GetRequest.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 void onError(Throwable error) {
|
||||
super.onError(error);
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return hasLeader && !hasError;
|
||||
}
|
||||
|
||||
private Type getDatumTypeFromKey(String key) {
|
||||
return TypeUtils.parameterize(Datum.class, getClassOfRecordFromKey(key));
|
||||
}
|
||||
|
||||
private Class<? extends Record> getClassOfRecordFromKey(String key) {
|
||||
if (KeyBuilder.matchSwitchKey(key)) {
|
||||
return com.alibaba.nacos.naming.misc.SwitchDomain.class;
|
||||
} else if (KeyBuilder.matchServiceMetaKey(key)) {
|
||||
return com.alibaba.nacos.naming.core.Service.class;
|
||||
} else if (KeyBuilder.matchInstanceListKey(key)) {
|
||||
return com.alibaba.nacos.naming.core.Instances.class;
|
||||
}
|
||||
return Record.class;
|
||||
}
|
||||
|
||||
private 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. See {@link com.alibaba.nacos.naming.core.ServiceManager#init()}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,9 +21,11 @@ import org.springframework.context.ApplicationEvent;
|
||||
/**
|
||||
* Base raft event.
|
||||
*
|
||||
* @deprecated will remove in 1.4.x
|
||||
* @author pbting
|
||||
* @date 2019-07-01 8:46 PM
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class BaseRaftEvent extends ApplicationEvent {
|
||||
|
||||
private final RaftPeer raftPeer;
|
||||
|
@ -19,9 +19,11 @@ package com.alibaba.nacos.naming.consistency.persistent.raft;
|
||||
/**
|
||||
* Leader election finished event.
|
||||
*
|
||||
* @deprecated will remove in 1.4.x
|
||||
* @author pbting
|
||||
* @date 2019-07-01 8:25 PM
|
||||
*/
|
||||
@Deprecated
|
||||
public class LeaderElectFinishedEvent extends BaseRaftEvent {
|
||||
|
||||
public LeaderElectFinishedEvent(Object source, RaftPeer raftPeer, RaftPeer local) {
|
||||
|
@ -19,9 +19,11 @@ package com.alibaba.nacos.naming.consistency.persistent.raft;
|
||||
/**
|
||||
* Make leader event.
|
||||
*
|
||||
* @deprecated will remove in 1.4.x
|
||||
* @author pbting
|
||||
* @date 2019-07-01 8:45 PM
|
||||
*/
|
||||
@Deprecated
|
||||
public class MakeLeaderEvent extends BaseRaftEvent {
|
||||
|
||||
public MakeLeaderEvent(Object source, RaftPeer raftPeer, RaftPeer local) {
|
||||
|
@ -17,39 +17,70 @@
|
||||
package com.alibaba.nacos.naming.consistency.persistent.raft;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.naming.cluster.ServerStatus;
|
||||
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.persistent.ClusterVersionJudgement;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
|
||||
import com.alibaba.nacos.naming.misc.Loggers;
|
||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import com.alibaba.nacos.naming.pojo.Record;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.alibaba.nacos.naming.utils.Constants;
|
||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* Use simplified Raft protocol to maintain the consistency status of Nacos cluster.
|
||||
*
|
||||
* @author nkorange
|
||||
* @since 1.0.0
|
||||
* @deprecated will remove in 1.4.x
|
||||
*/
|
||||
@Deprecated
|
||||
@DependsOn("ProtocolManager")
|
||||
@Service
|
||||
public class RaftConsistencyServiceImpl implements PersistentConsistencyService {
|
||||
|
||||
@Autowired
|
||||
private RaftCore raftCore;
|
||||
private final RaftCore raftCore;
|
||||
|
||||
@Autowired
|
||||
private RaftPeerSet peers;
|
||||
private final RaftPeerSet peers;
|
||||
|
||||
@Autowired
|
||||
private SwitchDomain switchDomain;
|
||||
private final SwitchDomain switchDomain;
|
||||
|
||||
private volatile boolean stopWork = false;
|
||||
|
||||
public RaftConsistencyServiceImpl(ClusterVersionJudgement versionJudgement, RaftCore raftCore,
|
||||
SwitchDomain switchDomain) {
|
||||
this.raftCore = raftCore;
|
||||
this.peers = raftCore.getPeerSet();
|
||||
this.switchDomain = switchDomain;
|
||||
versionJudgement.registerObserver(isAllNewVersion -> {
|
||||
stopWork = isAllNewVersion;
|
||||
if (stopWork) {
|
||||
try {
|
||||
this.raftCore.shutdown();
|
||||
} catch (NacosException e) {
|
||||
throw new NacosRuntimeException(NacosException.SERVER_ERROR, e);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
protected void init() throws Exception {
|
||||
if (ApplicationUtils.getProperty(Constants.NACOS_NAMING_USE_NEW_RAFT_FIRST, Boolean.class, false)) {
|
||||
this.raftCore.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String key, Record value) throws NacosException {
|
||||
checkIsStopWork();
|
||||
try {
|
||||
raftCore.signalPublish(key, value);
|
||||
} catch (Exception e) {
|
||||
@ -61,16 +92,14 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
|
||||
|
||||
@Override
|
||||
public void remove(String key) throws NacosException {
|
||||
checkIsStopWork();
|
||||
try {
|
||||
if (KeyBuilder.matchInstanceListKey(key) && !raftCore.isLeader()) {
|
||||
Datum datum = new Datum();
|
||||
datum.key = key;
|
||||
raftCore.onDelete(datum.key, peers.getLeader());
|
||||
raftCore.unlistenAll(key);
|
||||
return;
|
||||
raftCore.onDelete(key, peers.getLeader());
|
||||
} else {
|
||||
raftCore.signalDelete(key);
|
||||
}
|
||||
raftCore.signalDelete(key);
|
||||
raftCore.unlistenAll(key);
|
||||
raftCore.unListenAll(key);
|
||||
} catch (Exception e) {
|
||||
Loggers.RAFT.error("Raft remove failed.", e);
|
||||
throw new NacosException(NacosException.SERVER_ERROR, "Raft remove failed, key:" + key, e);
|
||||
@ -79,6 +108,7 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
|
||||
|
||||
@Override
|
||||
public Datum get(String key) throws NacosException {
|
||||
checkIsStopWork();
|
||||
return raftCore.getDatum(key);
|
||||
}
|
||||
|
||||
@ -130,4 +160,10 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
|
||||
"Raft onRemove failed, datum:" + datum + ", source: " + source, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIsStopWork() {
|
||||
if (stopWork) {
|
||||
throw new IllegalStateException("old raft protocol already stop work");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,18 +16,22 @@
|
||||
|
||||
package com.alibaba.nacos.naming.consistency.persistent.raft;
|
||||
|
||||
import com.alibaba.nacos.common.executor.ExecutorFactory;
|
||||
import com.alibaba.nacos.common.executor.NameThreadFactory;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.common.http.Callback;
|
||||
import com.alibaba.nacos.common.lifecycle.Closeable;
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
import com.alibaba.nacos.common.notify.EventPublisher;
|
||||
import com.alibaba.nacos.common.notify.NotifyCenter;
|
||||
import com.alibaba.nacos.common.utils.ConcurrentHashSet;
|
||||
import com.alibaba.nacos.common.utils.JacksonUtils;
|
||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||
import com.alibaba.nacos.consistency.DataOperation;
|
||||
import com.alibaba.nacos.core.utils.ClassUtils;
|
||||
import com.alibaba.nacos.naming.NamingApp;
|
||||
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.ClusterVersionJudgement;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentNotifier;
|
||||
import com.alibaba.nacos.naming.core.Instances;
|
||||
import com.alibaba.nacos.naming.core.Service;
|
||||
import com.alibaba.nacos.naming.misc.GlobalConfig;
|
||||
@ -39,14 +43,13 @@ import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||
import com.alibaba.nacos.naming.monitor.MetricsMonitor;
|
||||
import com.alibaba.nacos.naming.pojo.Record;
|
||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.javatuples.Pair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -63,13 +66,10 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@ -79,10 +79,12 @@ import java.util.zip.GZIPOutputStream;
|
||||
* Raft core code.
|
||||
*
|
||||
* @author nacos
|
||||
* @deprecated will remove in 1.4.x
|
||||
*/
|
||||
@Deprecated
|
||||
@DependsOn("ProtocolManager")
|
||||
@Component
|
||||
public class RaftCore {
|
||||
public class RaftCore implements Closeable {
|
||||
|
||||
public static final String API_VOTE = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/vote";
|
||||
|
||||
@ -100,10 +102,6 @@ public class RaftCore {
|
||||
|
||||
public static final String API_GET_PEER = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/peer";
|
||||
|
||||
private final ScheduledExecutorService executor = ExecutorFactory.Managed
|
||||
.newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class),
|
||||
new NameThreadFactory("com.alibaba.nacos.naming.raft.notifier"));
|
||||
|
||||
public static final Lock OPERATE_LOCK = new ReentrantLock();
|
||||
|
||||
public static final int PUBLISH_TERM_INCREASE_COUNT = 100;
|
||||
@ -112,25 +110,42 @@ public class RaftCore {
|
||||
|
||||
private volatile ConcurrentMap<String, Datum> datums = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private RaftPeerSet peers;
|
||||
|
||||
@Autowired
|
||||
private SwitchDomain switchDomain;
|
||||
private final SwitchDomain switchDomain;
|
||||
|
||||
@Autowired
|
||||
private GlobalConfig globalConfig;
|
||||
private final GlobalConfig globalConfig;
|
||||
|
||||
@Autowired
|
||||
private RaftProxy raftProxy;
|
||||
private final RaftProxy raftProxy;
|
||||
|
||||
@Autowired
|
||||
private RaftStore raftStore;
|
||||
private final RaftStore raftStore;
|
||||
|
||||
public volatile Notifier notifier = new Notifier();
|
||||
private final ClusterVersionJudgement versionJudgement;
|
||||
|
||||
public final PersistentNotifier notifier;
|
||||
|
||||
private final EventPublisher publisher;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
private volatile boolean stopWork = false;
|
||||
|
||||
private ScheduledFuture masterTask = null;
|
||||
|
||||
private ScheduledFuture heartbeatTask = null;
|
||||
|
||||
public RaftCore(RaftPeerSet peers, SwitchDomain switchDomain, GlobalConfig globalConfig, RaftProxy raftProxy,
|
||||
RaftStore raftStore, ClusterVersionJudgement versionJudgement) {
|
||||
this.peers = peers;
|
||||
this.switchDomain = switchDomain;
|
||||
this.globalConfig = globalConfig;
|
||||
this.raftProxy = raftProxy;
|
||||
this.raftStore = raftStore;
|
||||
this.versionJudgement = versionJudgement;
|
||||
this.notifier = new PersistentNotifier(key -> null == getDatum(key) ? null : getDatum(key).value);
|
||||
this.publisher = NotifyCenter.registerToPublisher(ValueChangeEvent.class, 16384);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init raft core.
|
||||
*
|
||||
@ -138,11 +153,7 @@ public class RaftCore {
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() throws Exception {
|
||||
|
||||
Loggers.RAFT.info("initializing Raft sub-system");
|
||||
|
||||
executor.submit(notifier);
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
|
||||
raftStore.loadDatums(notifier, datums);
|
||||
@ -151,26 +162,32 @@ public class RaftCore {
|
||||
|
||||
Loggers.RAFT.info("cache loaded, datum count: {}, current term: {}", datums.size(), peers.getTerm());
|
||||
|
||||
while (true) {
|
||||
if (notifier.tasks.size() <= 0) {
|
||||
break;
|
||||
}
|
||||
Thread.sleep(1000L);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
Loggers.RAFT.info("finish to load data from disk, cost: {} ms.", (System.currentTimeMillis() - start));
|
||||
|
||||
GlobalExecutor.registerMasterElection(new MasterElection());
|
||||
GlobalExecutor.registerHeartbeat(new HeartBeat());
|
||||
masterTask = GlobalExecutor.registerMasterElection(new MasterElection());
|
||||
heartbeatTask = GlobalExecutor.registerHeartbeat(new HeartBeat());
|
||||
|
||||
versionJudgement.registerObserver(isAllNewVersion -> {
|
||||
stopWork = isAllNewVersion;
|
||||
if (stopWork) {
|
||||
try {
|
||||
shutdown();
|
||||
} catch (NacosException e) {
|
||||
throw new NacosRuntimeException(NacosException.SERVER_ERROR, e);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
|
||||
NotifyCenter.registerSubscriber(notifier);
|
||||
|
||||
Loggers.RAFT.info("timer started: leader timeout ms: {}, heart-beat timeout ms: {}",
|
||||
GlobalExecutor.LEADER_TIMEOUT_MS, GlobalExecutor.HEARTBEAT_INTERVAL_MS);
|
||||
}
|
||||
|
||||
public Map<String, List<RecordListener>> getListeners() {
|
||||
return listeners;
|
||||
public Map<String, ConcurrentHashSet<RecordListener>> getListeners() {
|
||||
return notifier.getListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,7 +198,9 @@ public class RaftCore {
|
||||
* @throws Exception any exception during publish
|
||||
*/
|
||||
public void signalPublish(String key, Record value) throws Exception {
|
||||
|
||||
if (stopWork) {
|
||||
throw new IllegalStateException("old raft protocol already stop work");
|
||||
}
|
||||
if (!isLeader()) {
|
||||
ObjectNode params = JacksonUtils.createEmptyJsonNode();
|
||||
params.put("key", key);
|
||||
@ -195,8 +214,8 @@ public class RaftCore {
|
||||
return;
|
||||
}
|
||||
|
||||
OPERATE_LOCK.lock();
|
||||
try {
|
||||
OPERATE_LOCK.lock();
|
||||
final long start = System.currentTimeMillis();
|
||||
final Datum datum = new Datum();
|
||||
datum.key = key;
|
||||
@ -267,7 +286,9 @@ public class RaftCore {
|
||||
* @throws Exception any exception during delete
|
||||
*/
|
||||
public void signalDelete(final String key) throws Exception {
|
||||
|
||||
if (stopWork) {
|
||||
throw new IllegalStateException("old raft protocol already stop work");
|
||||
}
|
||||
OPERATE_LOCK.lock();
|
||||
try {
|
||||
|
||||
@ -328,6 +349,9 @@ public class RaftCore {
|
||||
* @throws Exception any exception during publish
|
||||
*/
|
||||
public void onPublish(Datum datum, RaftPeer source) throws Exception {
|
||||
if (stopWork) {
|
||||
throw new IllegalStateException("old raft protocol already stop work");
|
||||
}
|
||||
RaftPeer local = peers.local();
|
||||
if (datum.value == null) {
|
||||
Loggers.RAFT.warn("received empty datum");
|
||||
@ -369,9 +393,7 @@ public class RaftCore {
|
||||
}
|
||||
}
|
||||
raftStore.updateTerm(local.term.get());
|
||||
|
||||
notifier.addTask(datum.key, DataOperation.CHANGE);
|
||||
|
||||
NotifyCenter.publishEvent(ValueChangeEvent.builder().key(datum.key).action(DataOperation.CHANGE).build());
|
||||
Loggers.RAFT.info("data added/updated, key={}, term={}", datum.key, local.term);
|
||||
}
|
||||
|
||||
@ -383,7 +405,9 @@ public class RaftCore {
|
||||
* @throws Exception any exception during delete
|
||||
*/
|
||||
public void onDelete(String datumKey, RaftPeer source) throws Exception {
|
||||
|
||||
if (stopWork) {
|
||||
throw new IllegalStateException("old raft protocol already stop work");
|
||||
}
|
||||
RaftPeer local = peers.local();
|
||||
|
||||
if (!peers.isLeader(source.ip)) {
|
||||
@ -423,12 +447,30 @@ public class RaftCore {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws NacosException {
|
||||
this.stopWork = true;
|
||||
this.raftStore.shutdown();
|
||||
this.peers.shutdown();
|
||||
Loggers.RAFT.warn("start to close old raft protocol!!!");
|
||||
Loggers.RAFT.warn("stop old raft protocol task for notifier");
|
||||
NotifyCenter.deregisterSubscriber(notifier);
|
||||
Loggers.RAFT.warn("stop old raft protocol task for master task");
|
||||
masterTask.cancel(true);
|
||||
Loggers.RAFT.warn("stop old raft protocol task for heartbeat task");
|
||||
heartbeatTask.cancel(true);
|
||||
Loggers.RAFT.warn("clean old cache datum for old raft");
|
||||
datums.clear();
|
||||
}
|
||||
|
||||
public class MasterElection implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
if (stopWork) {
|
||||
return;
|
||||
}
|
||||
if (!peers.isReady()) {
|
||||
return;
|
||||
}
|
||||
@ -508,6 +550,9 @@ public class RaftCore {
|
||||
* @return self-peer information
|
||||
*/
|
||||
public synchronized RaftPeer receivedVote(RaftPeer remote) {
|
||||
if (stopWork) {
|
||||
throw new IllegalStateException("old raft protocol already stop work");
|
||||
}
|
||||
if (!peers.contains(remote)) {
|
||||
throw new IllegalStateException("can not find peer: " + remote.ip);
|
||||
}
|
||||
@ -540,7 +585,9 @@ public class RaftCore {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
if (stopWork) {
|
||||
return;
|
||||
}
|
||||
if (!peers.isReady()) {
|
||||
return;
|
||||
}
|
||||
@ -578,7 +625,7 @@ public class RaftCore {
|
||||
ArrayNode array = JacksonUtils.createEmptyArrayNode();
|
||||
|
||||
if (switchDomain.isSendBeatOnly()) {
|
||||
Loggers.RAFT.info("[SEND-BEAT-ONLY] {}", String.valueOf(switchDomain.isSendBeatOnly()));
|
||||
Loggers.RAFT.info("[SEND-BEAT-ONLY] {}", switchDomain.isSendBeatOnly());
|
||||
}
|
||||
|
||||
if (!switchDomain.isSendBeatOnly()) {
|
||||
@ -667,6 +714,9 @@ public class RaftCore {
|
||||
* @throws Exception any exception during handle
|
||||
*/
|
||||
public RaftPeer receivedBeat(JsonNode beat) throws Exception {
|
||||
if (stopWork) {
|
||||
throw new IllegalStateException("old raft protocol already stop work");
|
||||
}
|
||||
final RaftPeer local = peers.local();
|
||||
final RaftPeer remote = new RaftPeer();
|
||||
JsonNode peer = beat.get("peer");
|
||||
@ -767,8 +817,10 @@ public class RaftCore {
|
||||
processedCount, beatDatums.size(), datums.size());
|
||||
|
||||
// update datum entry
|
||||
String url = buildUrl(remote.ip, API_GET) + "?keys=" + URLEncoder.encode(keys, "UTF-8");
|
||||
HttpClient.asyncHttpGet(url, null, null, new Callback<String>() {
|
||||
String url = buildUrl(remote.ip, API_GET);
|
||||
Map<String, String> queryParam = new HashMap<>(1);
|
||||
queryParam.put("keys", URLEncoder.encode(keys, "UTF-8"));
|
||||
HttpClient.asyncHttpGet(url, null, queryParam, new Callback<String>() {
|
||||
@Override
|
||||
public void onReceive(RestResult<String> result) {
|
||||
if (!result.ok()) {
|
||||
@ -821,7 +873,7 @@ public class RaftCore {
|
||||
raftStore.write(newDatum);
|
||||
|
||||
datums.put(newDatum.key, newDatum);
|
||||
notifier.addTask(newDatum.key, DataOperation.CHANGE);
|
||||
notifier.notify(newDatum.key, DataOperation.CHANGE, newDatum.value);
|
||||
|
||||
local.resetLeaderDue();
|
||||
|
||||
@ -900,21 +952,9 @@ public class RaftCore {
|
||||
* @param listener new listener
|
||||
*/
|
||||
public void listen(String key, RecordListener listener) {
|
||||
|
||||
List<RecordListener> listenerList = listeners.get(key);
|
||||
if (listenerList != null && listenerList.contains(listener)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (listenerList == null) {
|
||||
listenerList = new CopyOnWriteArrayList<>();
|
||||
listeners.put(key, listenerList);
|
||||
}
|
||||
notifier.registerListener(key, listener);
|
||||
|
||||
Loggers.RAFT.info("add listener: {}", key);
|
||||
|
||||
listenerList.add(listener);
|
||||
|
||||
// if data present, notify immediately
|
||||
for (Datum datum : datums.values()) {
|
||||
if (!listener.interests(datum.key)) {
|
||||
@ -936,22 +976,11 @@ public class RaftCore {
|
||||
* @param listener listener
|
||||
*/
|
||||
public void unListen(String key, RecordListener listener) {
|
||||
|
||||
if (!listeners.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RecordListener dl : listeners.get(key)) {
|
||||
// TODO maybe use equal:
|
||||
if (dl == listener) {
|
||||
listeners.get(key).remove(listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
notifier.deregisterListener(key, listener);
|
||||
}
|
||||
|
||||
public void unlistenAll(String key) {
|
||||
listeners.remove(key);
|
||||
public void unListenAll(String key) {
|
||||
notifier.deregisterAllListener(key);
|
||||
}
|
||||
|
||||
public void setTerm(long term) {
|
||||
@ -1006,7 +1035,7 @@ public class RaftCore {
|
||||
|
||||
public void addDatum(Datum datum) {
|
||||
datums.put(datum.key, datum);
|
||||
notifier.addTask(datum.key, DataOperation.CHANGE);
|
||||
NotifyCenter.publishEvent(ValueChangeEvent.builder().key(datum.key).action(DataOperation.CHANGE).build());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1035,7 +1064,9 @@ public class RaftCore {
|
||||
raftStore.delete(deleted);
|
||||
Loggers.RAFT.info("datum deleted, key: {}", key);
|
||||
}
|
||||
notifier.addTask(URLDecoder.decode(key, "UTF-8"), DataOperation.DELETE);
|
||||
NotifyCenter.publishEvent(
|
||||
ValueChangeEvent.builder().key(URLDecoder.decode(key, "UTF-8")).action(DataOperation.DELETE)
|
||||
.build());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Loggers.RAFT.warn("datum key decode failed: {}", key);
|
||||
}
|
||||
@ -1045,115 +1076,9 @@ public class RaftCore {
|
||||
return initialized || !globalConfig.isDataWarmup();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getNotifyTaskCount() {
|
||||
return notifier.getTaskSize();
|
||||
return (int) publisher.currentEventSize();
|
||||
}
|
||||
|
||||
public class Notifier implements Runnable {
|
||||
|
||||
private ConcurrentHashMap<String, String> services = new ConcurrentHashMap<>(10 * 1024);
|
||||
|
||||
private BlockingQueue<Pair> tasks = new LinkedBlockingQueue<>(1024 * 1024);
|
||||
|
||||
/**
|
||||
* Add notify task.
|
||||
*
|
||||
* @param datumKey datum key
|
||||
* @param action action of datum
|
||||
*/
|
||||
public void addTask(String datumKey, DataOperation action) {
|
||||
|
||||
if (services.containsKey(datumKey) && action == DataOperation.CHANGE) {
|
||||
return;
|
||||
}
|
||||
if (action == DataOperation.CHANGE) {
|
||||
services.put(datumKey, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
Loggers.RAFT.info("add task {}", datumKey);
|
||||
|
||||
tasks.add(Pair.with(datumKey, action));
|
||||
}
|
||||
|
||||
public int getTaskSize() {
|
||||
return tasks.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Loggers.RAFT.info("raft notifier started");
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
|
||||
Pair pair = tasks.take();
|
||||
|
||||
if (pair == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String datumKey = (String) pair.getValue0();
|
||||
DataOperation action = (DataOperation) pair.getValue1();
|
||||
|
||||
services.remove(datumKey);
|
||||
|
||||
Loggers.RAFT.info("remove task {}", datumKey);
|
||||
|
||||
int count = 0;
|
||||
|
||||
if (listeners.containsKey(KeyBuilder.SERVICE_META_KEY_PREFIX)) {
|
||||
|
||||
if (KeyBuilder.matchServiceMetaKey(datumKey) && !KeyBuilder.matchSwitchKey(datumKey)) {
|
||||
|
||||
for (RecordListener listener : listeners.get(KeyBuilder.SERVICE_META_KEY_PREFIX)) {
|
||||
try {
|
||||
if (action == DataOperation.CHANGE) {
|
||||
listener.onChange(datumKey, getDatum(datumKey).value);
|
||||
}
|
||||
|
||||
if (action == DataOperation.DELETE) {
|
||||
listener.onDelete(datumKey);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Loggers.RAFT
|
||||
.error("[NACOS-RAFT] error while notifying listener of key: {}", datumKey,
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!listeners.containsKey(datumKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (RecordListener listener : listeners.get(datumKey)) {
|
||||
|
||||
count++;
|
||||
|
||||
try {
|
||||
if (action == DataOperation.CHANGE) {
|
||||
listener.onChange(datumKey, getDatum(datumKey).value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action == DataOperation.DELETE) {
|
||||
listener.onDelete(datumKey);
|
||||
continue;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Loggers.RAFT.error("[NACOS-RAFT] error while notifying listener of key: {}", datumKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (Loggers.RAFT.isDebugEnabled()) {
|
||||
Loggers.RAFT.debug("[NACOS-RAFT] datum change notified, key: {}, listener count: {}", datumKey,
|
||||
count);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Loggers.RAFT.error("[NACOS-RAFT] Error while handling notifying task", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ package com.alibaba.nacos.naming.consistency.persistent.raft;
|
||||
import com.alibaba.nacos.common.utils.JacksonUtils;
|
||||
import com.alibaba.nacos.core.cluster.Member;
|
||||
import com.alibaba.nacos.core.cluster.ServerMemberManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement;
|
||||
import com.alibaba.nacos.naming.misc.Loggers;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -30,25 +31,47 @@ import java.util.Map;
|
||||
/**
|
||||
* Inject the raft information from the naming module into the outlier information of the node.
|
||||
*
|
||||
* @deprecated will remove in 1.4.x
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
@Deprecated
|
||||
@Component
|
||||
public class RaftListener implements SmartApplicationListener {
|
||||
|
||||
private static final String GROUP = "naming";
|
||||
|
||||
@Autowired
|
||||
private ServerMemberManager memberManager;
|
||||
private final ServerMemberManager memberManager;
|
||||
|
||||
private final ClusterVersionJudgement versionJudgement;
|
||||
|
||||
private volatile boolean stopUpdate = false;
|
||||
|
||||
public RaftListener(ServerMemberManager memberManager, ClusterVersionJudgement versionJudgement) {
|
||||
this.memberManager = memberManager;
|
||||
this.versionJudgement = versionJudgement;
|
||||
this.init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.versionJudgement.registerObserver(isAllNewVersion -> {
|
||||
stopUpdate = isAllNewVersion;
|
||||
if (stopUpdate) {
|
||||
Loggers.RAFT.warn("start to move old raft protocol metadata");
|
||||
Member self = memberManager.getSelf();
|
||||
self.delExtendVal(GROUP);
|
||||
memberManager.update(self);
|
||||
}
|
||||
}, -2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
boolean a = BaseRaftEvent.class.isAssignableFrom(eventType);
|
||||
return a;
|
||||
return BaseRaftEvent.class.isAssignableFrom(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof BaseRaftEvent) {
|
||||
if (event instanceof BaseRaftEvent && !stopUpdate) {
|
||||
BaseRaftEvent raftEvent = (BaseRaftEvent) event;
|
||||
RaftPeer local = raftEvent.getLocal();
|
||||
String json = JacksonUtils.toJson(local);
|
||||
|
@ -26,8 +26,10 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
/**
|
||||
* Raft peer.
|
||||
*
|
||||
* @deprecated will remove in 1.4.x
|
||||
* @author nacos
|
||||
*/
|
||||
@Deprecated
|
||||
public class RaftPeer {
|
||||
|
||||
public String ip;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user