Merge pull request #4149 from KomachiSion/feature_support_grpc_core
Synchronize develop branch and upgrade version to 2.0.0-ALPHA
This commit is contained in:
commit
0d4f882835
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:
|
script:
|
||||||
- mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true
|
- 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 -Prelease-nacos -Dmaven.test.skip=true clean install -U
|
||||||
|
- mvn clean -Premove-test-data
|
||||||
- mvn clean install -Pcit-test
|
- mvn clean install -Pcit-test
|
||||||
|
- mvn clean -Premove-test-data
|
||||||
- mvn clean package -Pit-test
|
- mvn clean package -Pit-test
|
||||||
|
- mvn clean -Premove-test-data
|
||||||
after_success:
|
after_success:
|
||||||
- mvn clean package -Pit-test
|
- mvn clean package -Pit-test
|
||||||
- mvn sonar:sonar -Psonar-apache
|
- mvn sonar:sonar -Psonar-apache
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -188,6 +188,8 @@ public class Constants {
|
|||||||
|
|
||||||
public static final String HTTP_PREFIX = "http";
|
public static final String HTTP_PREFIX = "http";
|
||||||
|
|
||||||
|
public static final String ALL_PATTERN = "*";
|
||||||
|
|
||||||
public static final String COLON = ":";
|
public static final String COLON = ":";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ package com.alibaba.nacos.api.common;
|
|||||||
* <li> Global and common code starts with 10001.
|
* <li> Global and common code starts with 10001.
|
||||||
* <li> Naming module code starts with 20001.
|
* <li> Naming module code starts with 20001.
|
||||||
* <li> Config module code starts with 30001.
|
* <li> Config module code starts with 30001.
|
||||||
|
* <li> Core module code starts with 40001.
|
||||||
*
|
*
|
||||||
* @author nkorange
|
* @author nkorange
|
||||||
* @since 1.2.0
|
* @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.AbstractHealthChecker;
|
||||||
import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp;
|
import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -31,7 +32,9 @@ import java.util.Map;
|
|||||||
* @author nkorange
|
* @author nkorange
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("checkstyle:abbreviationaswordinname")
|
@SuppressWarnings("checkstyle:abbreviationaswordinname")
|
||||||
public class Cluster {
|
public class Cluster implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -7196138840047197271L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of belonging service.
|
* 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;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -33,7 +34,9 @@ import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN;
|
|||||||
* @author nkorange
|
* @author nkorange
|
||||||
*/
|
*/
|
||||||
@JsonInclude(Include.NON_NULL)
|
@JsonInclude(Include.NON_NULL)
|
||||||
public class Instance {
|
public class Instance implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -742906310567291979L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* unique id of this instance.
|
* unique id of this instance.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.alibaba.nacos.api.naming.pojo;
|
package com.alibaba.nacos.api.naming.pojo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -29,7 +30,9 @@ import java.util.Map;
|
|||||||
*
|
*
|
||||||
* @author nkorange
|
* @author nkorange
|
||||||
*/
|
*/
|
||||||
public class Service {
|
public class Service implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -3470985546826874460L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* service name.
|
* service name.
|
||||||
|
@ -25,6 +25,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
|
|||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract health checker.
|
* Abstract health checker.
|
||||||
*
|
*
|
||||||
@ -34,7 +36,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
|||||||
@JsonSubTypes({@JsonSubTypes.Type(name = Http.TYPE, value = Http.class),
|
@JsonSubTypes({@JsonSubTypes.Type(name = Http.TYPE, value = Http.class),
|
||||||
@JsonSubTypes.Type(name = Mysql.TYPE, value = Mysql.class),
|
@JsonSubTypes.Type(name = Mysql.TYPE, value = Mysql.class),
|
||||||
@JsonSubTypes.Type(name = Tcp.TYPE, value = Tcp.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
|
@JsonIgnore
|
||||||
protected final String type;
|
protected final String type;
|
||||||
|
@ -54,4 +54,25 @@ public class NamingUtils {
|
|||||||
}
|
}
|
||||||
return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[0];
|
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;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract selector that only contains a type.
|
* Abstract selector that only contains a type.
|
||||||
*
|
*
|
||||||
@ -27,7 +29,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
|||||||
* @since 0.7.0
|
* @since 0.7.0
|
||||||
*/
|
*/
|
||||||
@JsonTypeInfo(use = Id.NAME, property = "type", defaultImpl = NoneSelector.class)
|
@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.
|
* The type of this selector, each child class should announce its own unique type.
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
@ -84,4 +84,4 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</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>
|
<parent>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<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;
|
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");
|
.newUpdater(DefaultPublisher.class, Long.class, "lastEventSequence");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -116,7 +116,7 @@ public class DefaultPublisher extends Thread implements EventPublisher {
|
|||||||
}
|
}
|
||||||
final Event event = queue.take();
|
final Event event = queue.take();
|
||||||
receiveEvent(event);
|
receiveEvent(event);
|
||||||
updater.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
|
UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence()));
|
||||||
}
|
}
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
LOGGER.error("Event listener exception : {}", ex);
|
LOGGER.error("Event listener exception : {}", ex);
|
||||||
|
@ -79,7 +79,7 @@ public class DefaultSharePublisher extends DefaultPublisher {
|
|||||||
try {
|
try {
|
||||||
Set<Subscriber> sets = subMappings.get(subSlowEventType);
|
Set<Subscriber> sets = subMappings.get(subSlowEventType);
|
||||||
|
|
||||||
if (sets != null && sets.contains(subscriber)) {
|
if (sets != null) {
|
||||||
sets.remove(subscriber);
|
sets.remove(subscriber);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -129,21 +129,22 @@ public class MapUtils {
|
|||||||
/**
|
/**
|
||||||
* ComputeIfAbsent lazy load.
|
* ComputeIfAbsent lazy load.
|
||||||
*
|
*
|
||||||
* @param target target Map data.
|
* @param target target Map data.
|
||||||
* @param key map key.
|
* @param key map key.
|
||||||
* @param mappingFunction funtion which is need to be executed.
|
* @param mappingFunction function which is need to be executed.
|
||||||
* @param param1 function's parameter value1.
|
* @param param1 function's parameter value1.
|
||||||
* @param param2 function's parameter value1.
|
* @param param2 function's parameter value1.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
public static Object computeIfAbsent(Map target, Object key, BiFunction mappingFunction, Object param1,
|
public static Object computeIfAbsent(Map target, Object key, BiFunction mappingFunction, Object param1,
|
||||||
Object param2) {
|
Object param2) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(target, "target");
|
||||||
Objects.requireNonNull(key, "key");
|
Objects.requireNonNull(key, "key");
|
||||||
Objects.requireNonNull(key, "mappingFunction");
|
Objects.requireNonNull(mappingFunction, "mappingFunction");
|
||||||
Objects.requireNonNull(key, "param1");
|
Objects.requireNonNull(param1, "param1");
|
||||||
Objects.requireNonNull(key, "param2");
|
Objects.requireNonNull(param2, "param2");
|
||||||
|
|
||||||
Object val = target.get(key);
|
Object val = target.get(key);
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
@ -153,5 +154,4 @@ public class MapUtils {
|
|||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package com.alibaba.nacos.common.utils;
|
package com.alibaba.nacos.common.utils;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Properties;
|
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() {
|
public static String getFullClientVersion() {
|
||||||
return clientVersion;
|
return clientVersion;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@ -48,8 +50,9 @@ public class NacosExecuteTaskExecuteEngineTest {
|
|||||||
private AbstractExecuteTask task;
|
private AbstractExecuteTask task;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddTask() {
|
public void testAddTask() throws InterruptedException {
|
||||||
executeTaskExecuteEngine.addTask("test", task);
|
executeTaskExecuteEngine.addTask("test", task);
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
verify(task).run();
|
verify(task).run();
|
||||||
assertTrue(executeTaskExecuteEngine.isEmpty());
|
assertTrue(executeTaskExecuteEngine.isEmpty());
|
||||||
assertEquals(0, executeTaskExecuteEngine.size());
|
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>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<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.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* History management controller.
|
* History management controller.
|
||||||
*
|
*
|
||||||
@ -46,11 +43,11 @@ public class HistoryController {
|
|||||||
/**
|
/**
|
||||||
* Query the list history config.
|
* Query the list history config.
|
||||||
*
|
*
|
||||||
* @param dataId dataId string value.
|
* @param dataId dataId string value.
|
||||||
* @param group group string value.
|
* @param group group string value.
|
||||||
* @param tenant tenant string value.
|
* @param tenant tenant string value.
|
||||||
* @param appName appName string value.
|
* @param appName appName string value.
|
||||||
* @param pageNo pageNo string value.
|
* @param pageNo pageNo string value.
|
||||||
* @param pageSize pageSize string value.
|
* @param pageSize pageSize string value.
|
||||||
* @param modelMap modeMap.
|
* @param modelMap modeMap.
|
||||||
* @return
|
* @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
|
@GetMapping
|
||||||
public ConfigHistoryInfo getConfigHistoryInfo(HttpServletRequest request, HttpServletResponse response,
|
public ConfigHistoryInfo getConfigHistoryInfo(@RequestParam("nid") Long nid) {
|
||||||
@RequestParam("nid") Long nid, ModelMap modelMap) {
|
|
||||||
return persistService.detailConfigHistory(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,
|
protected void dumpOperate(DumpProcessor processor, DumpAllProcessor dumpAllProcessor,
|
||||||
DumpAllBetaProcessor dumpAllBetaProcessor, DumpAllTagProcessor dumpAllTagProcessor) throws NacosException {
|
DumpAllBetaProcessor dumpAllBetaProcessor, DumpAllTagProcessor dumpAllTagProcessor) throws NacosException {
|
||||||
TimerContext.start("CONFIG_DUMP_TO_FILE");
|
String dumpFileContext = "CONFIG_DUMP_TO_FILE";
|
||||||
|
TimerContext.start(dumpFileContext);
|
||||||
try {
|
try {
|
||||||
LogUtil.DEFAULT_LOG.warn("DumpService start");
|
LogUtil.DEFAULT_LOG.warn("DumpService start");
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ public abstract class DumpService {
|
|||||||
|
|
||||||
ConfigExecutor.scheduleConfigTask(clearConfigHistory, 10, 10, TimeUnit.MINUTES);
|
ConfigExecutor.scheduleConfigTask(clearConfigHistory, 10, 10, TimeUnit.MINUTES);
|
||||||
} finally {
|
} finally {
|
||||||
TimerContext.end(LogUtil.DUMP_LOG);
|
TimerContext.end(dumpFileContext, LogUtil.DUMP_LOG);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1077,6 +1077,15 @@ public interface PersistService {
|
|||||||
*/
|
*/
|
||||||
ConfigHistoryInfo detailConfigHistory(Long nid);
|
ConfigHistoryInfo detailConfigHistory(Long nid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get previous config detail.
|
||||||
|
*
|
||||||
|
* @param id id
|
||||||
|
* @return {@link ConfigHistoryInfo}
|
||||||
|
*/
|
||||||
|
ConfigHistoryInfo detailPreviousConfigHistory(Long id);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* insert tenant info.
|
* insert tenant info.
|
||||||
*
|
*
|
||||||
@ -1229,4 +1238,5 @@ public interface PersistService {
|
|||||||
* @return count by tenantId
|
* @return count by tenantId
|
||||||
*/
|
*/
|
||||||
int tenantInfoCountByTenantId(String tenantId);
|
int tenantInfoCountByTenantId(String tenantId);
|
||||||
|
|
||||||
}
|
}
|
@ -51,6 +51,10 @@ import javax.sql.DataSource;
|
|||||||
*/
|
*/
|
||||||
public class DerbySnapshotOperation implements SnapshotOperation {
|
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 backupSql = "CALL SYSCS_UTIL.SYSCS_BACKUP_DATABASE(?)";
|
||||||
|
|
||||||
private final String snapshotDir = "derby_data";
|
private final String snapshotDir = "derby_data";
|
||||||
@ -72,8 +76,7 @@ public class DerbySnapshotOperation implements SnapshotOperation {
|
|||||||
@Override
|
@Override
|
||||||
public void onSnapshotSave(Writer writer, BiConsumer<Boolean, Throwable> callFinally) {
|
public void onSnapshotSave(Writer writer, BiConsumer<Boolean, Throwable> callFinally) {
|
||||||
RaftExecutor.doSnapshot(() -> {
|
RaftExecutor.doSnapshot(() -> {
|
||||||
|
TimerContext.start(DERBY_SNAPSHOT_SAVE);
|
||||||
TimerContext.start("CONFIG_DERBY_SNAPSHOT_SAVE");
|
|
||||||
|
|
||||||
final Lock lock = writeLock;
|
final Lock lock = writeLock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
@ -100,7 +103,7 @@ public class DerbySnapshotOperation implements SnapshotOperation {
|
|||||||
callFinally.accept(false, t);
|
callFinally.accept(false, t);
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
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) {
|
public boolean onSnapshotLoad(Reader reader) {
|
||||||
final String readerPath = reader.getPath();
|
final String readerPath = reader.getPath();
|
||||||
final String sourceFile = Paths.get(readerPath, snapshotArchive).toString();
|
final String sourceFile = Paths.get(readerPath, snapshotArchive).toString();
|
||||||
|
TimerContext.start(DERBY_SNAPSHOT_LOAD);
|
||||||
TimerContext.start("CONFIG_DERBY_SNAPSHOT_LOAD");
|
|
||||||
final Lock lock = writeLock;
|
final Lock lock = writeLock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
@ -145,7 +147,7 @@ public class DerbySnapshotOperation implements SnapshotOperation {
|
|||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
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);
|
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
|
@Override
|
||||||
public void insertTenantInfoAtomic(String kp, String tenantId, String tenantName, String tenantDesc,
|
public void insertTenantInfoAtomic(String kp, String tenantId, String tenantName, String tenantDesc,
|
||||||
String createResoure, final long time) {
|
String createResoure, final long time) {
|
||||||
@ -2299,7 +2305,7 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService {
|
|||||||
int skipCount = 0;
|
int skipCount = 0;
|
||||||
List<Map<String, String>> failData = null;
|
List<Map<String, String>> failData = null;
|
||||||
List<Map<String, String>> skipData = null;
|
List<Map<String, String>> skipData = null;
|
||||||
|
|
||||||
final BiConsumer<Boolean, Throwable> callFinally = (result, t) -> {
|
final BiConsumer<Boolean, Throwable> callFinally = (result, t) -> {
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
throw new NacosRuntimeException(0, t);
|
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
|
@Override
|
||||||
public void insertTenantInfoAtomic(String kp, String tenantId, String tenantName, String tenantDesc,
|
public void insertTenantInfoAtomic(String kp, String tenantId, String tenantName, String tenantDesc,
|
||||||
String createResoure, final long time) {
|
String createResoure, final long time) {
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -48,4 +48,7 @@ public class SerializeFactory {
|
|||||||
return SERIALIZER_MAP.get(defaultSerializer);
|
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;
|
package com.alibaba.nacos.consistency;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@ -28,6 +29,15 @@ public interface Serializer {
|
|||||||
|
|
||||||
Map<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>(8);
|
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.
|
* Deserialize the data.
|
||||||
*
|
*
|
||||||
@ -36,7 +46,17 @@ public interface Serializer {
|
|||||||
* @param <T> class type
|
* @param <T> class type
|
||||||
* @return target object instance
|
* @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.
|
* Deserialize the data.
|
||||||
|
@ -29,4 +29,6 @@ public class MetadataKey {
|
|||||||
|
|
||||||
public static final String RAFT_GROUP_MEMBER = "raftGroupMember";
|
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.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hessian serializer.
|
* hessian serializer.
|
||||||
@ -40,12 +41,25 @@ public class HessianSerializer implements Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)) {
|
if (ByteUtils.isEmpty(data)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(data));
|
Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(data));
|
||||||
input.setSerializerFactory(serializerFactory);
|
input.setSerializerFactory(serializerFactory);
|
||||||
Object resultObject;
|
Object resultObject;
|
||||||
@ -78,4 +92,4 @@ public class HessianSerializer implements Serializer {
|
|||||||
return "Hessian";
|
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>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>nacos-console</artifactId>
|
<artifactId>nacos-console</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
@ -32,6 +32,9 @@ class DiffEditorDialog extends React.Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
publishConfig: PropTypes.func,
|
publishConfig: PropTypes.func,
|
||||||
|
title: PropTypes.string,
|
||||||
|
currentArea: PropTypes.string,
|
||||||
|
originalArea: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -71,6 +74,7 @@ class DiffEditorDialog extends React.Component {
|
|||||||
highlightDifferences: true,
|
highlightDifferences: true,
|
||||||
connect: 'align',
|
connect: 'align',
|
||||||
collapseIdentical: false,
|
collapseIdentical: false,
|
||||||
|
revertButtons: typeof this.props.publishConfig === 'function',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,19 +84,29 @@ class DiffEditorDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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 = (
|
const footer = (
|
||||||
<div>
|
<div>
|
||||||
{' '}
|
{' '}
|
||||||
<Button type="primary" onClick={this.confirmPub.bind(this)}>
|
{typeof this.props.publishConfig === 'function' ? (
|
||||||
{locale.publish}
|
publishButton
|
||||||
</Button>
|
) : (
|
||||||
|
<Button type="primary" onClick={this.closeDialog.bind(this)}>
|
||||||
|
{locale.back}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
console.log(footer);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Dialog
|
<Dialog
|
||||||
title={locale.contents}
|
title={title}
|
||||||
style={{ width: '80%' }}
|
style={{ width: '80%' }}
|
||||||
visible={this.state.dialogvisible}
|
visible={this.state.dialogvisible}
|
||||||
footer={footer}
|
footer={footer}
|
||||||
@ -101,8 +115,8 @@ class DiffEditorDialog extends React.Component {
|
|||||||
<div style={{ height: 400 }}>
|
<div style={{ height: 400 }}>
|
||||||
<div>
|
<div>
|
||||||
<Row>
|
<Row>
|
||||||
<Col style={{ textAlign: 'center' }}>{locale.currentArea}</Col>
|
<Col style={{ textAlign: 'center' }}>{currentArea}</Col>
|
||||||
<Col style={{ textAlign: 'center' }}>{locale.originalValue}</Col>
|
<Col style={{ textAlign: 'center' }}>{originalArea}</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ clear: 'both', height: 480 }} ref={this.diffeditor} />
|
<div style={{ clear: 'both', height: 480 }} ref={this.diffeditor} />
|
||||||
|
@ -378,9 +378,7 @@ const I18N_CONF = {
|
|||||||
},
|
},
|
||||||
DiffEditorDialog: {
|
DiffEditorDialog: {
|
||||||
publish: 'Publish',
|
publish: 'Publish',
|
||||||
contents: 'Content Comparison',
|
back: 'Back',
|
||||||
currentArea: 'Current Value',
|
|
||||||
originalValue: 'Original Value',
|
|
||||||
},
|
},
|
||||||
ConfigEditor: {
|
ConfigEditor: {
|
||||||
official: 'Official',
|
official: 'Official',
|
||||||
@ -411,6 +409,9 @@ const I18N_CONF = {
|
|||||||
publish: 'Publish',
|
publish: 'Publish',
|
||||||
back: 'Back',
|
back: 'Back',
|
||||||
codeValErrorPrompt: 'Configuration information may have syntax errors. Are you sure to submit?',
|
codeValErrorPrompt: 'Configuration information may have syntax errors. Are you sure to submit?',
|
||||||
|
dialogTitle: 'Content Comparison',
|
||||||
|
dialogCurrentArea: 'Current Value',
|
||||||
|
dialogOriginalArea: 'Original Value',
|
||||||
},
|
},
|
||||||
EditorNameSpace: {
|
EditorNameSpace: {
|
||||||
notice: 'Notice',
|
notice: 'Notice',
|
||||||
@ -498,6 +499,9 @@ const I18N_CONF = {
|
|||||||
betaRelease: 'Beta Publish:',
|
betaRelease: 'Beta Publish:',
|
||||||
configuration: 'Configuration Content:',
|
configuration: 'Configuration Content:',
|
||||||
back: 'Back',
|
back: 'Back',
|
||||||
|
versionComparison: 'Version Comparison',
|
||||||
|
dialogCurrentArea: 'Current Version',
|
||||||
|
dialogOriginalArea: 'Previous Version',
|
||||||
},
|
},
|
||||||
ConfigRollback: {
|
ConfigRollback: {
|
||||||
rollBack: 'Roll Back',
|
rollBack: 'Roll Back',
|
||||||
|
@ -376,9 +376,7 @@ const I18N_CONF = {
|
|||||||
},
|
},
|
||||||
DiffEditorDialog: {
|
DiffEditorDialog: {
|
||||||
publish: '确认发布',
|
publish: '确认发布',
|
||||||
contents: '内容比较',
|
back: '返回',
|
||||||
currentArea: '当前值',
|
|
||||||
originalValue: '原始值',
|
|
||||||
},
|
},
|
||||||
ConfigEditor: {
|
ConfigEditor: {
|
||||||
official: '正式',
|
official: '正式',
|
||||||
@ -409,6 +407,9 @@ const I18N_CONF = {
|
|||||||
publish: '发布',
|
publish: '发布',
|
||||||
back: '返回',
|
back: '返回',
|
||||||
codeValErrorPrompt: '配置信息可能有语法错误, 确定提交吗?',
|
codeValErrorPrompt: '配置信息可能有语法错误, 确定提交吗?',
|
||||||
|
dialogTitle: '内容比较',
|
||||||
|
dialogCurrentArea: '当前值',
|
||||||
|
dialogOriginalArea: '原始值',
|
||||||
},
|
},
|
||||||
EditorNameSpace: {
|
EditorNameSpace: {
|
||||||
notice: '提示',
|
notice: '提示',
|
||||||
@ -495,6 +496,9 @@ const I18N_CONF = {
|
|||||||
betaRelease: 'Beta发布:',
|
betaRelease: 'Beta发布:',
|
||||||
configuration: '配置内容:',
|
configuration: '配置内容:',
|
||||||
back: '返回',
|
back: '返回',
|
||||||
|
versionComparison: '版本对比',
|
||||||
|
dialogCurrentArea: '当前版本',
|
||||||
|
dialogOriginalArea: '上一版本',
|
||||||
},
|
},
|
||||||
ConfigRollback: {
|
ConfigRollback: {
|
||||||
rollBack: '回滚配置',
|
rollBack: '回滚配置',
|
||||||
|
@ -15,15 +15,27 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
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 { getParams, request } from '../../../globalLib';
|
||||||
import { generateUrl } from '../../../utils/nacosutil';
|
import { generateUrl } from '../../../utils/nacosutil';
|
||||||
|
import DiffEditorDialog from '../../../components/DiffEditorDialog';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const TabPane = Tab.Item;
|
const TabPane = Tab.Item;
|
||||||
const FormItem = Form.Item;
|
const FormItem = Form.Item;
|
||||||
|
const { Row, Col } = Grid;
|
||||||
|
|
||||||
@ConfigProvider.config
|
@ConfigProvider.config
|
||||||
class ConfigDetail extends React.Component {
|
class ConfigDetail extends React.Component {
|
||||||
@ -56,6 +68,7 @@ class ConfigDetail extends React.Component {
|
|||||||
this.searchGroup = getParams('searchGroup') || '';
|
this.searchGroup = getParams('searchGroup') || '';
|
||||||
this.pageSize = getParams('pageSize');
|
this.pageSize = getParams('pageSize');
|
||||||
this.pageNo = getParams('pageNo');
|
this.pageNo = getParams('pageNo');
|
||||||
|
this.diffEditorDialog = React.createRef();
|
||||||
// this.params = window.location.hash.split('?')[1]||'';
|
// 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() {
|
render() {
|
||||||
const { locale = {} } = this.props;
|
const { locale = {} } = this.props;
|
||||||
const { init } = this.field;
|
const { init } = this.field;
|
||||||
@ -276,12 +315,23 @@ class ConfigDetail extends React.Component {
|
|||||||
<FormItem label={locale.configuration} required {...formItemLayout}>
|
<FormItem label={locale.configuration} required {...formItemLayout}>
|
||||||
<div style={{ clear: 'both', height: 300 }} id="container" />
|
<div style={{ clear: 'both', height: 300 }} id="container" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label={' '} {...formItemLayout}>
|
</Form>
|
||||||
<Button type={'primary'} onClick={this.goList.bind(this)}>
|
<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}
|
{locale.back}
|
||||||
</Button>
|
</Button>
|
||||||
</FormItem>
|
</Col>
|
||||||
</Form>
|
</Row>
|
||||||
|
<DiffEditorDialog
|
||||||
|
ref={this.diffEditorDialog}
|
||||||
|
title={locale.versionComparison}
|
||||||
|
currentArea={locale.dialogCurrentArea}
|
||||||
|
originalArea={locale.dialogOriginalArea}
|
||||||
|
/>
|
||||||
</Loading>
|
</Loading>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,3 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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} />
|
<SuccessDialog ref={this.successDialog} />
|
||||||
</Loading>
|
</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>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -108,6 +108,10 @@ public class Member implements Comparable<Member>, Cloneable {
|
|||||||
extendInfo.put(key, value);
|
extendInfo.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void delExtendVal(String key) {
|
||||||
|
extendInfo.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean check() {
|
public boolean check() {
|
||||||
return StringUtils.isNoneBlank(ip, address) && port != -1;
|
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.ExecutorFactory;
|
||||||
import com.alibaba.nacos.common.executor.NameThreadFactory;
|
import com.alibaba.nacos.common.executor.NameThreadFactory;
|
||||||
import com.alibaba.nacos.common.executor.ThreadPoolManager;
|
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.common.notify.NotifyCenter;
|
||||||
import com.alibaba.nacos.sys.file.WatchFileCenter;
|
import com.alibaba.nacos.sys.file.WatchFileCenter;
|
||||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||||
@ -142,8 +141,6 @@ public class StartingSpringApplicationRunListener implements SpringApplicationRu
|
|||||||
logFilePath();
|
logFilePath();
|
||||||
|
|
||||||
LOGGER.error("Startup errors : {}", exception);
|
LOGGER.error("Startup errors : {}", exception);
|
||||||
|
|
||||||
HttpClientManager.shutdown();
|
|
||||||
ThreadPoolManager.shutdown();
|
ThreadPoolManager.shutdown();
|
||||||
WatchFileCenter.shutdown();
|
WatchFileCenter.shutdown();
|
||||||
NotifyCenter.shutdown();
|
NotifyCenter.shutdown();
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
package com.alibaba.nacos.core.controller;
|
package com.alibaba.nacos.core.controller;
|
||||||
|
|
||||||
import com.alibaba.nacos.common.http.Callback;
|
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.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.Header;
|
||||||
import com.alibaba.nacos.common.http.param.Query;
|
import com.alibaba.nacos.common.http.param.Query;
|
||||||
import com.alibaba.nacos.common.model.RestResult;
|
import com.alibaba.nacos.common.model.RestResult;
|
||||||
@ -152,7 +152,7 @@ public class NacosClusterController {
|
|||||||
public RestResult<String> leave(@RequestBody Collection<String> params) throws Exception {
|
public RestResult<String> leave(@RequestBody Collection<String> params) throws Exception {
|
||||||
Collection<Member> memberList = MemberUtils.multiParse(params);
|
Collection<Member> memberList = MemberUtils.multiParse(params);
|
||||||
memberManager.memberLeave(memberList);
|
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 GenericType<RestResult<String>> genericType = new GenericType<RestResult<String>>() {
|
||||||
};
|
};
|
||||||
final Collection<Member> notifyList = memberManager.allMembersWithoutSelf();
|
final Collection<Member> notifyList = memberManager.allMembersWithoutSelf();
|
||||||
@ -162,7 +162,7 @@ public class NacosClusterController {
|
|||||||
final String url = HttpUtils
|
final String url = HttpUtils
|
||||||
.buildUrl(false, member.getAddress(), ApplicationUtils.getContextPath(), Commons.NACOS_CORE_CONTEXT,
|
.buildUrl(false, member.getAddress(), ApplicationUtils.getContextPath(), Commons.NACOS_CORE_CONTEXT,
|
||||||
"/cluster/server/leave");
|
"/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
|
@Override
|
||||||
public void onReceive(RestResult<String> result) {
|
public void onReceive(RestResult<String> result) {
|
||||||
try {
|
try {
|
||||||
|
@ -36,6 +36,7 @@ import org.springframework.context.event.ContextStartedEvent;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -108,6 +109,7 @@ public class ProtocolManager extends MemberChangeListener implements Application
|
|||||||
return apProtocol;
|
return apProtocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (Objects.nonNull(apProtocol)) {
|
if (Objects.nonNull(apProtocol)) {
|
||||||
apProtocol.shutdown();
|
apProtocol.shutdown();
|
||||||
|
@ -51,7 +51,7 @@ public class DistroProtocol {
|
|||||||
|
|
||||||
private final DistroConfig distroConfig;
|
private final DistroConfig distroConfig;
|
||||||
|
|
||||||
private volatile boolean loadCompleted = false;
|
private volatile boolean isInitialized = false;
|
||||||
|
|
||||||
public DistroProtocol(ServerMemberManager memberManager, DistroComponentHolder distroComponentHolder,
|
public DistroProtocol(ServerMemberManager memberManager, DistroComponentHolder distroComponentHolder,
|
||||||
DistroTaskEngineHolder distroTaskEngineHolder, DistroConfig distroConfig) {
|
DistroTaskEngineHolder distroTaskEngineHolder, DistroConfig distroConfig) {
|
||||||
@ -59,32 +59,41 @@ public class DistroProtocol {
|
|||||||
this.distroComponentHolder = distroComponentHolder;
|
this.distroComponentHolder = distroComponentHolder;
|
||||||
this.distroTaskEngineHolder = distroTaskEngineHolder;
|
this.distroTaskEngineHolder = distroTaskEngineHolder;
|
||||||
this.distroConfig = distroConfig;
|
this.distroConfig = distroConfig;
|
||||||
startVerifyTask();
|
startDistroTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startVerifyTask() {
|
private void startDistroTask() {
|
||||||
if (ApplicationUtils.getStandaloneMode()) {
|
if (ApplicationUtils.getStandaloneMode()) {
|
||||||
|
isInitialized = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
startVerifyTask();
|
||||||
|
startLoadTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startLoadTask() {
|
||||||
DistroCallback loadCallback = new DistroCallback() {
|
DistroCallback loadCallback = new DistroCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess() {
|
public void onSuccess() {
|
||||||
loadCompleted = true;
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailed(Throwable throwable) {
|
public void onFailed(Throwable throwable) {
|
||||||
loadCompleted = false;
|
isInitialized = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
GlobalExecutor.schedulePartitionDataTimedSync(new DistroVerifyTask(memberManager, distroComponentHolder),
|
|
||||||
distroConfig.getVerifyIntervalMillis());
|
|
||||||
GlobalExecutor.submitLoadDataTask(
|
GlobalExecutor.submitLoadDataTask(
|
||||||
new DistroLoadDataTask(memberManager, distroComponentHolder, distroConfig, loadCallback));
|
new DistroLoadDataTask(memberManager, distroComponentHolder, distroConfig, loadCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLoadCompleted() {
|
private void startVerifyTask() {
|
||||||
return loadCompleted;
|
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.consistency.IdGenerator;
|
||||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
||||||
|
import com.alibaba.nacos.sys.utils.InetUtils;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -86,7 +87,7 @@ public class SnowFlowerIdGenerator implements IdGenerator {
|
|||||||
} else {
|
} else {
|
||||||
InetAddress address;
|
InetAddress address;
|
||||||
try {
|
try {
|
||||||
address = InetAddress.getLocalHost();
|
address = InetAddress.getByName(InetUtils.getSelfIP());
|
||||||
} catch (final UnknownHostException e) {
|
} catch (final UnknownHostException e) {
|
||||||
throw new IllegalStateException("Cannot get LocalHost InetAddress, please check your network!", 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 String leader = event.getLeader();
|
||||||
final Long term = event.getTerm();
|
final Long term = event.getTerm();
|
||||||
final List<String> raftClusterInfo = event.getRaftClusterInfo();
|
final List<String> raftClusterInfo = event.getRaftClusterInfo();
|
||||||
|
final String errMsg = event.getErrMsg();
|
||||||
|
|
||||||
// Leader information needs to be selectively updated. If it is valid data,
|
// Leader information needs to be selectively updated. If it is valid data,
|
||||||
// the information in the protocol metadata is updated.
|
// the information in the protocol metadata is updated.
|
||||||
MapUtils.putIfValNoEmpty(properties, MetadataKey.LEADER_META_DATA, leader);
|
MapUtils.putIfValNoEmpty(properties, MetadataKey.LEADER_META_DATA, leader);
|
||||||
MapUtils.putIfValNoNull(properties, MetadataKey.TERM_META_DATA, term);
|
MapUtils.putIfValNoNull(properties, MetadataKey.TERM_META_DATA, term);
|
||||||
MapUtils.putIfValNoEmpty(properties, MetadataKey.RAFT_GROUP_MEMBER, raftClusterInfo);
|
MapUtils.putIfValNoEmpty(properties, MetadataKey.RAFT_GROUP_MEMBER, raftClusterInfo);
|
||||||
|
MapUtils.putIfValNoEmpty(properties, MetadataKey.ERR_MSG, errMsg);
|
||||||
|
|
||||||
value.put(groupId, properties);
|
value.put(groupId, properties);
|
||||||
metaData.load(value);
|
metaData.load(value);
|
||||||
@ -198,6 +200,7 @@ public class JRaftProtocol extends AbstractConsistencyProtocol<RaftConfig, LogPr
|
|||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (initialized.get() && shutdowned.compareAndSet(false, true)) {
|
if (initialized.get() && shutdowned.compareAndSet(false, true)) {
|
||||||
|
Loggers.RAFT.info("shutdown jraft server");
|
||||||
raftServer.shutdown();
|
raftServer.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,17 +497,17 @@ public class JRaftServer {
|
|||||||
Configuration oldConf = instance.getConfiguration(groupName);
|
Configuration oldConf = instance.getConfiguration(groupName);
|
||||||
String oldLeader = Optional.ofNullable(instance.selectLeader(groupName)).orElse(PeerId.emptyPeer())
|
String oldLeader = Optional.ofNullable(instance.selectLeader(groupName)).orElse(PeerId.emptyPeer())
|
||||||
.getEndpoint().toString();
|
.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
|
// fix issue #3661 https://github.com/alibaba/nacos/issues/3661
|
||||||
status = instance.refreshLeader(this.cliClientService, groupName, rpcRequestTimeoutMs);
|
status = instance.refreshLeader(this.cliClientService, groupName, rpcRequestTimeoutMs);
|
||||||
if (!status.isOk()) {
|
if (!status.isOk()) {
|
||||||
Loggers.RAFT
|
Loggers.RAFT
|
||||||
.error("Fail to refresh leader for group : {}, status is : {}", groupName, status);
|
.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) {
|
} catch (Exception e) {
|
||||||
Loggers.RAFT.error("Fail to refresh raft metadata info for group : {}, error is : {}", groupName, 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) {
|
public void onError(RaftException e) {
|
||||||
super.onError(e);
|
super.onError(e);
|
||||||
processor.onError(e);
|
processor.onError(e);
|
||||||
|
NotifyCenter.publishEvent(
|
||||||
|
RaftEvent.builder().groupId(groupId).leader(leaderIp).term(term).raftClusterInfo(allPeers())
|
||||||
|
.errMsg(e.toString())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLeader() {
|
public boolean isLeader() {
|
||||||
|
@ -37,6 +37,8 @@ public class RaftEvent extends SlowEvent {
|
|||||||
|
|
||||||
private Long term = null;
|
private Long term = null;
|
||||||
|
|
||||||
|
private String errMsg = "";
|
||||||
|
|
||||||
private List<String> raftClusterInfo = Collections.emptyList();
|
private List<String> raftClusterInfo = Collections.emptyList();
|
||||||
|
|
||||||
public static RaftEventBuilder builder() {
|
public static RaftEventBuilder builder() {
|
||||||
@ -75,6 +77,14 @@ public class RaftEvent extends SlowEvent {
|
|||||||
this.raftClusterInfo = raftClusterInfo;
|
this.raftClusterInfo = raftClusterInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getErrMsg() {
|
||||||
|
return errMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrMsg(String errMsg) {
|
||||||
|
this.errMsg = errMsg;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "RaftEvent{" + "groupId='" + groupId + '\'' + ", leader='" + leader + '\'' + ", term=" + term
|
return "RaftEvent{" + "groupId='" + groupId + '\'' + ", leader='" + leader + '\'' + ", term=" + term
|
||||||
@ -91,6 +101,8 @@ public class RaftEvent extends SlowEvent {
|
|||||||
|
|
||||||
private List<String> raftClusterInfo = Collections.emptyList();
|
private List<String> raftClusterInfo = Collections.emptyList();
|
||||||
|
|
||||||
|
private String errMsg = "";
|
||||||
|
|
||||||
private RaftEventBuilder() {
|
private RaftEventBuilder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +125,11 @@ public class RaftEvent extends SlowEvent {
|
|||||||
this.raftClusterInfo = raftClusterInfo;
|
this.raftClusterInfo = raftClusterInfo;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RaftEventBuilder errMsg(String errMsg) {
|
||||||
|
this.errMsg = errMsg;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public RaftEvent build() {
|
public RaftEvent build() {
|
||||||
RaftEvent raftEvent = new RaftEvent();
|
RaftEvent raftEvent = new RaftEvent();
|
||||||
@ -120,6 +137,7 @@ public class RaftEvent extends SlowEvent {
|
|||||||
raftEvent.setLeader(leader);
|
raftEvent.setLeader(leader);
|
||||||
raftEvent.setTerm(term);
|
raftEvent.setTerm(term);
|
||||||
raftEvent.setRaftClusterInfo(raftClusterInfo);
|
raftEvent.setRaftClusterInfo(raftClusterInfo);
|
||||||
|
raftEvent.setErrMsg(errMsg);
|
||||||
return raftEvent;
|
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;
|
package com.alibaba.nacos.core.utils;
|
||||||
|
|
||||||
import com.alibaba.nacos.common.utils.LoggerUtils;
|
import com.alibaba.nacos.common.utils.LoggerUtils;
|
||||||
import com.alibaba.nacos.common.utils.Pair;
|
|
||||||
import com.alibaba.nacos.common.utils.StringUtils;
|
import com.alibaba.nacos.common.utils.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -33,47 +34,49 @@ import java.util.function.Supplier;
|
|||||||
*/
|
*/
|
||||||
public class TimerContext {
|
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) {
|
public static void start(final String name) {
|
||||||
long startTime = System.currentTimeMillis();
|
TIME_RECORD.get().put(name, System.currentTimeMillis());
|
||||||
TIME_RECORD.set(Pair.with(name, startTime));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void end(final Logger logger) {
|
public static void end(final String name, final Logger logger) {
|
||||||
end(logger, LoggerUtils.DEBUG);
|
end(name, logger, LoggerUtils.DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End the task and print based on the log level.
|
* End the task and print based on the log level.
|
||||||
*
|
*
|
||||||
|
* @param name context name
|
||||||
* @param logger logger
|
* @param logger logger
|
||||||
* @param level logger level
|
* @param level logger level
|
||||||
*/
|
*/
|
||||||
public static void end(final Logger logger, final String level) {
|
public static void end(final String name, final Logger logger, final String level) {
|
||||||
long endTime = System.currentTimeMillis();
|
Map<String, Long> record = TIME_RECORD.get();
|
||||||
Pair<String, Long> record = TIME_RECORD.get();
|
long contextTime = System.currentTimeMillis() - record.remove(name);
|
||||||
if (StringUtils.equals(level, LoggerUtils.DEBUG)) {
|
if (StringUtils.equals(level, LoggerUtils.DEBUG)) {
|
||||||
LoggerUtils.printIfDebugEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
LoggerUtils.printIfDebugEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||||
(endTime - record.getSecond()));
|
|
||||||
}
|
}
|
||||||
if (StringUtils.equals(level, LoggerUtils.INFO)) {
|
if (StringUtils.equals(level, LoggerUtils.INFO)) {
|
||||||
LoggerUtils.printIfInfoEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
LoggerUtils.printIfInfoEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||||
(endTime - record.getSecond()));
|
|
||||||
}
|
}
|
||||||
if (StringUtils.equals(level, LoggerUtils.TRACE)) {
|
if (StringUtils.equals(level, LoggerUtils.TRACE)) {
|
||||||
LoggerUtils.printIfTraceEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
LoggerUtils.printIfTraceEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||||
(endTime - record.getSecond()));
|
|
||||||
}
|
}
|
||||||
if (StringUtils.equals(level, LoggerUtils.ERROR)) {
|
if (StringUtils.equals(level, LoggerUtils.ERROR)) {
|
||||||
LoggerUtils.printIfErrorEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
LoggerUtils.printIfErrorEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||||
(endTime - record.getSecond()));
|
|
||||||
}
|
}
|
||||||
if (StringUtils.equals(level, LoggerUtils.WARN)) {
|
if (StringUtils.equals(level, LoggerUtils.WARN)) {
|
||||||
LoggerUtils.printIfWarnEnabled(logger, "{} cost time : {} ms", record.getFirst(),
|
LoggerUtils.printIfWarnEnabled(logger, "{} cost time : {} ms", name, contextTime);
|
||||||
(endTime - record.getSecond()));
|
}
|
||||||
|
if (record.isEmpty()) {
|
||||||
|
TIME_RECORD.remove();
|
||||||
}
|
}
|
||||||
TIME_RECORD.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,7 +91,7 @@ public class TimerContext {
|
|||||||
try {
|
try {
|
||||||
job.run();
|
job.run();
|
||||||
} finally {
|
} finally {
|
||||||
end(logger);
|
end(name, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +107,7 @@ public class TimerContext {
|
|||||||
try {
|
try {
|
||||||
return job.get();
|
return job.get();
|
||||||
} finally {
|
} finally {
|
||||||
end(logger);
|
end(name, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +124,7 @@ public class TimerContext {
|
|||||||
try {
|
try {
|
||||||
return job.apply(args);
|
return job.apply(args);
|
||||||
} finally {
|
} finally {
|
||||||
end(logger);
|
end(name, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +141,7 @@ public class TimerContext {
|
|||||||
try {
|
try {
|
||||||
job.accept(args);
|
job.accept(args);
|
||||||
} finally {
|
} finally {
|
||||||
end(logger);
|
end(name, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,26 @@ public class WebUtils {
|
|||||||
* @return Decoded data
|
* @return Decoded data
|
||||||
*/
|
*/
|
||||||
private static String resolveValue(String value, String encoding) {
|
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)) {
|
if (StringUtils.isEmpty(encoding)) {
|
||||||
encoding = StandardCharsets.UTF_8.name();
|
encoding = StandardCharsets.UTF_8.name();
|
||||||
}
|
}
|
||||||
@ -114,7 +134,7 @@ public class WebUtils {
|
|||||||
*
|
*
|
||||||
* @param request HttpServletRequest
|
* @param request HttpServletRequest
|
||||||
* @return the value of the request header "user-agent", or the value of the request header "client-version" if the
|
* @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) {
|
public static String getUserAgent(HttpServletRequest request) {
|
||||||
String userAgent = request.getHeader(HttpHeaderConsts.USER_AGENT_HEADER);
|
String userAgent = request.getHeader(HttpHeaderConsts.USER_AGENT_HEADER);
|
||||||
@ -144,8 +164,8 @@ public class WebUtils {
|
|||||||
* Handle file upload operations.
|
* Handle file upload operations.
|
||||||
*
|
*
|
||||||
* @param multipartFile file
|
* @param multipartFile file
|
||||||
* @param consumer post processor
|
* @param consumer post processor
|
||||||
* @param response {@link DeferredResult}
|
* @param response {@link DeferredResult}
|
||||||
*/
|
*/
|
||||||
public static void onFileUpload(MultipartFile multipartFile, Consumer<File> consumer,
|
public static void onFileUpload(MultipartFile multipartFile, Consumer<File> consumer,
|
||||||
DeferredResult<RestResult<String>> response) {
|
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 BASE_DIR="%BASE_DIR:~0,-5%"
|
||||||
|
|
||||||
set DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/"
|
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 MODE="cluster"
|
||||||
set FUNCTION_MODE="all"
|
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 %*
|
set COMMAND="%JAVA%" %NACOS_JVM_OPTS% %NACOS_OPTS% %NACOS_CONFIG_OPTS% %NACOS_LOG4J_OPTS% nacos.nacos %*
|
||||||
|
|
||||||
rem start nacos command
|
rem start nacos command
|
||||||
%COMMAND%
|
%COMMAND%
|
||||||
|
@ -79,7 +79,7 @@ export JAVA_HOME
|
|||||||
export JAVA="$JAVA_HOME/bin/java"
|
export JAVA="$JAVA_HOME/bin/java"
|
||||||
export BASE_DIR=`cd $(dirname $0)/..; pwd`
|
export BASE_DIR=`cd $(dirname $0)/..; pwd`
|
||||||
export DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/"
|
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
|
# JVM Configuration
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-all</artifactId>
|
<artifactId>nacos-all</artifactId>
|
||||||
<version>1.4.0-SNAPSHOT</version>
|
<version>2.0.0-ALPHA</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</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.GlobalExecutor;
|
||||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@ -38,11 +37,14 @@ public class ServerStatusManager {
|
|||||||
@Resource(name = "consistencyDelegate")
|
@Resource(name = "consistencyDelegate")
|
||||||
private ConsistencyService consistencyService;
|
private ConsistencyService consistencyService;
|
||||||
|
|
||||||
@Autowired
|
private final SwitchDomain switchDomain;
|
||||||
private SwitchDomain switchDomain;
|
|
||||||
|
|
||||||
private ServerStatus serverStatus = ServerStatus.STARTING;
|
private ServerStatus serverStatus = ServerStatus.STARTING;
|
||||||
|
|
||||||
|
public ServerStatusManager(SwitchDomain switchDomain) {
|
||||||
|
this.switchDomain = switchDomain;
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
GlobalExecutor.registerServerStatusUpdater(new ServerStatusUpdater());
|
GlobalExecutor.registerServerStatusUpdater(new ServerStatusUpdater());
|
||||||
|
@ -18,6 +18,7 @@ package com.alibaba.nacos.naming.consistency;
|
|||||||
|
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
import com.alibaba.nacos.naming.pojo.Record;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,11 +26,28 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
*
|
*
|
||||||
* @author nacos
|
* @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 String key;
|
||||||
|
|
||||||
public T value;
|
public T value;
|
||||||
|
|
||||||
public AtomicLong timestamp = new AtomicLong(0L);
|
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.api.exception.NacosException;
|
||||||
import com.alibaba.nacos.naming.consistency.ephemeral.EphemeralConsistencyService;
|
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 com.alibaba.nacos.naming.pojo.Record;
|
||||||
import org.springframework.context.annotation.DependsOn;
|
import org.springframework.context.annotation.DependsOn;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -33,11 +33,11 @@ import org.springframework.stereotype.Service;
|
|||||||
@Service("consistencyDelegate")
|
@Service("consistencyDelegate")
|
||||||
public class DelegateConsistencyServiceImpl implements ConsistencyService {
|
public class DelegateConsistencyServiceImpl implements ConsistencyService {
|
||||||
|
|
||||||
private final PersistentConsistencyService persistentConsistencyService;
|
private final PersistentConsistencyServiceDelegateImpl persistentConsistencyService;
|
||||||
|
|
||||||
private final EphemeralConsistencyService ephemeralConsistencyService;
|
private final EphemeralConsistencyService ephemeralConsistencyService;
|
||||||
|
|
||||||
public DelegateConsistencyServiceImpl(PersistentConsistencyService persistentConsistencyService,
|
public DelegateConsistencyServiceImpl(PersistentConsistencyServiceDelegateImpl persistentConsistencyService,
|
||||||
EphemeralConsistencyService ephemeralConsistencyService) {
|
EphemeralConsistencyService ephemeralConsistencyService) {
|
||||||
this.persistentConsistencyService = persistentConsistencyService;
|
this.persistentConsistencyService = persistentConsistencyService;
|
||||||
this.ephemeralConsistencyService = ephemeralConsistencyService;
|
this.ephemeralConsistencyService = ephemeralConsistencyService;
|
||||||
|
@ -19,6 +19,8 @@ package com.alibaba.nacos.naming.consistency;
|
|||||||
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import static com.alibaba.nacos.naming.misc.UtilsAndCommons.RAFT_CACHE_FILE_PREFIX;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key operations for data.
|
* Key operations for data.
|
||||||
*
|
*
|
||||||
@ -81,8 +83,7 @@ public class KeyBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean matchSwitchKey(String key) {
|
public static boolean matchSwitchKey(String key) {
|
||||||
return key.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME) || key
|
return key.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME);
|
||||||
.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME + UtilsAndCommons.RAFT_CACHE_FILE_SUFFIX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean matchServiceName(String key, String namespaceId, String serviceName) {
|
public static boolean matchServiceName(String key, String namespaceId, String serviceName) {
|
||||||
@ -141,4 +142,8 @@ public class KeyBuilder {
|
|||||||
public static String getServiceName(String key) {
|
public static String getServiceName(String key) {
|
||||||
return key.split(NAMESPACE_KEY_CONNECTOR)[1];
|
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() {
|
public boolean isInitialized() {
|
||||||
return distroProtocol.isLoadCompleted() || !globalConfig.isDataWarmup();
|
return distroProtocol.isInitialized() || !globalConfig.isDataWarmup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Notifier implements Runnable {
|
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.
|
* Base raft event.
|
||||||
*
|
*
|
||||||
|
* @deprecated will remove in 1.4.x
|
||||||
* @author pbting
|
* @author pbting
|
||||||
* @date 2019-07-01 8:46 PM
|
* @date 2019-07-01 8:46 PM
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class BaseRaftEvent extends ApplicationEvent {
|
public abstract class BaseRaftEvent extends ApplicationEvent {
|
||||||
|
|
||||||
private final RaftPeer raftPeer;
|
private final RaftPeer raftPeer;
|
||||||
|
@ -19,9 +19,11 @@ package com.alibaba.nacos.naming.consistency.persistent.raft;
|
|||||||
/**
|
/**
|
||||||
* Leader election finished event.
|
* Leader election finished event.
|
||||||
*
|
*
|
||||||
|
* @deprecated will remove in 1.4.x
|
||||||
* @author pbting
|
* @author pbting
|
||||||
* @date 2019-07-01 8:25 PM
|
* @date 2019-07-01 8:25 PM
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class LeaderElectFinishedEvent extends BaseRaftEvent {
|
public class LeaderElectFinishedEvent extends BaseRaftEvent {
|
||||||
|
|
||||||
public LeaderElectFinishedEvent(Object source, RaftPeer raftPeer, RaftPeer local) {
|
public LeaderElectFinishedEvent(Object source, RaftPeer raftPeer, RaftPeer local) {
|
||||||
|
@ -19,9 +19,11 @@ package com.alibaba.nacos.naming.consistency.persistent.raft;
|
|||||||
/**
|
/**
|
||||||
* Make leader event.
|
* Make leader event.
|
||||||
*
|
*
|
||||||
|
* @deprecated will remove in 1.4.x
|
||||||
* @author pbting
|
* @author pbting
|
||||||
* @date 2019-07-01 8:45 PM
|
* @date 2019-07-01 8:45 PM
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class MakeLeaderEvent extends BaseRaftEvent {
|
public class MakeLeaderEvent extends BaseRaftEvent {
|
||||||
|
|
||||||
public MakeLeaderEvent(Object source, RaftPeer raftPeer, RaftPeer local) {
|
public MakeLeaderEvent(Object source, RaftPeer raftPeer, RaftPeer local) {
|
||||||
|
@ -17,39 +17,70 @@
|
|||||||
package com.alibaba.nacos.naming.consistency.persistent.raft;
|
package com.alibaba.nacos.naming.consistency.persistent.raft;
|
||||||
|
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
|
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||||
import com.alibaba.nacos.naming.cluster.ServerStatus;
|
import com.alibaba.nacos.naming.cluster.ServerStatus;
|
||||||
import com.alibaba.nacos.naming.consistency.Datum;
|
import com.alibaba.nacos.naming.consistency.Datum;
|
||||||
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
import com.alibaba.nacos.naming.consistency.RecordListener;
|
||||||
|
import com.alibaba.nacos.naming.consistency.persistent.ClusterVersionJudgement;
|
||||||
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
|
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
|
||||||
import com.alibaba.nacos.naming.misc.Loggers;
|
import com.alibaba.nacos.naming.misc.Loggers;
|
||||||
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
import com.alibaba.nacos.naming.misc.SwitchDomain;
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
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.context.annotation.DependsOn;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use simplified Raft protocol to maintain the consistency status of Nacos cluster.
|
* Use simplified Raft protocol to maintain the consistency status of Nacos cluster.
|
||||||
*
|
*
|
||||||
* @author nkorange
|
* @author nkorange
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
|
* @deprecated will remove in 1.4.x
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@DependsOn("ProtocolManager")
|
@DependsOn("ProtocolManager")
|
||||||
@Service
|
@Service
|
||||||
public class RaftConsistencyServiceImpl implements PersistentConsistencyService {
|
public class RaftConsistencyServiceImpl implements PersistentConsistencyService {
|
||||||
|
|
||||||
@Autowired
|
private final RaftCore raftCore;
|
||||||
private RaftCore raftCore;
|
|
||||||
|
|
||||||
@Autowired
|
private final RaftPeerSet peers;
|
||||||
private RaftPeerSet peers;
|
|
||||||
|
|
||||||
@Autowired
|
private final SwitchDomain switchDomain;
|
||||||
private 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
|
@Override
|
||||||
public void put(String key, Record value) throws NacosException {
|
public void put(String key, Record value) throws NacosException {
|
||||||
|
checkIsStopWork();
|
||||||
try {
|
try {
|
||||||
raftCore.signalPublish(key, value);
|
raftCore.signalPublish(key, value);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -61,16 +92,14 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(String key) throws NacosException {
|
public void remove(String key) throws NacosException {
|
||||||
|
checkIsStopWork();
|
||||||
try {
|
try {
|
||||||
if (KeyBuilder.matchInstanceListKey(key) && !raftCore.isLeader()) {
|
if (KeyBuilder.matchInstanceListKey(key) && !raftCore.isLeader()) {
|
||||||
Datum datum = new Datum();
|
raftCore.onDelete(key, peers.getLeader());
|
||||||
datum.key = key;
|
} else {
|
||||||
raftCore.onDelete(datum.key, peers.getLeader());
|
raftCore.signalDelete(key);
|
||||||
raftCore.unlistenAll(key);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
raftCore.signalDelete(key);
|
raftCore.unListenAll(key);
|
||||||
raftCore.unlistenAll(key);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Loggers.RAFT.error("Raft remove failed.", e);
|
Loggers.RAFT.error("Raft remove failed.", e);
|
||||||
throw new NacosException(NacosException.SERVER_ERROR, "Raft remove failed, key:" + key, e);
|
throw new NacosException(NacosException.SERVER_ERROR, "Raft remove failed, key:" + key, e);
|
||||||
@ -79,6 +108,7 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Datum get(String key) throws NacosException {
|
public Datum get(String key) throws NacosException {
|
||||||
|
checkIsStopWork();
|
||||||
return raftCore.getDatum(key);
|
return raftCore.getDatum(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,4 +160,10 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
|
|||||||
"Raft onRemove failed, datum:" + datum + ", source: " + source, e);
|
"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;
|
package com.alibaba.nacos.naming.consistency.persistent.raft;
|
||||||
|
|
||||||
import com.alibaba.nacos.common.executor.ExecutorFactory;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
import com.alibaba.nacos.common.executor.NameThreadFactory;
|
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||||
import com.alibaba.nacos.common.http.Callback;
|
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.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.common.utils.JacksonUtils;
|
||||||
import com.alibaba.nacos.sys.utils.ApplicationUtils;
|
|
||||||
import com.alibaba.nacos.consistency.DataOperation;
|
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.Datum;
|
||||||
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
import com.alibaba.nacos.naming.consistency.KeyBuilder;
|
||||||
import com.alibaba.nacos.naming.consistency.RecordListener;
|
import com.alibaba.nacos.naming.consistency.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.Instances;
|
||||||
import com.alibaba.nacos.naming.core.Service;
|
import com.alibaba.nacos.naming.core.Service;
|
||||||
import com.alibaba.nacos.naming.misc.GlobalConfig;
|
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.misc.UtilsAndCommons;
|
||||||
import com.alibaba.nacos.naming.monitor.MetricsMonitor;
|
import com.alibaba.nacos.naming.monitor.MetricsMonitor;
|
||||||
import com.alibaba.nacos.naming.pojo.Record;
|
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.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
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.context.annotation.DependsOn;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -63,13 +66,10 @@ import java.util.Arrays;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
@ -79,10 +79,12 @@ import java.util.zip.GZIPOutputStream;
|
|||||||
* Raft core code.
|
* Raft core code.
|
||||||
*
|
*
|
||||||
* @author nacos
|
* @author nacos
|
||||||
|
* @deprecated will remove in 1.4.x
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@DependsOn("ProtocolManager")
|
@DependsOn("ProtocolManager")
|
||||||
@Component
|
@Component
|
||||||
public class RaftCore {
|
public class RaftCore implements Closeable {
|
||||||
|
|
||||||
public static final String API_VOTE = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/vote";
|
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";
|
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 Lock OPERATE_LOCK = new ReentrantLock();
|
||||||
|
|
||||||
public static final int PUBLISH_TERM_INCREASE_COUNT = 100;
|
public static final int PUBLISH_TERM_INCREASE_COUNT = 100;
|
||||||
@ -112,25 +110,42 @@ public class RaftCore {
|
|||||||
|
|
||||||
private volatile ConcurrentMap<String, Datum> datums = new ConcurrentHashMap<>();
|
private volatile ConcurrentMap<String, Datum> datums = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RaftPeerSet peers;
|
private RaftPeerSet peers;
|
||||||
|
|
||||||
@Autowired
|
private final SwitchDomain switchDomain;
|
||||||
private SwitchDomain switchDomain;
|
|
||||||
|
|
||||||
@Autowired
|
private final GlobalConfig globalConfig;
|
||||||
private GlobalConfig globalConfig;
|
|
||||||
|
|
||||||
@Autowired
|
private final RaftProxy raftProxy;
|
||||||
private RaftProxy raftProxy;
|
|
||||||
|
|
||||||
@Autowired
|
private final RaftStore raftStore;
|
||||||
private 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 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.
|
* Init raft core.
|
||||||
*
|
*
|
||||||
@ -138,11 +153,7 @@ public class RaftCore {
|
|||||||
*/
|
*/
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
|
|
||||||
Loggers.RAFT.info("initializing Raft sub-system");
|
Loggers.RAFT.info("initializing Raft sub-system");
|
||||||
|
|
||||||
executor.submit(notifier);
|
|
||||||
|
|
||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
|
|
||||||
raftStore.loadDatums(notifier, datums);
|
raftStore.loadDatums(notifier, datums);
|
||||||
@ -151,26 +162,32 @@ public class RaftCore {
|
|||||||
|
|
||||||
Loggers.RAFT.info("cache loaded, datum count: {}, current term: {}", datums.size(), peers.getTerm());
|
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;
|
initialized = true;
|
||||||
|
|
||||||
Loggers.RAFT.info("finish to load data from disk, cost: {} ms.", (System.currentTimeMillis() - start));
|
Loggers.RAFT.info("finish to load data from disk, cost: {} ms.", (System.currentTimeMillis() - start));
|
||||||
|
|
||||||
GlobalExecutor.registerMasterElection(new MasterElection());
|
masterTask = GlobalExecutor.registerMasterElection(new MasterElection());
|
||||||
GlobalExecutor.registerHeartbeat(new HeartBeat());
|
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: {}",
|
Loggers.RAFT.info("timer started: leader timeout ms: {}, heart-beat timeout ms: {}",
|
||||||
GlobalExecutor.LEADER_TIMEOUT_MS, GlobalExecutor.HEARTBEAT_INTERVAL_MS);
|
GlobalExecutor.LEADER_TIMEOUT_MS, GlobalExecutor.HEARTBEAT_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, List<RecordListener>> getListeners() {
|
public Map<String, ConcurrentHashSet<RecordListener>> getListeners() {
|
||||||
return listeners;
|
return notifier.getListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,7 +198,9 @@ public class RaftCore {
|
|||||||
* @throws Exception any exception during publish
|
* @throws Exception any exception during publish
|
||||||
*/
|
*/
|
||||||
public void signalPublish(String key, Record value) throws Exception {
|
public void signalPublish(String key, Record value) throws Exception {
|
||||||
|
if (stopWork) {
|
||||||
|
throw new IllegalStateException("old raft protocol already stop work");
|
||||||
|
}
|
||||||
if (!isLeader()) {
|
if (!isLeader()) {
|
||||||
ObjectNode params = JacksonUtils.createEmptyJsonNode();
|
ObjectNode params = JacksonUtils.createEmptyJsonNode();
|
||||||
params.put("key", key);
|
params.put("key", key);
|
||||||
@ -195,8 +214,8 @@ public class RaftCore {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OPERATE_LOCK.lock();
|
||||||
try {
|
try {
|
||||||
OPERATE_LOCK.lock();
|
|
||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
final Datum datum = new Datum();
|
final Datum datum = new Datum();
|
||||||
datum.key = key;
|
datum.key = key;
|
||||||
@ -267,7 +286,9 @@ public class RaftCore {
|
|||||||
* @throws Exception any exception during delete
|
* @throws Exception any exception during delete
|
||||||
*/
|
*/
|
||||||
public void signalDelete(final String key) throws Exception {
|
public void signalDelete(final String key) throws Exception {
|
||||||
|
if (stopWork) {
|
||||||
|
throw new IllegalStateException("old raft protocol already stop work");
|
||||||
|
}
|
||||||
OPERATE_LOCK.lock();
|
OPERATE_LOCK.lock();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -328,6 +349,9 @@ public class RaftCore {
|
|||||||
* @throws Exception any exception during publish
|
* @throws Exception any exception during publish
|
||||||
*/
|
*/
|
||||||
public void onPublish(Datum datum, RaftPeer source) throws Exception {
|
public void onPublish(Datum datum, RaftPeer source) throws Exception {
|
||||||
|
if (stopWork) {
|
||||||
|
throw new IllegalStateException("old raft protocol already stop work");
|
||||||
|
}
|
||||||
RaftPeer local = peers.local();
|
RaftPeer local = peers.local();
|
||||||
if (datum.value == null) {
|
if (datum.value == null) {
|
||||||
Loggers.RAFT.warn("received empty datum");
|
Loggers.RAFT.warn("received empty datum");
|
||||||
@ -369,9 +393,7 @@ public class RaftCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
raftStore.updateTerm(local.term.get());
|
raftStore.updateTerm(local.term.get());
|
||||||
|
NotifyCenter.publishEvent(ValueChangeEvent.builder().key(datum.key).action(DataOperation.CHANGE).build());
|
||||||
notifier.addTask(datum.key, DataOperation.CHANGE);
|
|
||||||
|
|
||||||
Loggers.RAFT.info("data added/updated, key={}, term={}", datum.key, local.term);
|
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
|
* @throws Exception any exception during delete
|
||||||
*/
|
*/
|
||||||
public void onDelete(String datumKey, RaftPeer source) throws Exception {
|
public void onDelete(String datumKey, RaftPeer source) throws Exception {
|
||||||
|
if (stopWork) {
|
||||||
|
throw new IllegalStateException("old raft protocol already stop work");
|
||||||
|
}
|
||||||
RaftPeer local = peers.local();
|
RaftPeer local = peers.local();
|
||||||
|
|
||||||
if (!peers.isLeader(source.ip)) {
|
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 {
|
public class MasterElection implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
if (stopWork) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!peers.isReady()) {
|
if (!peers.isReady()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -508,6 +550,9 @@ public class RaftCore {
|
|||||||
* @return self-peer information
|
* @return self-peer information
|
||||||
*/
|
*/
|
||||||
public synchronized RaftPeer receivedVote(RaftPeer remote) {
|
public synchronized RaftPeer receivedVote(RaftPeer remote) {
|
||||||
|
if (stopWork) {
|
||||||
|
throw new IllegalStateException("old raft protocol already stop work");
|
||||||
|
}
|
||||||
if (!peers.contains(remote)) {
|
if (!peers.contains(remote)) {
|
||||||
throw new IllegalStateException("can not find peer: " + remote.ip);
|
throw new IllegalStateException("can not find peer: " + remote.ip);
|
||||||
}
|
}
|
||||||
@ -540,7 +585,9 @@ public class RaftCore {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
if (stopWork) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!peers.isReady()) {
|
if (!peers.isReady()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -578,7 +625,7 @@ public class RaftCore {
|
|||||||
ArrayNode array = JacksonUtils.createEmptyArrayNode();
|
ArrayNode array = JacksonUtils.createEmptyArrayNode();
|
||||||
|
|
||||||
if (switchDomain.isSendBeatOnly()) {
|
if (switchDomain.isSendBeatOnly()) {
|
||||||
Loggers.RAFT.info("[SEND-BEAT-ONLY] {}", String.valueOf(switchDomain.isSendBeatOnly()));
|
Loggers.RAFT.info("[SEND-BEAT-ONLY] {}", switchDomain.isSendBeatOnly());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!switchDomain.isSendBeatOnly()) {
|
if (!switchDomain.isSendBeatOnly()) {
|
||||||
@ -667,6 +714,9 @@ public class RaftCore {
|
|||||||
* @throws Exception any exception during handle
|
* @throws Exception any exception during handle
|
||||||
*/
|
*/
|
||||||
public RaftPeer receivedBeat(JsonNode beat) throws Exception {
|
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 local = peers.local();
|
||||||
final RaftPeer remote = new RaftPeer();
|
final RaftPeer remote = new RaftPeer();
|
||||||
JsonNode peer = beat.get("peer");
|
JsonNode peer = beat.get("peer");
|
||||||
@ -767,8 +817,10 @@ public class RaftCore {
|
|||||||
processedCount, beatDatums.size(), datums.size());
|
processedCount, beatDatums.size(), datums.size());
|
||||||
|
|
||||||
// update datum entry
|
// update datum entry
|
||||||
String url = buildUrl(remote.ip, API_GET) + "?keys=" + URLEncoder.encode(keys, "UTF-8");
|
String url = buildUrl(remote.ip, API_GET);
|
||||||
HttpClient.asyncHttpGet(url, null, null, new Callback<String>() {
|
Map<String, String> queryParam = new HashMap<>(1);
|
||||||
|
queryParam.put("keys", URLEncoder.encode(keys, "UTF-8"));
|
||||||
|
HttpClient.asyncHttpGet(url, null, queryParam, new Callback<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(RestResult<String> result) {
|
public void onReceive(RestResult<String> result) {
|
||||||
if (!result.ok()) {
|
if (!result.ok()) {
|
||||||
@ -821,7 +873,7 @@ public class RaftCore {
|
|||||||
raftStore.write(newDatum);
|
raftStore.write(newDatum);
|
||||||
|
|
||||||
datums.put(newDatum.key, newDatum);
|
datums.put(newDatum.key, newDatum);
|
||||||
notifier.addTask(newDatum.key, DataOperation.CHANGE);
|
notifier.notify(newDatum.key, DataOperation.CHANGE, newDatum.value);
|
||||||
|
|
||||||
local.resetLeaderDue();
|
local.resetLeaderDue();
|
||||||
|
|
||||||
@ -900,21 +952,9 @@ public class RaftCore {
|
|||||||
* @param listener new listener
|
* @param listener new listener
|
||||||
*/
|
*/
|
||||||
public void listen(String key, RecordListener listener) {
|
public void listen(String key, RecordListener listener) {
|
||||||
|
notifier.registerListener(key, listener);
|
||||||
List<RecordListener> listenerList = listeners.get(key);
|
|
||||||
if (listenerList != null && listenerList.contains(listener)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listenerList == null) {
|
|
||||||
listenerList = new CopyOnWriteArrayList<>();
|
|
||||||
listeners.put(key, listenerList);
|
|
||||||
}
|
|
||||||
|
|
||||||
Loggers.RAFT.info("add listener: {}", key);
|
Loggers.RAFT.info("add listener: {}", key);
|
||||||
|
|
||||||
listenerList.add(listener);
|
|
||||||
|
|
||||||
// if data present, notify immediately
|
// if data present, notify immediately
|
||||||
for (Datum datum : datums.values()) {
|
for (Datum datum : datums.values()) {
|
||||||
if (!listener.interests(datum.key)) {
|
if (!listener.interests(datum.key)) {
|
||||||
@ -936,22 +976,11 @@ public class RaftCore {
|
|||||||
* @param listener listener
|
* @param listener listener
|
||||||
*/
|
*/
|
||||||
public void unListen(String key, RecordListener listener) {
|
public void unListen(String key, RecordListener listener) {
|
||||||
|
notifier.deregisterListener(key, 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlistenAll(String key) {
|
public void unListenAll(String key) {
|
||||||
listeners.remove(key);
|
notifier.deregisterAllListener(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTerm(long term) {
|
public void setTerm(long term) {
|
||||||
@ -1006,7 +1035,7 @@ public class RaftCore {
|
|||||||
|
|
||||||
public void addDatum(Datum datum) {
|
public void addDatum(Datum datum) {
|
||||||
datums.put(datum.key, 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);
|
raftStore.delete(deleted);
|
||||||
Loggers.RAFT.info("datum deleted, key: {}", key);
|
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) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
Loggers.RAFT.warn("datum key decode failed: {}", key);
|
Loggers.RAFT.warn("datum key decode failed: {}", key);
|
||||||
}
|
}
|
||||||
@ -1045,115 +1076,9 @@ public class RaftCore {
|
|||||||
return initialized || !globalConfig.isDataWarmup();
|
return initialized || !globalConfig.isDataWarmup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public int getNotifyTaskCount() {
|
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.common.utils.JacksonUtils;
|
||||||
import com.alibaba.nacos.core.cluster.Member;
|
import com.alibaba.nacos.core.cluster.Member;
|
||||||
import com.alibaba.nacos.core.cluster.ServerMemberManager;
|
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.ApplicationEvent;
|
||||||
import org.springframework.context.event.SmartApplicationListener;
|
import org.springframework.context.event.SmartApplicationListener;
|
||||||
import org.springframework.stereotype.Component;
|
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.
|
* 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>
|
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Component
|
@Component
|
||||||
public class RaftListener implements SmartApplicationListener {
|
public class RaftListener implements SmartApplicationListener {
|
||||||
|
|
||||||
private static final String GROUP = "naming";
|
private static final String GROUP = "naming";
|
||||||
|
|
||||||
@Autowired
|
private final ServerMemberManager memberManager;
|
||||||
private 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
|
@Override
|
||||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||||
boolean a = BaseRaftEvent.class.isAssignableFrom(eventType);
|
return BaseRaftEvent.class.isAssignableFrom(eventType);
|
||||||
return a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(ApplicationEvent event) {
|
public void onApplicationEvent(ApplicationEvent event) {
|
||||||
if (event instanceof BaseRaftEvent) {
|
if (event instanceof BaseRaftEvent && !stopUpdate) {
|
||||||
BaseRaftEvent raftEvent = (BaseRaftEvent) event;
|
BaseRaftEvent raftEvent = (BaseRaftEvent) event;
|
||||||
RaftPeer local = raftEvent.getLocal();
|
RaftPeer local = raftEvent.getLocal();
|
||||||
String json = JacksonUtils.toJson(local);
|
String json = JacksonUtils.toJson(local);
|
||||||
|
@ -26,8 +26,10 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
/**
|
/**
|
||||||
* Raft peer.
|
* Raft peer.
|
||||||
*
|
*
|
||||||
|
* @deprecated will remove in 1.4.x
|
||||||
* @author nacos
|
* @author nacos
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class RaftPeer {
|
public class RaftPeer {
|
||||||
|
|
||||||
public String ip;
|
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