Merge pull request #902 from alibaba/feature_naming_group

Feature naming group
This commit is contained in:
Fury Zhu 2019-03-14 17:23:03 +08:00 committed by GitHub
commit e4255e1281
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
201 changed files with 10849 additions and 8444 deletions

View File

@ -16,7 +16,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.api.cmdb.pojo;
import java.util.Map;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public class Entity {

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.api.cmdb.pojo;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public class EntityEvent {

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.api.cmdb.pojo;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public enum EntityEventType {

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.api.cmdb.pojo;
import java.util.Set;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public class Label {

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.api.cmdb.pojo;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public enum PreservedEntityTypes {

View File

@ -26,7 +26,7 @@ import java.util.Set;
/**
* Service to visit CMDB store
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public interface CmdbService {

View File

@ -22,8 +22,6 @@ package com.alibaba.nacos.api.common;
*/
public class Constants {
public static final String CLIENT_VERSION_HEADER = "Client-Version";
public static final String CLIENT_VERSION = "3.0.0";
public static int DATA_IN_BODY_VERSION = 204;
@ -121,11 +119,13 @@ public class Constants {
public static final int NAMING_INSTANCE_ID_SEG_COUNT = 4;
public static final String NAMING_HTTP_HEADER_SPILIER = "\\|";
public static final String NAMING_DEFAULT_CLUSTER_NAME = "DEFAULT";
public static final String DEFAULT_CLUSTER_NAME = "DEFAULT";
public static final String REQUEST_PARAM_NAMESPACE_ID = "namespaceId";
public static final String REQUEST_PARAM_DEFAULT_NAMESPACE_ID = "public";
public static final String REQUEST_PARAM_SERVICE_NAME = "serviceName";
public static final String REQUEST_PARAM_GROUP = "group";
public static final String REQUEST_PARAM_DEFAULT_GROUP = "DEFAULT_GROUP";
public static final String DEFAULT_NAMESPACE_ID = "public";
public static final int WRITE_REDIRECT_CODE = 307;
public static final String SERVICE_INFO_SPLITER = "@@";
public static final String NULL_STRING = "null";
}

View File

@ -0,0 +1,34 @@
/*
* 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.api.naming;
/**
* Common parameters for service discovery
*
* @author nkorange
* @since 1.0.0
*/
public class CommonParams {
public static final String SERVICE_NAME = "serviceName";
public static final String CLUSTER_NAME = "clusterName";
public static final String NAMESPACE_ID = "namespaceId";
public static final String GROUP_NAME = "groupName";
}

View File

@ -23,7 +23,7 @@ import com.alibaba.nacos.api.exception.NacosException;
/**
* Naming Factory
*
* @author dungu.zpf
* @author nkorange
*/
public class NamingFactory {

View File

@ -27,7 +27,7 @@ import java.util.List;
/**
* Naming Service
*
* @author dungu.zpf
* @author nkorange
*/
public interface NamingService {
@ -41,6 +41,17 @@ public interface NamingService {
*/
void registerInstance(String serviceName, String ip, int port) throws NacosException;
/**
* register a instance to service
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @throws NacosException
*/
void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException;
/**
* register a instance to service with specified cluster name
*
@ -52,6 +63,18 @@ public interface NamingService {
*/
void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;
/**
* register a instance to service with specified cluster name
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @param clusterName instance cluster name
* @throws NacosException
*/
void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException;
/**
* register a instance to service with specified instance properties
*
@ -61,6 +84,16 @@ public interface NamingService {
*/
void registerInstance(String serviceName, Instance instance) throws NacosException;
/**
* register a instance to service with specified instance properties
*
* @param serviceName name of service
* @param groupName group of service
* @param instance instance to register
* @throws NacosException
*/
void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException;
/**
* deregister instance from a service
*
@ -71,6 +104,17 @@ public interface NamingService {
*/
void deregisterInstance(String serviceName, String ip, int port) throws NacosException;
/**
* deregister instance from a service
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @throws NacosException
*/
void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException;
/**
* deregister instance with specified cluster name from a service
*
@ -82,6 +126,18 @@ public interface NamingService {
*/
void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;
/**
* deregister instance with specified cluster name from a service
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @param clusterName instance cluster name
* @throws NacosException
*/
void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException;
/**
* get all instances of a service
*
@ -91,6 +147,16 @@ public interface NamingService {
*/
List<Instance> getAllInstances(String serviceName) throws NacosException;
/**
* get all instances of a service
*
* @param serviceName name of service
* @param groupName group of service
* @return A list of instance
* @throws NacosException
*/
List<Instance> getAllInstances(String serviceName, String groupName) throws NacosException;
/**
* Get all instances of a service
*
@ -101,6 +167,17 @@ public interface NamingService {
*/
List<Instance> getAllInstances(String serviceName, boolean subscribe) throws NacosException;
/**
* Get all instances of a service
*
* @param serviceName name of service
* @param groupName group of service
* @param subscribe if subscribe the service
* @return A list of instance
* @throws NacosException
*/
List<Instance> getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException;
/**
* Get all instances within specified clusters of a service
*
@ -111,6 +188,17 @@ public interface NamingService {
*/
List<Instance> getAllInstances(String serviceName, List<String> clusters) throws NacosException;
/**
* Get all instances within specified clusters of a service
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @return A list of qualified instance
* @throws NacosException
*/
List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters) throws NacosException;
/**
* Get all instances within specified clusters of a service
*
@ -122,6 +210,18 @@ public interface NamingService {
*/
List<Instance> getAllInstances(String serviceName, List<String> clusters, boolean subscribe) throws NacosException;
/**
* Get all instances within specified clusters of a service
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param subscribe if subscribe the service
* @return A list of qualified instance
* @throws NacosException
*/
List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException;
/**
* Get qualified instances of service
*
@ -132,6 +232,17 @@ public interface NamingService {
*/
List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException;
/**
* Get qualified instances of service
*
* @param serviceName name of service
* @param groupName group of service
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @return A qualified list of instance
* @throws NacosException
*/
List<Instance> selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException;
/**
* Get qualified instances of service
*
@ -143,6 +254,18 @@ public interface NamingService {
*/
List<Instance> selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException;
/**
* Get qualified instances of service
*
* @param serviceName name of service
* @param groupName group of service
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @param subscribe if subscribe the service
* @return A qualified list of instance
* @throws NacosException
*/
List<Instance> selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException;
/**
* Get qualified instances within specified clusters of service
*
@ -154,6 +277,18 @@ public interface NamingService {
*/
List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy) throws NacosException;
/**
* Get qualified instances within specified clusters of service
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @return A qualified list of instance
* @throws NacosException
*/
List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy) throws NacosException;
/**
* Get qualified instances within specified clusters of service
*
@ -166,6 +301,19 @@ public interface NamingService {
*/
List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException;
/**
* Get qualified instances within specified clusters of service
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @param subscribe if subscribe the service
* @return A qualified list of instance
* @throws NacosException
*/
List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy
*
@ -175,6 +323,16 @@ public interface NamingService {
*/
Instance selectOneHealthyInstance(String serviceName) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy
*
* @param serviceName name of service
* @param groupName group of service
* @return qualified instance
* @throws NacosException
*/
Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException;
/**
* select one healthy instance of service using predefined load balance strategy
*
@ -185,6 +343,17 @@ public interface NamingService {
*/
Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException;
/**
* select one healthy instance of service using predefined load balance strategy
*
* @param serviceName name of service
* @param groupName group of service
* @param subscribe if subscribe the service
* @return qualified instance
* @throws NacosException
*/
Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy
*
@ -195,6 +364,17 @@ public interface NamingService {
*/
Instance selectOneHealthyInstance(String serviceName, List<String> clusters) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters a list of clusters should the instance belongs to
* @return qualified instance
* @throws NacosException
*/
Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy
*
@ -206,6 +386,18 @@ public interface NamingService {
*/
Instance selectOneHealthyInstance(String serviceName, List<String> clusters, boolean subscribe) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters a list of clusters should the instance belongs to
* @param subscribe if subscribe the service
* @return qualified instance
* @throws NacosException
*/
Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException;
/**
* Subscribe service to receive events of instances alteration
*
@ -216,7 +408,17 @@ public interface NamingService {
void subscribe(String serviceName, EventListener listener) throws NacosException;
/**
* subscribe service to receive events of instances alteration
* Subscribe service to receive events of instances alteration
*
* @param serviceName name of service
* @param groupName group of service
* @param listener event listener
* @throws NacosException
*/
void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException;
/**
* Subscribe service to receive events of instances alteration
*
* @param serviceName name of service
* @param clusters list of cluster
@ -226,7 +428,18 @@ public interface NamingService {
void subscribe(String serviceName, List<String> clusters, EventListener listener) throws NacosException;
/**
* unsubscribe event listener of service
* Subscribe service to receive events of instances alteration
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param listener event listener
* @throws NacosException
*/
void subscribe(String serviceName, String groupName, List<String> clusters, EventListener listener) throws NacosException;
/**
* Unsubscribe event listener of service
*
* @param serviceName name of service
* @param listener event listener
@ -238,6 +451,16 @@ public interface NamingService {
* unsubscribe event listener of service
*
* @param serviceName name of service
* @param groupName group of service
* @param listener event listener
* @throws NacosException
*/
void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException;
/**
* Unsubscribe event listener of service
*
* @param serviceName name of service
* @param clusters list of cluster
* @param listener event listener
* @throws NacosException
@ -245,7 +468,18 @@ public interface NamingService {
void unsubscribe(String serviceName, List<String> clusters, EventListener listener) throws NacosException;
/**
* get all service names from server
* Unsubscribe event listener of service
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param listener event listener
* @throws NacosException
*/
void unsubscribe(String serviceName, String groupName, List<String> clusters, EventListener listener) throws NacosException;
/**
* Get all service names from server
*
* @param pageNo page index
* @param pageSize page size
@ -255,7 +489,18 @@ public interface NamingService {
ListView<String> getServicesOfServer(int pageNo, int pageSize) throws NacosException;
/**
* Get all subscribed services of current client
* Get all service names from server
*
* @param pageNo page index
* @param pageSize page size
* @param groupName group name
* @return list of service names
* @throws NacosException
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException;
/**
* Get all service names from server with selector
*
* @param pageNo page index
* @param pageSize page size
@ -266,6 +511,18 @@ public interface NamingService {
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException;
/**
* Get all service names from server with selector
*
* @param pageNo page index
* @param pageSize page size
* @param groupName group name
* @param selector selector to filter the resource
* @return list of service names
* @throws NacosException
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException;
/**
* Get all subscribed services of current client
*

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.api.naming.listener;
/**
* Event Interface
*
* @author dungu.zpf
* @author nkorange
*/
public interface Event {
}

View File

@ -22,7 +22,7 @@ import com.alibaba.nacos.api.naming.pojo.Instance;
/**
* Naming Event
*
* @author dungu.zpf
* @author nkorange
*/
public class NamingEvent implements Event {

View File

@ -25,7 +25,7 @@ import java.util.Map;
import java.util.Objects;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public abstract class AbstractHealthChecker implements Cloneable {

View File

@ -21,7 +21,7 @@ import java.util.Map;
/**
* Cluster
*
* @author dungu.zpf
* @author nkorange
*/
public class Cluster {

View File

@ -24,7 +24,7 @@ import java.util.Map;
/**
* Instance
*
* @author dungu.zpf
* @author nkorange
*/
public class Instance {
@ -51,11 +51,20 @@ public class Instance {
/**
* instance health status
*/
@JSONField(name = "valid")
private boolean healthy = true;
/**
* If instance is enabled to accept request
*/
private boolean enabled = true;
/**
* If instance is ephemeral
*
* @since 1.0.0
*/
private boolean ephemeral = true;
/**
* cluster information of instance
*/
@ -147,6 +156,14 @@ public class Instance {
this.enabled = enabled;
}
public boolean isEphemeral() {
return ephemeral;
}
public void setEphemeral(boolean ephemeral) {
this.ephemeral = ephemeral;
}
@Override
public String toString() {
return JSON.toJSONString(this);
@ -162,7 +179,7 @@ public class Instance {
return false;
}
Instance host = (Instance)obj;
Instance host = (Instance) obj;
return strEquals(toString(), host.toString());
}

View File

@ -22,7 +22,7 @@ import java.util.List;
/**
* ListView
*
* @author dungu.zpf
* @author nkorange
*/
public class ListView<T> {

View File

@ -15,18 +15,20 @@
*/
package com.alibaba.nacos.api.naming.pojo;
import com.alibaba.nacos.api.selector.AbstractSelector;
import java.util.HashMap;
import java.util.Map;
/**
* Service
* Service of Nacos
* <p>
* We introduce a 'service --> cluster --> instance' model, in which service stores a list of clusters,
* which contains a list of instances.
* <p>
* Typically we put some unique properties between instances to service level.
*
* @author dungu.zpf
* @author nkorange
*/
public class Service {
/**
* service name
*/
@ -40,25 +42,18 @@ public class Service {
/**
* application name of this service
*/
private String app;
private String appName;
/**
* Service group which is meant to classify services into different sets.
* Service group to classify services into different sets.
*/
private String group;
/**
* Health check mode.
*/
private String healthCheckMode;
/**
* Selector name of this service
*/
private AbstractSelector selector;
private String groupName;
private Map<String, String> metadata = new HashMap<String, String>();
public Service() {
}
public Service(String name) {
this.name = name;
}
@ -71,14 +66,6 @@ public class Service {
this.name = name;
}
public String getHealthCheckMode() {
return healthCheckMode;
}
public void setHealthCheckMode(String healthCheckMode) {
this.healthCheckMode = healthCheckMode;
}
public float getProtectThreshold() {
return protectThreshold;
}
@ -87,20 +74,20 @@ public class Service {
this.protectThreshold = protectThreshold;
}
public String getApp() {
return app;
public String getAppName() {
return appName;
}
public void setApp(String app) {
this.app = app;
public void setAppName(String appName) {
this.appName = appName;
}
public String getGroup() {
return group;
public String getGroupName() {
return groupName;
}
public void setGroup(String group) {
this.group = group;
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public Map<String, String> getMetadata() {
@ -114,12 +101,4 @@ public class Service {
public void addMetadata(String key, String value) {
this.metadata.put(key, value);
}
public AbstractSelector getSelector() {
return selector;
}
public void setSelector(AbstractSelector selector) {
this.selector = selector;
}
}

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.api.naming.pojo;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.nacos.api.common.Constants;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@ -26,7 +27,7 @@ import java.util.List;
/**
* ServiceInfo
*
* @author dungu.zpf
* @author nkorange
*/
public class ServiceInfo {
@ -34,9 +35,10 @@ public class ServiceInfo {
private String jsonFromServer = EMPTY;
public static final String SPLITER = "@@";
@JSONField(name = "dom")
private String name;
private String groupName;
private String clusters;
private long cacheMillis = 1000L;
@ -67,7 +69,7 @@ public class ServiceInfo {
int clusterIndex = 1;
int serviceNameIndex = 0;
String[] keys = key.split(SPLITER);
String[] keys = key.split(Constants.SERVICE_INFO_SPLITER);
if (keys.length >= maxIndex) {
this.name = keys[serviceNameIndex];
this.clusters = keys[clusterIndex];
@ -105,6 +107,14 @@ public class ServiceInfo {
this.name = name;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setLastRefTime(long lastRefTime) {
this.lastRefTime = lastRefTime;
}
@ -178,13 +188,16 @@ public class ServiceInfo {
@JSONField(serialize = false)
public static ServiceInfo fromKey(String key) {
ServiceInfo serviceInfo = new ServiceInfo();
if (key.contains(SPLITER)) {
serviceInfo.setName(key.split(SPLITER)[0]);
serviceInfo.setClusters(key.split(SPLITER)[1]);
return serviceInfo;
int maxSegCount = 3;
String[] segs = key.split(Constants.SERVICE_INFO_SPLITER);
if (segs.length == maxSegCount -1) {
serviceInfo.setGroupName(segs[0]);
serviceInfo.setName(segs[1]);
} else if (segs.length == maxSegCount) {
serviceInfo.setGroupName(segs[0]);
serviceInfo.setName(segs[1]);
serviceInfo.setClusters(segs[2]);
}
serviceInfo.setName(key);
return serviceInfo;
}
@ -192,7 +205,7 @@ public class ServiceInfo {
public static String getKey(String name, String clusters) {
if (!isEmpty(clusters)) {
return name + SPLITER + clusters;
return name + Constants.SERVICE_INFO_SPLITER + clusters;
}
return name;

View File

@ -0,0 +1,43 @@
/*
* 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.api.naming.utils;
import com.alibaba.nacos.api.common.Constants;
/**
* @author nkorange
* @since 1.0.0
*/
public class NamingUtils {
public static String getGroupedName(String serviceName, String groupName) {
return groupName + Constants.SERVICE_INFO_SPLITER + serviceName;
}
public static String getServiceName(String serviceNameWithGroup) {
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
return serviceNameWithGroup;
}
return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[1];
}
public static String getGroupName(String serviceNameWithGroup) {
if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) {
return Constants.DEFAULT_GROUP;
}
return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[0];
}
}

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.api.selector;
/**
* Abstract selector that only contains a type
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public abstract class AbstractSelector {

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.api.selector;
/**
* The selector to filter resource with flexible expression.
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public class ExpressionSelector extends AbstractSelector {

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.api.selector;
/**
* The types of selector accepted by Nacos
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public enum SelectorType {

View File

@ -16,7 +16,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -24,6 +24,7 @@ import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.api.naming.utils.NamingUtils;
import com.alibaba.nacos.api.selector.AbstractSelector;
import com.alibaba.nacos.client.identify.CredentialService;
import com.alibaba.nacos.client.naming.beat.BeatInfo;
@ -47,7 +48,7 @@ import java.util.Properties;
import java.util.concurrent.Callable;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
public class NacosNamingService implements NamingService {
@ -249,25 +250,41 @@ public class NacosNamingService implements NamingService {
@Override
public void registerInstance(String serviceName, String ip, int port) throws NacosException {
registerInstance(serviceName, ip, port, Constants.NAMING_DEFAULT_CLUSTER_NAME);
registerInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME);
}
@Override
public void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException {
registerInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME);
}
@Override
public void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException {
registerInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName);
}
@Override
public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException {
Instance instance = new Instance();
instance.setIp(ip);
instance.setPort(port);
instance.setWeight(1.0);
instance.setClusterName(clusterName);
registerInstance(serviceName, instance);
registerInstance(serviceName, groupName, instance);
}
@Override
public void registerInstance(String serviceName, Instance instance) throws NacosException {
registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
}
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
BeatInfo beatInfo = new BeatInfo();
beatInfo.setServiceName(serviceName);
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
@ -275,20 +292,30 @@ public class NacosNamingService implements NamingService {
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
beatReactor.addBeatInfo(serviceName, beatInfo);
beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
serverProxy.registerService(serviceName, instance);
serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}
@Override
public void deregisterInstance(String serviceName, String ip, int port) throws NacosException {
deregisterInstance(serviceName, ip, port, Constants.NAMING_DEFAULT_CLUSTER_NAME);
deregisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME);
}
@Override
public void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException {
deregisterInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME);
}
@Override
public void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException {
beatReactor.removeBeatInfo(serviceName, ip, port);
serverProxy.deregisterService(serviceName, ip, port, clusterName);
deregisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName);
}
@Override
public void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException {
beatReactor.removeBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), ip, port);
serverProxy.deregisterService(NamingUtils.getGroupedName(serviceName, groupName), ip, port, clusterName);
}
@Override
@ -296,25 +323,45 @@ public class NacosNamingService implements NamingService {
return getAllInstances(serviceName, new ArrayList<String>());
}
@Override
public List<Instance> getAllInstances(String serviceName, String groupName) throws NacosException {
return getAllInstances(serviceName, groupName, new ArrayList<String>());
}
@Override
public List<Instance> getAllInstances(String serviceName, boolean subscribe) throws NacosException {
return getAllInstances(serviceName, new ArrayList<String>(), subscribe);
}
@Override
public List<Instance> getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException {
return getAllInstances(serviceName, groupName, new ArrayList<String>(), subscribe);
}
@Override
public List<Instance> getAllInstances(String serviceName, List<String> clusters) throws NacosException {
return getAllInstances(serviceName, clusters, true);
}
@Override
public List<Instance> getAllInstances(String serviceName, List<String> clusters, boolean subscribe) throws
NacosException {
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters) throws NacosException {
return getAllInstances(serviceName, groupName, clusters, true);
}
@Override
public List<Instance> getAllInstances(String serviceName, List<String> clusters, boolean subscribe)
throws NacosException {
return getAllInstances(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe);
}
@Override
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException {
ServiceInfo serviceInfo;
if (subscribe) {
serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","));
serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
} else {
serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(serviceName, StringUtils.join(clusters, ","));
serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
}
List<Instance> list;
if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) {
@ -329,24 +376,46 @@ public class NacosNamingService implements NamingService {
}
@Override
public List<Instance> selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException {
public List<Instance> selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException {
return selectInstances(serviceName, groupName, healthy, true);
}
@Override
public List<Instance> selectInstances(String serviceName, boolean healthy, boolean subscribe)
throws NacosException {
return selectInstances(serviceName, new ArrayList<String>(), healthy, subscribe);
}
@Override
public List<Instance> selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException {
return selectInstances(serviceName, groupName, new ArrayList<String>(), healthy, subscribe);
}
@Override
public List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy)
throws NacosException {
return selectInstances(serviceName, clusters, healthy, true);
}
@Override
public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy) throws NacosException {
return selectInstances(serviceName, groupName, clusters, healthy, true);
}
@Override
public List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy,
boolean subscribe) throws NacosException {
return selectInstances(serviceName, Constants.DEFAULT_GROUP, clusters, healthy, subscribe);
}
@Override
public List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy, boolean subscribe) throws NacosException {
ServiceInfo serviceInfo;
if (subscribe) {
serviceInfo = hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ","));
serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
} else {
serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(serviceName, StringUtils.join(clusters, ","));
serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
}
return selectInstances(serviceInfo, healthy);
}
@ -356,60 +425,109 @@ public class NacosNamingService implements NamingService {
return selectOneHealthyInstance(serviceName, new ArrayList<String>());
}
@Override
public Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException {
return selectOneHealthyInstance(serviceName, groupName, true);
}
@Override
public Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException {
return selectOneHealthyInstance(serviceName, new ArrayList<String>(), subscribe);
}
@Override
public Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException {
return selectOneHealthyInstance(serviceName, groupName, new ArrayList<String>(), subscribe);
}
@Override
public Instance selectOneHealthyInstance(String serviceName, List<String> clusters) throws NacosException {
return selectOneHealthyInstance(serviceName, clusters, true);
}
@Override
public Instance selectOneHealthyInstance(String serviceName, List<String> clusters, boolean subscribe) throws
NacosException {
public Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters) throws NacosException {
return selectOneHealthyInstance(serviceName, groupName, clusters, true);
}
@Override
public Instance selectOneHealthyInstance(String serviceName, List<String> clusters, boolean subscribe)
throws NacosException {
return selectOneHealthyInstance(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe);
}
@Override
public Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException {
if (subscribe) {
return Balancer.RandomByWeight.selectHost(
hostReactor.getServiceInfo(serviceName, StringUtils.join(clusters, ",")));
hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")));
} else {
return Balancer.RandomByWeight.selectHost(
hostReactor.getServiceInfoDirectlyFromServer(serviceName, StringUtils.join(clusters, ",")));
hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")));
}
}
@Override
public void subscribe(String service, EventListener listener) {
eventDispatcher.addListener(hostReactor.getServiceInfo(service, StringUtils.EMPTY), StringUtils.EMPTY,
listener);
public void subscribe(String serviceName, EventListener listener) throws NacosException {
subscribe(serviceName, new ArrayList<String>(), listener);
}
@Override
public void subscribe(String service, List<String> clusters, EventListener listener) {
eventDispatcher.addListener(hostReactor.getServiceInfo(service, StringUtils.join(clusters, ",")),
StringUtils.join(clusters, ","), listener);
public void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException {
subscribe(serviceName, groupName, new ArrayList<String>(), listener);
}
@Override
public void unsubscribe(String service, EventListener listener) {
eventDispatcher.removeListener(service, StringUtils.EMPTY, listener);
public void subscribe(String serviceName, List<String> clusters, EventListener listener) throws NacosException {
subscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener);
}
@Override
public void unsubscribe(String service, List<String> clusters, EventListener listener) {
eventDispatcher.removeListener(service, StringUtils.join(clusters, ","), listener);
public void subscribe(String serviceName, String groupName, List<String> clusters, EventListener listener) throws NacosException {
eventDispatcher.addListener(hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ",")), StringUtils.join(clusters, ","), listener);
}
@Override
public void unsubscribe(String serviceName, EventListener listener) throws NacosException {
unsubscribe(serviceName, new ArrayList<String>(), listener);
}
@Override
public void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException {
unsubscribe(serviceName, groupName, new ArrayList<String>(), listener);
}
@Override
public void unsubscribe(String serviceName, List<String> clusters, EventListener listener) throws NacosException {
unsubscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener);
}
@Override
public void unsubscribe(String serviceName, String groupName, List<String> clusters, EventListener listener) throws NacosException {
eventDispatcher.removeListener(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","), listener);
}
@Override
public ListView<String> getServicesOfServer(int pageNo, int pageSize) throws NacosException {
return serverProxy.getServiceList(pageNo, pageSize);
return serverProxy.getServiceList(pageNo, pageSize, Constants.DEFAULT_GROUP);
}
@Override
public ListView<String> getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws
NacosException {
return serverProxy.getServiceList(pageNo, pageSize, selector);
public ListView<String> getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException {
return getServicesOfServer(pageNo, pageSize, groupName, null);
}
@Override
public ListView<String> getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector)
throws NacosException {
return getServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP, selector);
}
@Override
public ListView<String> getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException {
return serverProxy.getServiceList(pageNo, pageSize, groupName, selector);
}
@Override

View File

@ -34,7 +34,7 @@ import java.util.concurrent.*;
import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class FailoverReactor {

View File

@ -20,7 +20,7 @@ import com.alibaba.fastjson.JSON;
import java.util.Map;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class BeatInfo {
@ -30,7 +30,7 @@ public class BeatInfo {
private String serviceName;
private String cluster;
private Map<String, String> metadata;
private boolean scheduled;
private volatile boolean scheduled;
@Override
public String toString() {

View File

@ -58,20 +58,21 @@ public class BeatReactor {
executorService.schedule(new BeatProcessor(), 0, TimeUnit.MILLISECONDS);
}
public void addBeatInfo(String dom, BeatInfo beatInfo) {
public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
dom2Beat.put(buildKey(dom, beatInfo.getIp(), beatInfo.getPort()), beatInfo);
dom2Beat.put(buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort()), beatInfo);
MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}
public void removeBeatInfo(String dom, String ip, int port) {
NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.", dom, ip, port);
dom2Beat.remove(buildKey(dom, ip, port));
public void removeBeatInfo(String serviceName, String ip, int port) {
NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.", serviceName, ip, port);
dom2Beat.remove(buildKey(serviceName, ip, port));
MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}
public String buildKey(String dom, String ip, int port) {
return dom + Constants.NAMING_INSTANCE_ID_SPLITTER + ip + Constants.NAMING_INSTANCE_ID_SPLITTER + port;
public String buildKey(String serviceName, String ip, int port) {
return serviceName + Constants.NAMING_INSTANCE_ID_SPLITTER
+ ip + Constants.NAMING_INSTANCE_ID_SPLITTER + port;
}
class BeatProcessor implements Runnable {

View File

@ -29,7 +29,7 @@ import java.nio.charset.CharsetDecoder;
import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class ConcurrentDiskUtil {

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.client.naming.cache;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
@ -44,7 +45,6 @@ public class DiskCache {
makeSureCacheDirExists(dir);
File file = new File(dir, dom.getKeyEncoded());
if (!file.exists()) {
// add another !file.exists() to avoid conflicted creating-new-file from multi-instances
@ -93,8 +93,8 @@ public class DiskCache {
String fileName = URLDecoder.decode(file.getName(), "UTF-8");
if (!(fileName.endsWith(ServiceInfo.SPLITER + "meta") || fileName.endsWith(
ServiceInfo.SPLITER + "special-url"))) {
if (!(fileName.endsWith(Constants.SERVICE_INFO_SPLITER + "meta") || fileName.endsWith(
Constants.SERVICE_INFO_SPLITER + "special-url"))) {
ServiceInfo dom = new ServiceInfo(fileName);
List<Instance> ips = new ArrayList<Instance>();
dom.setHosts(ips);

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.client.naming.core;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class ProtectMode {

View File

@ -77,7 +77,7 @@ public class PushReceiver implements Runnable {
PushPacket pushPacket = JSON.parseObject(json, PushPacket.class);
String ack;
if ("dom".equals(pushPacket.type)) {
if ("dom".equals(pushPacket.type) || "service".equals(pushPacket.type)) {
hostReactor.processServiceJSON(pushPacket.data);
// send ack to server

View File

@ -15,6 +15,7 @@
*/
package com.alibaba.nacos.client.naming.net;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.client.naming.utils.IoUtils;
import com.alibaba.nacos.client.naming.utils.StringUtils;
import com.google.common.net.HttpHeaders;
@ -32,7 +33,7 @@ import java.util.zip.GZIPInputStream;
import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class HttpClient {
@ -68,7 +69,7 @@ public class HttpClient {
HttpURLConnection conn = null;
try {
String encodedContent = encodingParams(paramValues, encoding);
url += (null == encodedContent) ? "" : ("?" + encodedContent);
url += (StringUtils.isEmpty(encodedContent)) ? "" : ("?" + encodedContent);
conn = (HttpURLConnection) new URL(url).openConnection();
@ -114,7 +115,8 @@ public class HttpClient {
InputStream inputStream;
if (HttpURLConnection.HTTP_OK == respCode
|| HttpURLConnection.HTTP_NOT_MODIFIED == respCode) {
|| HttpURLConnection.HTTP_NOT_MODIFIED == respCode
|| Constants.WRITE_REDIRECT_CODE == respCode) {
inputStream = conn.getInputStream();
} else {
inputStream = conn.getErrorStream();
@ -172,7 +174,7 @@ public class HttpClient {
private static String encodingParams(Map<String, String> params, String encoding)
throws UnsupportedEncodingException {
if (null == params || params.isEmpty()) {
return null;
return "";
}
params.put("encoding", encoding);

View File

@ -20,8 +20,8 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.SystemPropertyKeyConst;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.CommonParams;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.api.selector.AbstractSelector;
@ -44,7 +44,7 @@ import java.util.concurrent.*;
import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class NamingProxy {
@ -111,7 +111,6 @@ public class NamingProxy {
try {
String urlString = "http://" + endpoint + "/nacos/serverlist";
List<String> headers = builderHeaders();
HttpClient.HttpResult result = HttpClient.httpGet(urlString, headers, null, UtilAndComs.ENCODING);
@ -166,37 +165,39 @@ public class NamingProxy {
}
}
public void registerService(String serviceName, Instance instance) throws NacosException {
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
namespaceId, serviceName, instance);
final Map<String, String> params = new HashMap<String, String>(8);
params.put(Constants.REQUEST_PARAM_NAMESPACE_ID, namespaceId);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put(CommonParams.GROUP_NAME, groupName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("weight", String.valueOf(instance.getWeight()));
params.put("enable", String.valueOf(instance.isEnabled()));
params.put("healthy", String.valueOf(instance.isHealthy()));
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
params.put("metadata", JSON.toJSONString(instance.getMetadata()));
params.put("serviceName", serviceName);
params.put("clusterName", instance.getClusterName());
reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);
}
public void deregisterService(String serviceName, String ip, int port, String cluster) throws NacosException {
public void deregisterService(String serviceName, String ip, int port, String clusterName) throws NacosException {
NAMING_LOGGER.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}:{}@{}",
namespaceId, serviceName, ip, port, cluster);
namespaceId, serviceName, ip, port, clusterName);
final Map<String, String> params = new HashMap<String, String>(8);
params.put(Constants.REQUEST_PARAM_NAMESPACE_ID, namespaceId);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put("ip", ip);
params.put("port", String.valueOf(port));
params.put("serviceName", serviceName);
params.put("cluster", cluster);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put(CommonParams.CLUSTER_NAME, clusterName);
reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.DELETE);
}
@ -205,8 +206,8 @@ public class NamingProxy {
throws NacosException {
final Map<String, String> params = new HashMap<String, String>(8);
params.put(Constants.REQUEST_PARAM_NAMESPACE_ID, namespaceId);
params.put("serviceName", serviceName);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put("clusters", clusters);
params.put("udpPort", String.valueOf(udpPort));
params.put("clientIP", NetUtils.localIP());
@ -220,8 +221,8 @@ public class NamingProxy {
NAMING_LOGGER.info("[BEAT] {} sending beat to server: {}", namespaceId, beatInfo.toString());
Map<String, String> params = new HashMap<String, String>(4);
params.put("beat", JSON.toJSONString(beatInfo));
params.put(Constants.REQUEST_PARAM_NAMESPACE_ID, namespaceId);
params.put("serviceName", beatInfo.getServiceName());
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, beatInfo.getServiceName());
String result = reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/beat", params, HttpMethod.PUT);
JSONObject jsonObject = JSON.parseObject(result);
@ -237,24 +238,26 @@ public class NamingProxy {
public boolean serverHealthy() {
try {
reqAPI(UtilAndComs.NACOS_URL_BASE + "/api/hello", new HashMap<String, String>(2));
String result = reqAPI(UtilAndComs.NACOS_URL_BASE + "/operator/metrics", new HashMap<String, String>(2));
JSONObject json = JSON.parseObject(result);
String serverStatus = json.getString("status");
return "UP".equals(serverStatus);
} catch (Exception e) {
return false;
}
return true;
}
public ListView<String> getServiceList(int pageNo, int pageSize) throws NacosException {
return getServiceList(pageNo, pageSize, null);
public ListView<String> getServiceList(int pageNo, int pageSize, String groupName) throws NacosException {
return getServiceList(pageNo, pageSize, groupName, null);
}
public ListView<String> getServiceList(int pageNo, int pageSize, AbstractSelector selector) throws NacosException {
public ListView<String> getServiceList(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException {
Map<String, String> params = new HashMap<String, String>(4);
params.put("pageNo", String.valueOf(pageNo));
params.put("pageSize", String.valueOf(pageSize));
params.put(Constants.REQUEST_PARAM_NAMESPACE_ID, namespaceId);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.GROUP_NAME, groupName);
if (selector != null) {
switch (SelectorType.valueOf(selector.getType())) {
@ -309,7 +312,6 @@ public class NamingProxy {
long start = System.currentTimeMillis();
long end = 0;
checkSignature(params);
List<String> headers = builderHeaders();
String url;
@ -334,10 +336,6 @@ public class NamingProxy {
return StringUtils.EMPTY;
}
NAMING_LOGGER.error("[CALL-SERVER] failed to req API:" + HttpClient.getPrefix() + curServer
+ api + ". code:"
+ result.code + " msg: " + result.content);
throw new NacosException(NacosException.SERVER_ERROR, "failed to req API:" + HttpClient.getPrefix() + curServer
+ api + ". code:"
+ result.code + " msg: " + result.content);
@ -349,12 +347,14 @@ public class NamingProxy {
public String reqAPI(String api, Map<String, String> params, List<String> servers, String method) {
params.put(Constants.REQUEST_PARAM_NAMESPACE_ID, getNamespaceId());
params.put(CommonParams.NAMESPACE_ID, getNamespaceId());
if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) {
throw new IllegalArgumentException("no server available");
}
Exception exception = new Exception();
if (servers != null && !servers.isEmpty()) {
Random random = new Random(System.currentTimeMillis());
@ -364,25 +364,32 @@ public class NamingProxy {
String server = servers.get(index);
try {
return callServer(api, params, server, method);
} catch (NacosException e) {
exception = e;
NAMING_LOGGER.error("request {} failed.", server, e);
} catch (Exception e) {
NAMING_LOGGER.error("[NA] req api:" + api + " failed, server(" + server, e);
exception = e;
NAMING_LOGGER.error("request {} failed.", server, e);
}
index = (index + 1) % servers.size();
}
throw new IllegalStateException("failed to req API:" + api + " after all servers(" + servers + ") tried");
throw new IllegalStateException("failed to req API:" + api + " after all servers(" + servers + ") tried: "
+ exception.getMessage());
}
for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
try {
return callServer(api, params, nacosDomain);
} catch (Exception e) {
exception = e;
NAMING_LOGGER.error("[NA] req api:" + api + " failed, server(" + nacosDomain, e);
}
}
throw new IllegalStateException("failed to req API:/api/" + api + " after all servers(" + servers + ") tried");
throw new IllegalStateException("failed to req API:/api/" + api + " after all servers(" + servers + ") tried: "
+ exception.getMessage());
}
@ -408,6 +415,7 @@ public class NamingProxy {
public List<String> builderHeaders() {
List<String> headers = Arrays.asList("Client-Version", UtilAndComs.VERSION,
"User-Agent", UtilAndComs.VERSION,
"Accept-Encoding", "gzip,deflate,sdch",
"Connection", "Keep-Alive",
"RequestId", UuidUtils.generateUuid(), "Request-Module", "Naming");

View File

@ -20,7 +20,7 @@ import java.util.Arrays;
import java.util.List;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class Chooser<K, T> {

View File

@ -20,7 +20,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class GenericPoller<T> implements Poller<T> {

View File

@ -23,14 +23,22 @@ import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class IoUtils {
static public String toString(InputStream input, String encoding) throws IOException {
return (null == encoding) ? toString(new InputStreamReader(input, "UTF-8"))
: toString(new InputStreamReader(input, encoding));
static public String toString(InputStream input, String encoding) {
try {
return (null == encoding) ? toString(new InputStreamReader(input, "UTF-8"))
: toString(new InputStreamReader(input, encoding));
} catch (Exception e) {
NAMING_LOGGER.error("NA", "read input failed.", e);
return StringUtils.EMPTY;
}
}
static public String toString(Reader reader) throws IOException {
@ -78,7 +86,7 @@ public class IoUtils {
}
static private BufferedReader toBufferedReader(Reader reader) {
return reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(
return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(
reader);
}

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.client.naming.utils;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class Pair<T> {

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.client.naming.utils;
import java.util.List;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public interface Poller<T> {
/**

View File

@ -22,7 +22,7 @@ import java.util.Collection;
import java.util.Locale;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class StringUtils {
public static boolean isEmpty(String str) {

View File

@ -20,14 +20,14 @@ package com.alibaba.nacos.client.naming.utils;
*/
public class UtilAndComs {
public static final String VERSION = "Nacos-Java-Client:v1.0.0";
public static String WEB_CONTEXT = "/nacos";
public static String NACOS_URL_BASE = WEB_CONTEXT + "/v1/ns";
public static String NACOS_URL_INSTANCE = NACOS_URL_BASE + "/instance";
public static final String VERSION = "Nacos-Java-Client:v0.2.1";
public static final String ENCODING = "UTF-8";
public static final String ENV_LIST_KEY = "envList";
@ -40,8 +40,6 @@ public class UtilAndComs {
public static final int REQUEST_DOMAIN_RETRY_COUNT = 3;
public static final String DEFAULT_NAMING_ID = "default";
public static final String NACOS_NAMING_LOG_NAME = "com.alibaba.nacos.naming.log.filename";
public static final String NACOS_NAMING_LOG_LEVEL = "com.alibaba.nacos.naming.log.level";

View File

@ -30,7 +30,7 @@ import java.util.Map;
import java.util.Properties;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class NamingTest {

View File

@ -18,7 +18,7 @@
<parent>
<artifactId>nacos-all</artifactId>
<groupId>com.alibaba.nacos</groupId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -19,7 +19,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
@SpringBootApplication

View File

@ -27,7 +27,7 @@ import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
@RestController
@ -40,7 +40,7 @@ public class OperationController {
@Autowired
private CmdbProvider cmdbProvider;
@RequestMapping(value = "/updateSwitch", method = RequestMethod.POST)
@RequestMapping(value = "/switch", method = RequestMethod.PUT)
public String updateSwitch(HttpServletRequest request) throws Exception {
String entry = WebUtils.required(request, "entry");
@ -64,7 +64,7 @@ public class OperationController {
return "ok";
}
@RequestMapping(value = "/queryLabel", method = RequestMethod.GET)
@RequestMapping(value = "/label", method = RequestMethod.GET)
public String queryLabel(HttpServletRequest request) throws Exception {
String entry = WebUtils.required(request, "entry");
String label = WebUtils.required(request, "label");

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.cmdb.core;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public class CmdbManager {

View File

@ -19,7 +19,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
@Component

View File

@ -35,7 +35,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
@Component

View File

@ -21,7 +21,7 @@ import com.alibaba.nacos.api.cmdb.pojo.Entity;
import java.util.List;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public interface CmdbReader {

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.cmdb.service;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public interface CmdbWriter {

View File

@ -20,7 +20,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.7.0
*/
public class UtilsAndCommons {

View File

@ -18,7 +18,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.common.util;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
* @since 0.8.0
*/
public class HttpMethod {

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.common.util;
import java.util.UUID;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class UuidUtils {

View File

@ -17,7 +17,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -24,7 +24,7 @@ import javax.servlet.ServletContext;
/**
* Running config
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
@Component
public class RunningConfigUtils implements ApplicationListener<WebServerInitializedEvent> {
@ -32,7 +32,7 @@ public class RunningConfigUtils implements ApplicationListener<WebServerInitiali
private static int serverPort;
private static String contextPath;
private static String clusterName = "serverlist";
@Autowired
@ -52,7 +52,7 @@ public class RunningConfigUtils implements ApplicationListener<WebServerInitiali
public static String getContextPath() {
return contextPath;
}
public static String getClusterName() {
return clusterName;
}
@ -64,5 +64,5 @@ public class RunningConfigUtils implements ApplicationListener<WebServerInitiali
public static void setContextPath(String contextPath) {
RunningConfigUtils.contextPath = contextPath;
}
}

View File

@ -18,7 +18,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>nacos-console</artifactId>
<!--<packaging>war</packaging>-->

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.console.controller;
import com.alibaba.nacos.config.server.service.PersistService;
import com.alibaba.nacos.naming.web.ApiCommands;
import com.alibaba.nacos.naming.controllers.OperatorController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -38,10 +38,10 @@ public class HealthController {
private static final Logger logger = LoggerFactory.getLogger(HealthController.class);
private final PersistService persistService;
private final ApiCommands apiCommands;
private final OperatorController apiCommands;
@Autowired
public HealthController(PersistService persistService, ApiCommands apiCommands) {
public HealthController(PersistService persistService, OperatorController apiCommands) {
this.persistService = persistService;
this.apiCommands = apiCommands;
}
@ -98,7 +98,7 @@ public class HealthController {
private boolean isNamingReadiness(HttpServletRequest request) {
try {
apiCommands.hello(request);
apiCommands.metrics(request);
return true;
} catch (Exception e) {
logger.error("Naming health check fail.", e);

View File

@ -48,3 +48,26 @@ db.password=4b9622f3f70c7677835ac5a6719e7caf
#security.basic.enabled=false
#nacos.security.ignore.urls=/**
nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**
management.metrics.export.elastic.enabled=false
#management.metrics.export.elastic.host=http://localhost:9200
# metrics for influx
management.metrics.export.influx.enabled=false
#management.metrics.export.influx.db=springboot
#management.metrics.export.influx.uri=http://localhost:8086
#management.metrics.export.influx.auto-create-db=true
#management.metrics.export.influx.consistency=one
#management.metrics.export.influx.compressed=true
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D
# default current work dir
server.tomcat.basedir=
nacos.naming.distro.taskDispatchThreadCount=10
nacos.naming.distro.taskDispatchPeriod=200
nacos.naming.distro.batchSyncKeyCount=1000
nacos.naming.distro.initDataRatio=0.9
nacos.naming.distro.syncRetryDelay=5000
nacos.naming.data.warmup=false

View File

@ -17,7 +17,7 @@ package com.alibaba.nacos.console.controller;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.config.server.service.PersistService;
import com.alibaba.nacos.naming.web.ApiCommands;
import com.alibaba.nacos.naming.controllers.OperatorController;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -54,7 +54,7 @@ public class HealthControllerTest {
private PersistService persistService;
@Mock
private ApiCommands apiCommands;
private OperatorController apiCommands;
private MockMvc mockmvc;
@ -75,14 +75,14 @@ public class HealthControllerTest {
String url = "/v1/console/health/readiness";
Mockito.when(persistService.configInfoCount(any(String.class))).thenReturn(0);
Mockito.when(apiCommands.hello(any(HttpServletRequest.class))).thenReturn(new JSONObject());
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenReturn(new JSONObject());
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(url);
Assert.assertEquals(200, mockmvc.perform(builder).andReturn().getResponse().getStatus());
// Config and Naming are not in readiness
Mockito.when(persistService.configInfoCount(any(String.class))).thenThrow(
new RuntimeException("HealthControllerTest.testReadiness"));
Mockito.when(apiCommands.hello(any(HttpServletRequest.class))).thenThrow(
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenThrow(
new RuntimeException("HealthControllerTest.testReadiness"));
builder = MockMvcRequestBuilders.get(url);
MockHttpServletResponse response = mockmvc.perform(builder).andReturn().getResponse();
@ -92,18 +92,18 @@ public class HealthControllerTest {
// Config is not in readiness
Mockito.when(persistService.configInfoCount(any(String.class))).thenThrow(
new RuntimeException("HealthControllerTest.testReadiness"));
Mockito.when(apiCommands.hello(any(HttpServletRequest.class))).thenReturn(new JSONObject());
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenReturn(new JSONObject());
response = mockmvc.perform(builder).andReturn().getResponse();
Assert.assertEquals(500, response.getStatus());
Assert.assertEquals("Config is not in readiness", response.getContentAsString());
// Naming is not in readiness
Mockito.when(persistService.configInfoCount(any(String.class))).thenReturn(0);
Mockito.when(apiCommands.hello(any(HttpServletRequest.class))).thenThrow(
Mockito.when(apiCommands.metrics(any(HttpServletRequest.class))).thenThrow(
new RuntimeException("HealthControllerTest.testReadiness"));
builder = MockMvcRequestBuilders.get(url);
response = mockmvc.perform(builder).andReturn().getResponse();
Assert.assertEquals(500, response.getStatus());
Assert.assertEquals("Naming is not in readiness", response.getContentAsString());
}
}
}

View File

@ -18,7 +18,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -21,7 +21,7 @@ import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class WebUtils {

View File

@ -101,6 +101,7 @@ else
JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M"
fi
JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/nacos-server.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"

View File

@ -38,3 +38,10 @@ server.tomcat.basedir=
#nacos.security.ignore.urls=/**
nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**
nacos.naming.distro.taskDispatchThreadCount=10
nacos.naming.distro.taskDispatchPeriod=200
nacos.naming.distro.batchSyncKeyCount=1000
nacos.naming.distro.initDataRatio=0.9
nacos.naming.distro.syncRetryDelay=5000
nacos.naming.data.warmup=true

View File

@ -76,6 +76,31 @@
<appender-ref ref="naming-raft"/>
</appender>
<appender name="naming-ephemeral"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/naming-ephemeral.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/naming-ephemeral.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
<maxFileSize>1GB</maxFileSize>
<MaxHistory>15</MaxHistory>
<totalSizeCap>3GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<Pattern>%date %level %msg%n%n</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="async-naming-ephemeral" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>1024</queueSize>
<neverBlock>true</neverBlock>
<appender-ref ref="naming-ephemeral"/>
</appender>
<appender name="naming-event"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/naming-event.log</file>
@ -484,6 +509,10 @@
<level value="INFO"/>
<appender-ref ref="async-naming-raft"/>
</logger>
<logger name="com.alibaba.nacos.naming.ephemeral" additivity="false">
<level value="DEBUG"/>
<appender-ref ref="async-naming-ephemeral"/>
</logger>
<logger name="com.alibaba.nacos.naming.event" additivity="false">
<level value="INFO"/>
<appender-ref ref="async-naming-event"/>

View File

@ -18,7 +18,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -18,7 +18,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -25,7 +25,7 @@ import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class NamingExample {

View File

@ -18,7 +18,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>0.9.1</version>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -204,9 +204,9 @@
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application.properties</exclude>
</excludes>
<!--<excludes>-->
<!--<exclude>application.properties</exclude>-->
<!--</excludes>-->
</resource>
</resources>
</build>

View File

@ -16,10 +16,11 @@
package com.alibaba.nacos.naming.acl;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.naming.CommonParams;
import com.alibaba.nacos.core.utils.WebUtils;
import com.alibaba.nacos.naming.core.Domain;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.misc.Switch;
import com.alibaba.nacos.naming.core.Service;
import com.alibaba.nacos.naming.core.ServiceManager;
import com.alibaba.nacos.naming.misc.SwitchDomain;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
@ -31,13 +32,16 @@ import java.security.AccessControlException;
import java.util.Map;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
@Component
public class AuthChecker {
@Autowired
private DomainsManager domainsManager;
private ServiceManager serviceManager;
@Autowired
private SwitchDomain switchDomain;
static private String[] APP_WHITE_LIST = {};
static private String[] TOKEN_WHITE_LIST = {"traffic-scheduling@midware"};
@ -53,34 +57,32 @@ public class AuthChecker {
return;
}
agent = req.getHeader("User-Agent");
if (StringUtils.startsWith(agent, UtilsAndCommons.NACOS_SERVER_HEADER)) {
return;
}
throw new IllegalAccessException("illegal access,agent= " + agent + ", token=" + token);
}
public void doAuth(Map<String, String[]> params, HttpServletRequest req) throws Exception {
String namespaceId = WebUtils.optional(req, Constants.REQUEST_PARAM_NAMESPACE_ID,
UtilsAndCommons.getDefaultNamespaceId());
String dom = WebUtils.optional(req, "name", "");
if (StringUtils.isEmpty(dom)) {
dom = WebUtils.optional(req, "dom", "");
String namespaceId = WebUtils.optional(req, CommonParams.NAMESPACE_ID,
Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.optional(req, "name", "");
if (StringUtils.isEmpty(serviceName)) {
serviceName = WebUtils.optional(req, "serviceName", "");
}
if (StringUtils.isEmpty(dom)) {
dom = WebUtils.optional(req, "tag", "");
if (StringUtils.isEmpty(serviceName)) {
serviceName = WebUtils.optional(req, "tag", "");
}
Domain domObj;
if (req.getRequestURI().equals(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.API_UPDATE_SWITCH) ||
req.getRequestURI().equals(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.API_SET_ALL_WEIGHTS)) {
// we consider switch is a kind of special domain
domObj = Switch.getDom();
} else {
domObj = domainsManager.getDomain(namespaceId, dom);
}
Service service = serviceManager.getService(namespaceId, serviceName);
if (domObj == null) {
if (service == null) {
if (!req.getRequestURI().equals(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.API_SET_ALL_WEIGHTS)) {
throw new IllegalStateException("auth failed, dom does not exist: " + dom);
throw new IllegalStateException("auth failed, service does not exist: " + serviceName);
}
}
@ -88,11 +90,11 @@ public class AuthChecker {
String auth = req.getParameter("auth");
String userName = req.getParameter("userName");
if (StringUtils.isEmpty(auth) && StringUtils.isEmpty(token)) {
throw new IllegalArgumentException("provide 'authInfo' or 'token' to access this dom");
throw new IllegalArgumentException("provide 'authInfo' or 'token' to access this service");
}
// try valid token
if ((domObj != null && StringUtils.equals(domObj.getToken(), token))) {
if ((service != null && StringUtils.equals(service.getToken(), token))) {
return;
}
@ -114,8 +116,8 @@ public class AuthChecker {
throw new AccessControlException("un-registered SDK app");
}
if (!domObj.getOwners().contains(authInfo.getOperator())
&& !Switch.getMasters().contains(authInfo.getOperator())) {
if (!service.getOwners().contains(authInfo.getOperator())
&& !switchDomain.getMasters().contains(authInfo.getOperator())) {
throw new AccessControlException("dom already exists and you're not among the owners");
}
}

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.naming.acl;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @author nkorange
*/
public class AuthInfo {
private String operator;

View File

@ -16,8 +16,9 @@
package com.alibaba.nacos.naming.boot;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.raft.RaftCore;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
@ -28,7 +29,7 @@ import javax.servlet.ServletContext;
/**
* @author nkorange
*/
@Component
@Component("runningConfig")
public class RunningConfig implements ApplicationListener<WebServerInitializedEvent> {
private static int serverPort;
@ -47,12 +48,6 @@ public class RunningConfig implements ApplicationListener<WebServerInitializedEv
serverPort = event.getWebServer().getPort();
contextPath = servletContext.getContextPath();
try {
RaftCore.init();
} catch (Exception e) {
Loggers.RAFT.error("[NACOS-RAFT] {} {}", "failed to initialize raft sub system", e);
}
}
public static int getServerPort() {
@ -60,6 +55,10 @@ public class RunningConfig implements ApplicationListener<WebServerInitializedEv
}
public static String getContextPath() {
if (StringUtils.isBlank(contextPath)) {
return UtilsAndCommons.NACOS_SERVER_CONTEXT;
}
return contextPath;
}
}

View File

@ -16,18 +16,18 @@
package com.alibaba.nacos.naming.boot;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* A global spring context bean to inject bean for non-bean classes
*
* @author nkorange
*/
@Component
@Component("nacosApplicationContext")
public class SpringContext implements ApplicationContextAware {
@Autowired
static ApplicationContext context;
@Override

View File

@ -0,0 +1,441 @@
/*
* 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.cluster;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.core.utils.SystemUtils;
import com.alibaba.nacos.naming.boot.RunningConfig;
import com.alibaba.nacos.naming.cluster.servers.Server;
import com.alibaba.nacos.naming.cluster.servers.ServerChangeListener;
import com.alibaba.nacos.naming.misc.*;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static com.alibaba.nacos.core.utils.SystemUtils.*;
/**
* The manager to globally refresh and operate server list.
*
* @author nkorange
* @since 1.0.0
*/
@Component("serverListManager")
public class ServerListManager {
public static final int STABLE_PERIOD = 60 * 1000;
@Autowired
private SwitchDomain switchDomain;
private List<ServerChangeListener> listeners = new ArrayList<>();
private List<Server> servers = new ArrayList<>();
private List<Server> healthyServers = new ArrayList<>();
private Map<String, List<Server>> distroConfig = new ConcurrentHashMap<>();
private Set<String> liveSites = new HashSet<>();
public final String LOCALHOST_SITE = UtilsAndCommons.UNKNOWN_SITE;
private long lastHealthServerMillis = 0L;
private boolean autoDisabledHealthCheck = false;
private Synchronizer synchronizer = new ServerStatusSynchronizer();
public void listen(ServerChangeListener listener) {
listeners.add(listener);
}
@PostConstruct
public void init() {
GlobalExecutor.registerServerListUpdater(new ServerListUpdater());
GlobalExecutor.registerServerStatusReporter(new ServerStatusReporter(), 5000);
}
public List<Server> refreshServerList() {
List<Server> result = new ArrayList<>();
if (STANDALONE_MODE) {
Server server = new Server();
server.setIp(NetUtils.getLocalAddress());
server.setServePort(RunningConfig.getServerPort());
result.add(server);
return result;
}
List<String> serverList = new ArrayList<>();
try {
serverList = readClusterConf();
} catch (Exception e) {
Loggers.SRV_LOG.warn("failed to get config: " + CLUSTER_CONF_FILE_PATH, e);
}
if (Loggers.DEBUG_LOG.isDebugEnabled()) {
Loggers.DEBUG_LOG.debug("SERVER-LIST from cluster.conf: {}", result);
}
//use system env
if (CollectionUtils.isEmpty(serverList)) {
serverList = SystemUtils.getIPsBySystemEnv(UtilsAndCommons.SELF_SERVICE_CLUSTER_ENV);
if (Loggers.DEBUG_LOG.isDebugEnabled()) {
Loggers.DEBUG_LOG.debug("SERVER-LIST from system variable: {}", result);
}
}
if (CollectionUtils.isNotEmpty(serverList)) {
for (int i = 0; i < serverList.size(); i++) {
String ip;
int port;
if (serverList.get(0).contains(UtilsAndCommons.IP_PORT_SPLITER)) {
ip = serverList.get(i).split(UtilsAndCommons.IP_PORT_SPLITER)[0];
port = Integer.parseInt(serverList.get(i).split(UtilsAndCommons.IP_PORT_SPLITER)[1]);
} else {
ip = serverList.get(i);
port = RunningConfig.getServerPort();
}
Server member = new Server();
member.setIp(ip);
member.setServePort(port);
result.add(member);
}
}
return result;
}
public boolean contains(String s) {
for (Server server : servers) {
if (server.getKey().equals(s)) {
return true;
}
}
return false;
}
public List<Server> getServers() {
return servers;
}
public List<Server> getHealthyServers() {
return healthyServers;
}
private void notifyListeners() {
GlobalExecutor.notifyServerListChange(new Runnable() {
@Override
public void run() {
for (ServerChangeListener listener : listeners) {
listener.onChangeServerList(servers);
listener.onChangeHealthyServerList(healthyServers);
}
}
});
}
public Map<String, List<Server>> getDistroConfig() {
return distroConfig;
}
public synchronized void onReceiveServerStatus(String configInfo) {
String[] configs = configInfo.split("\r\n");
if (configs.length == 0) {
return;
}
List<Server> newHealthyList = new ArrayList<>();
List<Server> tmpServerList = new ArrayList<>();
for (String config : configs) {
tmpServerList.clear();
// site:ip:lastReportTime:weight
String[] params = config.split("#");
if (params.length <= 3) {
Loggers.SRV_LOG.warn("received malformed distro map data: {}", config);
continue;
}
Server server = new Server();
server.setSite(params[0]);
server.setIp(params[1].split(UtilsAndCommons.IP_PORT_SPLITER)[0]);
server.setServePort(Integer.parseInt(params[1].split(UtilsAndCommons.IP_PORT_SPLITER)[1]));
server.setLastRefTime(Long.parseLong(params[2]));
if (!contains(server.getKey())) {
throw new IllegalArgumentException("server: " + server.getKey() + " is not in serverlist");
}
Date date = new Date(Long.parseLong(params[2]));
server.setLastRefTimeStr(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
server.setWeight(params.length == 4 ? Integer.parseInt(params[3]) : 1);
server.setAlive(System.currentTimeMillis() - server.getLastRefTime() < switchDomain.getDistroServerExpiredMillis());
List<Server> list = distroConfig.get(server.getSite());
if (list == null || list.size() <= 0) {
list = new ArrayList<>();
list.add(server);
distroConfig.put(server.getSite(), list);
}
for (Server s : list) {
String serverId = s.getKey() + "_" + s.getSite();
String newServerId = server.getKey() + "_" + server.getSite();
if (serverId.equals(newServerId)) {
if (s.isAlive() != server.isAlive() || s.getWeight() != server.getWeight()) {
Loggers.SRV_LOG.warn("server beat out of date, current: {}, last: {}",
JSON.toJSONString(server), JSON.toJSONString(s));
}
tmpServerList.add(server);
continue;
}
tmpServerList.add(s);
}
if (!tmpServerList.contains(server)) {
tmpServerList.add(server);
}
distroConfig.put(server.getSite(), tmpServerList);
}
liveSites.addAll(distroConfig.keySet());
List<Server> servers = distroConfig.get(LOCALHOST_SITE);
if (CollectionUtils.isEmpty(servers)) {
return;
}
//local site servers
List<String> allLocalSiteSrvs = new ArrayList<String>();
for (Server server : servers) {
if (server.getKey().endsWith(":0")) {
continue;
}
server.setAdWeight(switchDomain.getAdWeight(server.getKey()) == null ? 0 : switchDomain.getAdWeight(server.getKey()));
for (int i = 0; i < server.getWeight() + server.getAdWeight(); i++) {
if (!allLocalSiteSrvs.contains(server.getKey())) {
allLocalSiteSrvs.add(server.getKey());
}
if (server.isAlive() && !newHealthyList.contains(server)) {
newHealthyList.add(server);
}
}
}
Collections.sort(newHealthyList);
float curRatio = (float) newHealthyList.size() / allLocalSiteSrvs.size();
if (autoDisabledHealthCheck
&& curRatio > switchDomain.getDistroThreshold()
&& System.currentTimeMillis() - lastHealthServerMillis > STABLE_PERIOD) {
Loggers.SRV_LOG.info("[NACOS-DISTRO] distro threshold restored and " +
"stable now, enable health check. current ratio: {}", curRatio);
switchDomain.setHealthCheckEnabled(true);
// we must set this variable, otherwise it will conflict with user's action
autoDisabledHealthCheck = false;
}
if (!CollectionUtils.isEqualCollection(healthyServers, newHealthyList)) {
// for every change disable healthy check for some while
if (switchDomain.isHealthCheckEnabled()) {
Loggers.SRV_LOG.info("[NACOS-DISTRO] healthy server list changed, " +
"disable health check for {} ms from now on", STABLE_PERIOD);
switchDomain.setHealthCheckEnabled(false);
autoDisabledHealthCheck = true;
lastHealthServerMillis = System.currentTimeMillis();
}
healthyServers = newHealthyList;
notifyListeners();
}
}
public void clean() {
cleanInvalidServers();
for (Map.Entry<String, List<Server>> entry : distroConfig.entrySet()) {
for (Server server : entry.getValue()) {
//request other server to clean invalid servers
if (!server.getKey().equals(NetUtils.localServer())) {
requestOtherServerCleanInvalidServers(server.getKey());
}
}
}
}
public Set<String> getLiveSites() {
return liveSites;
}
private void cleanInvalidServers() {
for (Map.Entry<String, List<Server>> entry : distroConfig.entrySet()) {
List<Server> tmpServers = null;
List<Server> currentServerList = entry.getValue();
for (Server server : entry.getValue()) {
if (!server.isAlive()) {
tmpServers = new ArrayList<>();
for (Server server1 : currentServerList) {
String serverKey1 = server1.getKey() + "_" + server1.getSite();
String serverKey = server.getKey() + "_" + server.getSite();
if (!serverKey.equals(serverKey1) && !tmpServers.contains(server1)) {
tmpServers.add(server1);
}
}
}
}
if (tmpServers != null) {
distroConfig.put(entry.getKey(), tmpServers);
}
}
}
private void requestOtherServerCleanInvalidServers(String serverIP) {
Map<String, String> params = new HashMap<String, String>(1);
params.put("action", "without-diamond-clean");
try {
NamingProxy.reqAPI("distroStatus", params, serverIP, false);
} catch (Exception e) {
Loggers.SRV_LOG.warn("[DISTRO-STATUS-CLEAN] Failed to request to clean server status to " + serverIP, e);
}
}
public class ServerListUpdater implements Runnable {
@Override
public void run() {
try {
List<Server> refreshedServers = refreshServerList();
List<Server> oldServers = servers;
if (CollectionUtils.isEmpty(refreshedServers)) {
Loggers.RAFT.warn("refresh server list failed, ignore it.");
return;
}
boolean changed = false;
List<Server> newServers = (List<Server>) CollectionUtils.subtract(refreshedServers, oldServers);
if (CollectionUtils.isNotEmpty(newServers)) {
servers.addAll(newServers);
changed = true;
Loggers.RAFT.info("server list is updated, new: {} servers: {}", newServers.size(), newServers);
}
List<Server> deadServers = (List<Server>) CollectionUtils.subtract(oldServers, refreshedServers);
if (CollectionUtils.isNotEmpty(deadServers)) {
servers.removeAll(deadServers);
changed = true;
Loggers.RAFT.info("server list is updated, dead: {}, servers: {}", deadServers.size(), deadServers);
}
if (changed) {
notifyListeners();
}
} catch (Exception e) {
Loggers.RAFT.info("error while updating server list.", e);
}
}
}
private class ServerStatusReporter implements Runnable {
@Override
public void run() {
try {
if (RunningConfig.getServerPort() <= 0) {
return;
}
for (String key : distroConfig.keySet()) {
for (Server server : distroConfig.get(key)) {
server.setAlive(System.currentTimeMillis() - server.getLastRefTime() < switchDomain.getDistroServerExpiredMillis());
}
}
int weight = Runtime.getRuntime().availableProcessors() / 2;
if (weight <= 0) {
weight = 1;
}
long curTime = System.currentTimeMillis();
String status = LOCALHOST_SITE + "#" + NetUtils.localServer() + "#" + curTime + "#" + weight + "\r\n";
//send status to itself
onReceiveServerStatus(status);
List<Server> allServers = getServers();
if (!contains(NetUtils.localServer())) {
Loggers.SRV_LOG.error("local ip is not in serverlist, ip: {}, serverlist: {}", NetUtils.localServer(), allServers);
return;
}
if (allServers.size() > 0 && !NetUtils.localServer().contains(UtilsAndCommons.LOCAL_HOST_IP)) {
for (com.alibaba.nacos.naming.cluster.servers.Server server : allServers) {
if (server.getKey().equals(NetUtils.localServer())) {
continue;
}
Message msg = new Message();
msg.setData(status);
synchronizer.send(server.getKey(), msg);
}
}
} catch (Exception e) {
Loggers.SRV_LOG.error("[SERVER-STATUS] Exception while sending server status", e);
} finally {
GlobalExecutor.registerServerStatusReporter(this, switchDomain.getServerStatusSynchronizationPeriodMillis());
}
}
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.cluster;
/**
* Server running mode
* <p>
* We use CAP theory to set the server mode, users can select their preferred mode in running time.
* <p>
* CP mode provides strong consistency, data persistence but network partition tolerance.
* <p>
* AP mode provides eventual consistency and network partition tolerance but data persistence.
* <p>
* Mixed mode provides CP for some data and AP for some other data.
* <p>
* Service level information and cluster level information are always operated via CP protocol, so
* in AP mode they cannot be edited.
*
* @author nkorange
* @since 1.0.0
*/
public enum ServerMode {
/**
* AP mode
*/
AP,
/**
* CP mode
*/
CP,
/**
* Mixed mode
*/
MIXED
}

View File

@ -0,0 +1,49 @@
/*
* 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.cluster;
/**
* A flag to indicate the exact status of a server.
*
* @author nkorange
* @since 1.0.0
*/
public enum ServerStatus {
/**
* server is up and ready for request
*/
UP,
/**
* server is out of service, something abnormal happened
*/
DOWN,
/**
* server is preparing itself for request, usually 'UP' is the next status
*/
STARTING,
/**
* server is manually paused
*/
PAUSED,
/**
* only write operation is permitted.
*/
WRITE_ONLY,
/**
* only read operation is permitted.
*/
READ_ONLY
}

View File

@ -0,0 +1,75 @@
/*
* 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.cluster;
import com.alibaba.nacos.naming.consistency.ConsistencyService;
import com.alibaba.nacos.naming.misc.GlobalExecutor;
import com.alibaba.nacos.naming.misc.SwitchDomain;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* Detect and control the working status of local server
*
* @author nkorange
* @since 1.0.0
*/
@Service
public class ServerStatusManager {
@Resource(name = "consistencyDelegate")
private ConsistencyService consistencyService;
@Autowired
private SwitchDomain switchDomain;
private ServerStatus serverStatus = ServerStatus.STARTING;
@PostConstruct
public void init() {
GlobalExecutor.registerServerStatusUpdater(new ServerStatusUpdater());
}
private void refreshServerStatus() {
if (StringUtils.isNotBlank(switchDomain.getOverriddenServerStatus())) {
serverStatus = ServerStatus.valueOf(switchDomain.getOverriddenServerStatus());
return;
}
if (consistencyService.isAvailable()) {
serverStatus = ServerStatus.UP;
} else {
serverStatus = ServerStatus.DOWN;
}
}
public ServerStatus getServerStatus() {
return serverStatus;
}
public class ServerStatusUpdater implements Runnable {
@Override
public void run() {
refreshServerStatus();
}
}
}

View File

@ -0,0 +1,154 @@
/*
* 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.cluster.servers;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
/**
* Member node of Nacos cluster
*
* @author nkorange
* @since 1.0.0
*/
public class Server implements Comparable<Server> {
/**
* IP of member
*/
private String ip;
/**
* serving port of member.
*/
private int servePort;
private String site = UtilsAndCommons.UNKNOWN_SITE;
private int weight = 1;
/**
* additional weight, used to adjust manually
*/
private int adWeight;
private boolean alive = false;
private long lastRefTime = 0L;
private String lastRefTimeStr;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getServePort() {
return servePort;
}
public void setServePort(int servePort) {
this.servePort = servePort;
}
public String getSite() {
return site;
}
public void setSite(String site) {
this.site = site;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getAdWeight() {
return adWeight;
}
public void setAdWeight(int adWeight) {
this.adWeight = adWeight;
}
public boolean isAlive() {
return alive;
}
public void setAlive(boolean alive) {
this.alive = alive;
}
public long getLastRefTime() {
return lastRefTime;
}
public void setLastRefTime(long lastRefTime) {
this.lastRefTime = lastRefTime;
}
public String getLastRefTimeStr() {
return lastRefTimeStr;
}
public void setLastRefTimeStr(String lastRefTimeStr) {
this.lastRefTimeStr = lastRefTimeStr;
}
public String getKey() {
return ip + UtilsAndCommons.IP_PORT_SPLITER + servePort;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Server server = (Server) o;
return servePort == server.servePort && ip.equals(server.ip);
}
@Override
public int hashCode() {
int result = ip.hashCode();
result = 31 * result + servePort;
return result;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
@Override
public int compareTo(Server server) {
if (server == null) {
return 1;
}
return this.getKey().compareTo(server.getKey());
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.cluster.servers;
import java.util.List;
/**
* Nacos cluster member change event listener
*
* @author nkorange
* @since 1.0.0
*/
public interface ServerChangeListener {
/**
* If member list changed, this method is invoked.
*
* @param servers servers after change
*/
void onChangeServerList(List<Server> servers);
/**
* If reachable member list changed, this method is invoked.
*
* @param healthyServer reachable servers after change
*/
void onChangeHealthyServerList(List<Server> healthyServer);
}

View File

@ -0,0 +1,87 @@
/*
* 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.cluster.transport;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.naming.consistency.Datum;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.pojo.Record;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
/**
* Use FastJSON to serialize data
*
* @author nkorange
* @since 1.0.0
*/
@Component
public class FastJsonSerializer implements Serializer {
@Override
public <T> byte[] serialize(T data) {
return JSON.toJSONBytes(data);
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
try {
return JSON.parseObject(new String(data, "UTF-8"), clazz);
} catch (UnsupportedEncodingException e) {
return null;
}
}
@Override
public <T> T deserialize(byte[] data, TypeReference<T> clazz) {
try {
String dataString = new String(data, "UTF-8");
return JSON.parseObject(dataString, clazz);
} catch (Exception e) {
Loggers.SRV_LOG.error("deserialize data failed.", e);
}
return null;
}
@Override
public <T extends Record> Map<String, Datum<T>> deserializeMap(byte[] data, Class<T> clazz) {
try {
String dataString = new String(data, "UTF-8");
Map<String, JSONObject> dataMap = JSON.parseObject(dataString, new TypeReference<Map<String, JSONObject>>() {
});
Map<String, Datum<T>> resultMap = new HashMap<>(dataMap.size());
for (Map.Entry<String, JSONObject> entry : dataMap.entrySet()) {
Datum<T> datum = new Datum<>();
datum.timestamp.set(entry.getValue().getLongValue("timestamp"));
datum.key = entry.getValue().getString("key");
datum.value = JSON.parseObject(entry.getValue().getJSONObject("value").toJSONString(), clazz);
resultMap.put(entry.getKey(), datum);
}
return resultMap;
} catch (Exception e) {
Loggers.SRV_LOG.error("deserialize data failed.", e);
}
return null;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.cluster.transport;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.naming.consistency.Datum;
import com.alibaba.nacos.naming.pojo.Record;
import java.util.Map;
/**
* Serializer specially for large map of data
*
* @author nkorange
* @since 1.0.0
*/
public interface Serializer {
/**
* Serialize data with some kind of serializing protocol
*
* @param data data to serialize
* @param <T> type of data
* @return byte array of serialized data
*/
<T> byte[] serialize(T data);
/**
* Deserialize byte array data to target type
*
* @param data data to deserialize
* @param clazz target type
* @param <T> target type
* @return deserialized data map
*/
<T> T deserialize(byte[] data, Class<T> clazz);
/**
* Deserialize byte array data to target generic type
*
* @param data data to deserialize
* @param clazz target type
* @param <T> target type
* @return deserialized data
*/
<T> T deserialize(byte[] data, TypeReference<T> clazz);
/**
* Deserialize byte array data to target type
*
* @param <T> target type
* @param data data to deserialize
* @param clazz target type
* @return deserialized data map
*/
<T extends Record> Map<String, Datum<T>> deserializeMap(byte[] data, Class<T> clazz);
}

View File

@ -0,0 +1,30 @@
/*
* 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;
/**
* @author nkorange
*/
public enum ApplyAction {
/**
* Data changed
*/
CHANGE,
/**
* Data deleted
*/
DELETE
}

View File

@ -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.naming.consistency;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.naming.pojo.Record;
/**
* Consistence service for all implementations to derive.
* <p>
* We announce this consistency service to decouple the specific consistency implementation with business logic.
* User should not be aware of what consistency protocol is being used.
* <p>
* In this way, we also provide space for user to extend the underlying consistency protocols, as long as they
* obey our consistency baseline.
*
* @author nkorange
* @since 1.0.0
*/
public interface ConsistencyService {
/**
* Put a data related to a key to Nacos cluster
*
* @param key key of data, this key should be globally unique
* @param value value of data
* @throws NacosException
* @see
*/
void put(String key, Record value) throws NacosException;
/**
* Remove a data from Nacos cluster
*
* @param key key of data
* @throws NacosException
*/
void remove(String key) throws NacosException;
/**
* Get a data from Nacos cluster
*
* @param key key of data
* @return data related to the key
* @throws NacosException
*/
Datum get(String key) throws NacosException;
/**
* Listen for changes of a data
*
* @param key key of data
* @param listener callback of data change
* @throws NacosException
*/
void listen(String key, RecordListener listener) throws NacosException;
/**
* Cancel listening of a data
*
* @param key key of data
* @param listener callback of data change
* @throws NacosException
*/
void unlisten(String key, RecordListener listener) throws NacosException;
/**
* Tell the status of this consistency service
*
* @return true if available
*/
boolean isAvailable();
}

View File

@ -13,17 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.raft;
package com.alibaba.nacos.naming.consistency;
import com.alibaba.nacos.naming.pojo.Record;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author nacos
*/
public class Datum {
public class Datum<T extends Record> {
public String key;
public String value;
public T value;
public AtomicLong timestamp = new AtomicLong(0L);
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.consistency;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.naming.consistency.ephemeral.EphemeralConsistencyService;
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
import com.alibaba.nacos.naming.pojo.Record;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Consistency delegate
*
* @author nkorange
* @since 1.0.0
*/
@Service("consistencyDelegate")
public class DelegateConsistencyServiceImpl implements ConsistencyService {
@Autowired
private PersistentConsistencyService persistentConsistencyService;
@Autowired
private EphemeralConsistencyService ephemeralConsistencyService;
@Override
public void put(String key, Record value) throws NacosException {
mapConsistencyService(key).put(key, value);
}
@Override
public void remove(String key) throws NacosException {
mapConsistencyService(key).remove(key);
}
@Override
public Datum get(String key) throws NacosException {
return mapConsistencyService(key).get(key);
}
@Override
public void listen(String key, RecordListener listener) throws NacosException {
// this special key is listened by both:
if (KeyBuilder.SERVICE_META_KEY_PREFIX.equals(key)) {
persistentConsistencyService.listen(key, listener);
ephemeralConsistencyService.listen(key, listener);
return;
}
mapConsistencyService(key).listen(key, listener);
}
@Override
public void unlisten(String key, RecordListener listener) throws NacosException {
mapConsistencyService(key).unlisten(key, listener);
}
@Override
public boolean isAvailable() {
return ephemeralConsistencyService.isAvailable() && persistentConsistencyService.isAvailable();
}
private ConsistencyService mapConsistencyService(String key) {
return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.consistency;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import org.apache.commons.lang3.StringUtils;
/**
* Key operations for data
*
* @author nkorange
* @since 1.0.0
*/
public class KeyBuilder {
public static final String NAMESPACE_KEY_CONNECTOR = "##";
private static final String EPHEMERAL_KEY_PREFIX = "ephemeral.";
public static final String SERVICE_META_KEY_PREFIX = "com.alibaba.nacos.naming.domains.meta.";
public static final String INSTANCE_LIST_KEY_PREFIX = "com.alibaba.nacos.naming.iplist.";
public static final String BRIEF_SERVICE_META_KEY_PREFIX = "meta.";
public static final String BRIEF_INSTANCE_LIST_KEY_PREFIX = "iplist.";
private static String buildEphemeralInstanceListKey(String namespaceId, String serviceName) {
return INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX + namespaceId + NAMESPACE_KEY_CONNECTOR
+ serviceName;
}
private static String buildPersistentInstanceListKey(String namespaceId, String serviceName) {
return INSTANCE_LIST_KEY_PREFIX + namespaceId + NAMESPACE_KEY_CONNECTOR
+ serviceName;
}
public static String buildInstanceListKey(String namespaceId, String serviceName, boolean ephemeral) {
return ephemeral ? buildEphemeralInstanceListKey(namespaceId, serviceName) :
buildPersistentInstanceListKey(namespaceId, serviceName);
}
public static String buildServiceMetaKey(String namespaceId, String serviceName) {
return SERVICE_META_KEY_PREFIX + namespaceId + NAMESPACE_KEY_CONNECTOR + serviceName;
}
public static String getSwitchDomainKey() {
return SERVICE_META_KEY_PREFIX + UtilsAndCommons.SWITCH_DOMAIN_NAME;
}
public static boolean matchEphemeralInstanceListKey(String key) {
return key.startsWith(INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX);
}
public static boolean matchInstanceListKey(String key) {
return key.startsWith(INSTANCE_LIST_KEY_PREFIX) || key.startsWith(BRIEF_INSTANCE_LIST_KEY_PREFIX);
}
public static boolean matchServiceMetaKey(String key) {
return key.startsWith(SERVICE_META_KEY_PREFIX) || key.startsWith(BRIEF_SERVICE_META_KEY_PREFIX);
}
public static boolean matchSwitchKey(String key) {
return key.endsWith(UtilsAndCommons.SWITCH_DOMAIN_NAME);
}
public static boolean matchServiceName(String key, String namespaceId, String serviceName) {
return key.endsWith(namespaceId + NAMESPACE_KEY_CONNECTOR + serviceName);
}
public static boolean matchServiceMetaKey(String key, String namespaceId, String serviceName) {
return matchServiceMetaKey(key) && matchServiceName(key, namespaceId, serviceName);
}
public static boolean matchInstanceListKey(String key, String namespaceId, String serviceName) {
return matchInstanceListKey(key) && matchServiceName(key, namespaceId, serviceName);
}
public static boolean matchEphemeralKey(String key) {
// currently only instance list has ephemeral type:
return matchEphemeralInstanceListKey(key);
}
public static boolean matchPersistentKey(String key) {
return !matchEphemeralKey(key);
}
public static String briefInstanceListkey(String key) {
return BRIEF_INSTANCE_LIST_KEY_PREFIX + key.split(INSTANCE_LIST_KEY_PREFIX)[1];
}
public static String briefServiceMetaKey(String key) {
return BRIEF_SERVICE_META_KEY_PREFIX + key.split(SERVICE_META_KEY_PREFIX)[1];
}
public static String detailInstanceListkey(String key) {
return INSTANCE_LIST_KEY_PREFIX.substring(0, INSTANCE_LIST_KEY_PREFIX.indexOf(BRIEF_INSTANCE_LIST_KEY_PREFIX))
+ key;
}
public static String detailServiceMetaKey(String key) {
return SERVICE_META_KEY_PREFIX.substring(0, SERVICE_META_KEY_PREFIX.indexOf(BRIEF_SERVICE_META_KEY_PREFIX))
+ key;
}
public static String getNamespace(String key) {
if (matchSwitchKey(key)) {
return StringUtils.EMPTY;
}
if (matchServiceMetaKey(key)) {
return key.split(NAMESPACE_KEY_CONNECTOR)[0].substring(SERVICE_META_KEY_PREFIX.length());
}
if (matchEphemeralInstanceListKey(key)) {
return key.split(NAMESPACE_KEY_CONNECTOR)[0].substring(INSTANCE_LIST_KEY_PREFIX.length() + EPHEMERAL_KEY_PREFIX.length());
}
if (matchInstanceListKey(key)) {
return key.split(NAMESPACE_KEY_CONNECTOR)[0].substring(INSTANCE_LIST_KEY_PREFIX.length());
}
return StringUtils.EMPTY;
}
public static String getServiceName(String key) {
return key.split(NAMESPACE_KEY_CONNECTOR)[1];
}
}

View File

@ -13,12 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.raft;
package com.alibaba.nacos.naming.consistency;
import com.alibaba.nacos.naming.pojo.Record;
/**
* Data listener public interface
*
* @author nacos
*/
public interface RaftListener {
public interface RecordListener<T extends Record> {
/**
* Determine if the listener was registered with this key
@ -43,14 +47,13 @@ public interface RaftListener {
* @param value data of the key
* @throws Exception
*/
void onChange(String key, String value) throws Exception;
void onChange(String key, T value) throws Exception;
/**
* Action to do if data of target key has been removed
*
* @param key target key
* @param value data of the key
* @param key target key
* @throws Exception
*/
void onDelete(String key, String value) throws Exception;
void onDelete(String key) throws Exception;
}

View File

@ -0,0 +1,35 @@
/*
* 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.ephemeral;
import com.alibaba.nacos.naming.consistency.ConsistencyService;
/**
* A type of consistency for ephemeral data.
* <p>
* This kind of consistency is not required to store data on disk or database, because the
* ephemeral data always keeps a session with server and as long as the session still lives
* the ephemeral data won't be lost.
* <p>
* What is required is that writing should always be successful even if network partition
* happens. And when the network recovers, data of each partition is merged into one set,
* so the cluster resumes to a consistent status.
*
* @author nkorange
* @since 1.0.0
*/
public interface EphemeralConsistencyService extends ConsistencyService {
}

View File

@ -0,0 +1,87 @@
/*
* 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.ephemeral.distro;
import com.alibaba.nacos.naming.consistency.Datum;
import com.alibaba.nacos.naming.core.Instances;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Store of data
*
* @author nkorange
* @since 1.0.0
*/
@Component
public class DataStore {
private Map<String, Datum> dataMap = new ConcurrentHashMap<>(1024);
public void put(String key, Datum value) {
dataMap.put(key, value);
}
public Datum remove(String key) {
return dataMap.remove(key);
}
public Set<String> keys() {
return dataMap.keySet();
}
public Datum get(String key) {
return dataMap.get(key);
}
public boolean contains(String key) {
return dataMap.containsKey(key);
}
public Map<String, Datum> batchGet(List<String> keys) {
Map<String, Datum> map = new HashMap<>(128);
for (String key : keys) {
if (!dataMap.containsKey(key)) {
continue;
}
map.put(key, dataMap.get(key));
}
return map;
}
public int getInstanceCount() {
int count = 0;
for (Map.Entry<String, Datum> entry : dataMap.entrySet()) {
try {
Datum instancesDatum = entry.getValue();
if (instancesDatum.value instanceof Instances) {
count += ((Instances) instancesDatum.value).getInstanceList().size();
}
} catch (Exception ignore) {
}
}
return count;
}
public Map<String, Datum> getDataMap() {
return dataMap;
}
}

View File

@ -0,0 +1,217 @@
/*
* 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.ephemeral.distro;
import com.alibaba.nacos.naming.cluster.ServerListManager;
import com.alibaba.nacos.naming.cluster.servers.Server;
import com.alibaba.nacos.naming.cluster.servers.ServerChangeListener;
import com.alibaba.nacos.naming.cluster.transport.Serializer;
import com.alibaba.nacos.naming.consistency.Datum;
import com.alibaba.nacos.naming.consistency.KeyBuilder;
import com.alibaba.nacos.naming.core.DistroMapper;
import com.alibaba.nacos.naming.misc.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Data replicator
*
* @author nkorange
* @since 1.0.0
*/
@Component
@DependsOn("serverListManager")
public class DataSyncer implements ServerChangeListener {
@Autowired
private DataStore dataStore;
@Autowired
private GlobalConfig partitionConfig;
@Autowired
private Serializer serializer;
@Autowired
private DistroMapper distroMapper;
@Autowired
private ServerListManager serverListManager;
private Map<String, String> taskMap = new ConcurrentHashMap<>();
private List<Server> servers;
@PostConstruct
public void init() {
serverListManager.listen(this);
startTimedSync();
}
public void submit(SyncTask task, long delay) {
// If it's a new task:
if (task.getRetryCount() == 0) {
Iterator<String> iterator = task.getKeys().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
if (StringUtils.isNotBlank(taskMap.putIfAbsent(buildKey(key, task.getTargetServer()), key))) {
// associated key already exist:
if (Loggers.EPHEMERAL.isDebugEnabled()) {
Loggers.EPHEMERAL.debug("sync already in process, key: {}", key);
}
iterator.remove();
}
}
}
if (task.getKeys().isEmpty()) {
// all keys are removed:
return;
}
GlobalExecutor.submitDataSync(new Runnable() {
@Override
public void run() {
try {
if (servers == null || servers.isEmpty()) {
Loggers.SRV_LOG.warn("try to sync data but server list is empty.");
return;
}
List<String> keys = task.getKeys();
if (Loggers.EPHEMERAL.isDebugEnabled()) {
Loggers.EPHEMERAL.debug("sync keys: {}", keys);
}
Map<String, Datum> datumMap = dataStore.batchGet(keys);
if (datumMap == null || datumMap.isEmpty()) {
// clear all flags of this task:
for (String key : task.getKeys()) {
taskMap.remove(buildKey(key, task.getTargetServer()));
}
return;
}
byte[] data = serializer.serialize(datumMap);
long timestamp = System.currentTimeMillis();
boolean success = NamingProxy.syncData(data, task.getTargetServer());
if (!success) {
SyncTask syncTask = new SyncTask();
syncTask.setKeys(task.getKeys());
syncTask.setRetryCount(task.getRetryCount() + 1);
syncTask.setLastExecuteTime(timestamp);
syncTask.setTargetServer(task.getTargetServer());
retrySync(syncTask);
} else {
// clear all flags of this task:
for (String key : task.getKeys()) {
taskMap.remove(buildKey(key, task.getTargetServer()));
}
}
} catch (Exception e) {
Loggers.EPHEMERAL.error("sync data failed.", e);
}
}
}, delay);
}
public void retrySync(SyncTask syncTask) {
Server server = new Server();
server.setIp(syncTask.getTargetServer().split(":")[0]);
server.setServePort(Integer.parseInt(syncTask.getTargetServer().split(":")[1]));
if (!servers.contains(server)) {
// if server is no longer in healthy server list, ignore this task:
return;
}
// TODO may choose other retry policy.
submit(syncTask, partitionConfig.getSyncRetryDelay());
}
public void startTimedSync() {
GlobalExecutor.schedulePartitionDataTimedSync(new TimedSync());
}
public class TimedSync implements Runnable {
@Override
public void run() {
try {
// send local timestamps to other servers:
Map<String, String> keyChecksums = new HashMap<>(64);
for (String key : dataStore.keys()) {
if (!distroMapper.responsible(KeyBuilder.getServiceName(key))) {
continue;
}
keyChecksums.put(key, dataStore.get(key).value.getChecksum());
}
if (keyChecksums.isEmpty()) {
return;
}
if (Loggers.EPHEMERAL.isDebugEnabled()) {
Loggers.EPHEMERAL.debug("sync checksums: {}", keyChecksums);
}
for (Server member : servers) {
if (NetUtils.localServer().equals(member.getKey())) {
continue;
}
NamingProxy.syncChecksums(keyChecksums, member.getKey());
}
} catch (Exception e) {
Loggers.EPHEMERAL.error("timed sync task failed.", e);
}
}
}
public List<Server> getServers() {
return servers;
}
public String buildKey(String key, String targetServer) {
return key + UtilsAndCommons.CACHE_KEY_SPLITER + targetServer;
}
@Override
public void onChangeServerList(List<Server> latestMembers) {
}
@Override
public void onChangeHealthyServerList(List<Server> healthServers) {
servers = healthServers;
}
}

View File

@ -0,0 +1,317 @@
/*
* 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.ephemeral.distro;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.core.utils.SystemUtils;
import com.alibaba.nacos.naming.cluster.ServerListManager;
import com.alibaba.nacos.naming.cluster.ServerMode;
import com.alibaba.nacos.naming.cluster.ServerStatus;
import com.alibaba.nacos.naming.cluster.servers.Server;
import com.alibaba.nacos.naming.cluster.transport.Serializer;
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.ephemeral.EphemeralConsistencyService;
import com.alibaba.nacos.naming.core.DistroMapper;
import com.alibaba.nacos.naming.core.Instances;
import com.alibaba.nacos.naming.core.Service;
import com.alibaba.nacos.naming.misc.*;
import com.alibaba.nacos.naming.pojo.Record;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A consistency protocol algorithm called <b>Partition</b>
* <p>
* Use a distro algorithm to divide data into many blocks. Each Nacos server node takes
* responsibility for exactly one block of data. Each block of data is generated, removed
* and synchronized by its responsible server. So every Nacos server only handles writings
* for a subset of the total service data.
* <p>
* At mean time every Nacos server receives data sync of other Nacos server, so every Nacos
* server will eventually have a complete set of data.
*
* @author nkorange
* @since 1.0.0
*/
@org.springframework.stereotype.Service("distroConsistencyService")
public class DistroConsistencyServiceImpl implements EphemeralConsistencyService {
@Autowired
private DistroMapper distroMapper;
@Autowired
private DataStore dataStore;
@Autowired
private TaskDispatcher taskDispatcher;
@Autowired
private DataSyncer dataSyncer;
@Autowired
private Serializer serializer;
@Autowired
private ServerListManager serverListManager;
@Autowired
private SwitchDomain switchDomain;
@Autowired
private GlobalConfig globalConfig;
private boolean initialized = false;
private volatile Map<String, List<RecordListener>> listeners = new ConcurrentHashMap<>();
@PostConstruct
public void init() throws Exception {
GlobalExecutor.submit(new Runnable() {
@Override
public void run() {
try {
load();
} catch (Exception e) {
Loggers.EPHEMERAL.error("load data failed.", e);
}
}
});
}
public void load() throws Exception {
if (SystemUtils.STANDALONE_MODE) {
initialized = true;
return;
}
// size = 1 means only myself in the list, we need at least one another server alive:
while (serverListManager.getHealthyServers().size() <= 1) {
Thread.sleep(1000L);
Loggers.EPHEMERAL.info("waiting server list init...");
}
for (Server server : serverListManager.getHealthyServers()) {
if (NetUtils.localServer().equals(server.getKey())) {
continue;
}
if (Loggers.EPHEMERAL.isDebugEnabled()) {
Loggers.EPHEMERAL.debug("sync from " + server);
}
// try sync data from remote server:
if (syncAllDataFromRemote(server)) {
initialized = true;
return;
}
}
}
@Override
public void put(String key, Record value) throws NacosException {
onPut(key, value);
taskDispatcher.addTask(key);
}
@Override
public void remove(String key) throws NacosException {
onRemove(key);
}
@Override
public Datum get(String key) throws NacosException {
return dataStore.get(key);
}
public void onPut(String key, Record value) {
if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
Datum<Instances> datum = new Datum<>();
datum.value = (Instances) value;
datum.key = key;
datum.timestamp.incrementAndGet();
dataStore.put(key, datum);
}
if (!listeners.containsKey(key)) {
return;
}
for (RecordListener listener : listeners.get(key)) {
try {
listener.onChange(key, value);
} catch (Exception e) {
Loggers.EPHEMERAL.error("notify " + listener + ", key:" + key + " failed.", e);
}
}
}
public void onRemove(String key) {
dataStore.remove(key);
if (!listeners.containsKey(key)) {
return;
}
for (RecordListener listener : listeners.get(key)) {
try {
listener.onDelete(key);
} catch (Exception e) {
Loggers.EPHEMERAL.error("notify " + listener + ", key:" + key + " failed.", e);
}
}
}
public void onReceiveChecksums(Map<String, String> checksumMap, String server) {
List<String> toUpdateKeys = new ArrayList<>();
List<String> toRemoveKeys = new ArrayList<>();
for (Map.Entry<String, String> entry : checksumMap.entrySet()) {
if (distroMapper.responsible(KeyBuilder.getServiceName(entry.getKey()))) {
// this key should not be sent from remote server:
Loggers.EPHEMERAL.error("receive responsible key timestamp of " + entry.getKey() + " from " + server);
// abort the procedure:
return;
}
if (!dataStore.contains(entry.getKey()) ||
dataStore.get(entry.getKey()).value == null ||
!dataStore.get(entry.getKey()).value.getChecksum().equals(entry.getValue())) {
toUpdateKeys.add(entry.getKey());
}
}
for (String key : dataStore.keys()) {
if (!server.equals(distroMapper.mapSrv(KeyBuilder.getServiceName(key)))) {
continue;
}
if (!checksumMap.containsKey(key)) {
toRemoveKeys.add(key);
}
}
Loggers.EPHEMERAL.info("to remove keys: {}, to update keys: {}, source: {}", toRemoveKeys, toUpdateKeys, server);
for (String key : toRemoveKeys) {
onRemove(key);
}
if (toUpdateKeys.isEmpty()) {
return;
}
try {
byte[] result = NamingProxy.getData(toUpdateKeys, server);
processData(result);
} catch (Exception e) {
Loggers.EPHEMERAL.error("get data from " + server + " failed!", e);
}
}
public boolean syncAllDataFromRemote(Server server) {
try {
byte[] data = NamingProxy.getAllData(server.getKey());
processData(data);
return true;
} catch (Exception e) {
Loggers.EPHEMERAL.error("sync full data from " + server + " failed!", e);
return false;
}
}
public void processData(byte[] data) throws Exception {
if (data.length > 0) {
Map<String, Datum<Instances>> datumMap =
serializer.deserializeMap(data, Instances.class);
for (Map.Entry<String, Datum<Instances>> entry : datumMap.entrySet()) {
dataStore.put(entry.getKey(), entry.getValue());
if (!listeners.containsKey(entry.getKey())) {
// pretty sure the service not exist:
if (ServerMode.AP.name().equals(switchDomain.getServerMode())) {
// create empty service
Service service = new Service();
String serviceName = KeyBuilder.getServiceName(entry.getKey());
String namespaceId = KeyBuilder.getNamespace(entry.getKey());
service.setName(serviceName);
service.setNamespaceId(namespaceId);
service.setGroupName(Constants.DEFAULT_GROUP);
// now validate the service. if failed, exception will be thrown
service.setLastModifiedMillis(System.currentTimeMillis());
service.recalculateChecksum();
listeners.get(KeyBuilder.SERVICE_META_KEY_PREFIX).get(0)
.onChange(KeyBuilder.buildServiceMetaKey(namespaceId, serviceName), service);
}
}
}
for (Map.Entry<String, Datum<Instances>> entry : datumMap.entrySet()) {
dataStore.put(entry.getKey(), entry.getValue());
if (!listeners.containsKey(entry.getKey())) {
Loggers.EPHEMERAL.warn("listener not found: {}", entry.getKey());
continue;
}
for (RecordListener listener : listeners.get(entry.getKey())) {
try {
listener.onChange(entry.getKey(), entry.getValue().value);
} catch (Exception e) {
Loggers.EPHEMERAL.error("notify " + listener + ", key: " + entry.getKey() + " failed.", e);
}
}
}
}
}
@Override
public void listen(String key, RecordListener listener) throws NacosException {
if (!listeners.containsKey(key)) {
listeners.put(key, new ArrayList<>());
}
listeners.get(key).add(listener);
}
@Override
public void unlisten(String key, RecordListener listener) throws NacosException {
if (!listeners.containsKey(key)) {
return;
}
for (RecordListener recordListener : listeners.get(key)) {
if (recordListener.equals(listener)) {
listeners.get(key).remove(listener);
break;
}
}
}
@Override
public boolean isAvailable() {
return isInitialized() || ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus());
}
public boolean isInitialized() {
return initialized || !globalConfig.isDataWarmup();
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.ephemeral.distro;
import java.util.List;
/**
* @author nkorange
* @since 1.0.0
*/
public class SyncTask {
private List<String> keys;
private int retryCount;
private long lastExecuteTime;
private String targetServer;
public List<String> getKeys() {
return keys;
}
public void setKeys(List<String> keys) {
this.keys = keys;
}
public int getRetryCount() {
return retryCount;
}
public void setRetryCount(int retryCount) {
this.retryCount = retryCount;
}
public long getLastExecuteTime() {
return lastExecuteTime;
}
public void setLastExecuteTime(long lastExecuteTime) {
this.lastExecuteTime = lastExecuteTime;
}
public String getTargetServer() {
return targetServer;
}
public void setTargetServer(String targetServer) {
this.targetServer = targetServer;
}
}

View File

@ -0,0 +1,136 @@
/*
* 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.ephemeral.distro;
import com.alibaba.nacos.naming.cluster.servers.Server;
import com.alibaba.nacos.naming.misc.GlobalConfig;
import com.alibaba.nacos.naming.misc.GlobalExecutor;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.NetUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Data sync task dispatcher
*
* @author nkorange
* @since 1.0.0
*/
@Component
public class TaskDispatcher {
@Autowired
private GlobalConfig partitionConfig;
@Autowired
private DataSyncer dataSyncer;
private List<TaskScheduler> taskSchedulerList = new ArrayList<>();
@PostConstruct
public void init() {
for (int i = 0; i < partitionConfig.getTaskDispatchThreadCount(); i++) {
TaskScheduler taskScheduler = new TaskScheduler(i);
taskSchedulerList.add(taskScheduler);
GlobalExecutor.submitTaskDispatch(taskScheduler);
}
}
public int mapTask(String key) {
return Math.abs(key.hashCode()) % partitionConfig.getTaskDispatchThreadCount();
}
public void addTask(String key) {
taskSchedulerList.get(mapTask(key)).addTask(key);
}
public class TaskScheduler implements Runnable {
private int index;
private int dataSize = 0;
private long lastDispatchTime = 0L;
private BlockingQueue<String> queue = new LinkedBlockingQueue<>(128 * 1024);
public TaskScheduler(int index) {
this.index = index;
}
public void addTask(String key) {
queue.offer(key);
}
public int getIndex() {
return index;
}
@Override
public void run() {
List<String> keys = new ArrayList<>();
while (true) {
try {
String key = queue.poll(partitionConfig.getTaskDispatchPeriod(),
TimeUnit.MILLISECONDS);
if (dataSyncer.getServers() == null || dataSyncer.getServers().isEmpty()) {
continue;
}
if (dataSize == 0) {
keys = new ArrayList<>();
}
if (StringUtils.isNotBlank(key)) {
keys.add(key);
dataSize++;
}
if (dataSize == partitionConfig.getBatchSyncKeyCount() ||
(System.currentTimeMillis() - lastDispatchTime) > partitionConfig.getTaskDispatchPeriod()) {
for (Server member : dataSyncer.getServers()) {
if (NetUtils.localServer().equals(member.getKey())) {
continue;
}
SyncTask syncTask = new SyncTask();
syncTask.setKeys(keys);
syncTask.setTargetServer(member.getKey());
dataSyncer.submit(syncTask, 0);
}
lastDispatchTime = System.currentTimeMillis();
dataSize = 0;
}
} catch (Exception e) {
Loggers.EPHEMERAL.error("dispatch sync task failed.", e);
}
}
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.consistency.persistent;
import com.alibaba.nacos.naming.consistency.ConsistencyService;
/**
* A consistency service that guarantee CP consistency for the published data.
* <p>
* CP consistency is hereby defined as follows:
* <p>
* Once the writing operation returned client a success, the data within the operation is guaranteed to be
* successfully written to the cluster. And the data should be consistent between servers after some time
* without any outside interfere.
*
* @author nkorange
* @since 1.0.0
*/
public interface PersistentConsistencyService extends ConsistencyService {
}

View File

@ -0,0 +1,101 @@
/*
* 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.raft;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.naming.cluster.ServerStatus;
import com.alibaba.nacos.naming.consistency.Datum;
import com.alibaba.nacos.naming.consistency.RecordListener;
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.SwitchDomain;
import com.alibaba.nacos.naming.pojo.Record;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Use simplified Raft protocol to maintain the consistency status of Nacos cluster.
*
* @author nkorange
* @since 1.0.0
*/
@Service
public class RaftConsistencyServiceImpl implements PersistentConsistencyService {
@Autowired
private RaftCore raftCore;
@Autowired
private SwitchDomain switchDomain;
@Override
public void put(String key, Record value) throws NacosException {
try {
raftCore.signalPublish(key, value);
} catch (Exception e) {
Loggers.RAFT.error("Raft put failed.", e);
throw new NacosException(NacosException.SERVER_ERROR, "Raft put failed, key:" + key + ", value:" + value);
}
}
@Override
public void remove(String key) throws NacosException {
try {
raftCore.signalDelete(key);
} catch (Exception e) {
Loggers.RAFT.error("Raft remove failed.", e);
throw new NacosException(NacosException.SERVER_ERROR, "Raft remove failed, key:" + key);
}
}
@Override
public Datum get(String key) throws NacosException {
return raftCore.getDatum(key);
}
@Override
public void listen(String key, RecordListener listener) throws NacosException {
raftCore.listen(key, listener);
}
@Override
public void unlisten(String key, RecordListener listener) throws NacosException {
raftCore.unlisten(key, listener);
}
@Override
public boolean isAvailable() {
return raftCore.isInitialized() || ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus());
}
public void onPut(Datum datum, RaftPeer source) throws NacosException {
try {
raftCore.onPublish(datum, source);
} catch (Exception e) {
Loggers.RAFT.error("Raft onPut failed.", e);
throw new NacosException(NacosException.SERVER_ERROR, "Raft onPut failed, datum:" + datum + ", source: " + source);
}
}
public void onRemove(Datum datum, RaftPeer source) throws NacosException {
try {
raftCore.onDelete(datum, source);
} catch (Exception e) {
Loggers.RAFT.error("Raft onRemove failed.", e);
throw new NacosException(NacosException.SERVER_ERROR, "Raft onRemove failed, datum:" + datum + ", source: " + source);
}
}
}

View File

@ -0,0 +1,976 @@
/*
* 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.raft;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.naming.boot.RunningConfig;
import com.alibaba.nacos.naming.consistency.ApplyAction;
import com.alibaba.nacos.naming.consistency.RecordListener;
import com.alibaba.nacos.naming.consistency.Datum;
import com.alibaba.nacos.naming.consistency.KeyBuilder;
import com.alibaba.nacos.naming.core.Instances;
import com.alibaba.nacos.naming.core.Service;
import com.alibaba.nacos.naming.misc.*;
import com.alibaba.nacos.naming.monitor.MetricsMonitor;
import com.alibaba.nacos.naming.pojo.Record;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.javatuples.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.GZIPOutputStream;
import static com.alibaba.nacos.core.utils.SystemUtils.STANDALONE_MODE;
/**
* @author nacos
*/
@Component
public class RaftCore {
public static final String API_VOTE = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/vote";
public static final String API_BEAT = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/beat";
public static final String API_PUB = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum";
public static final String API_DEL = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum";
public static final String API_GET = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum";
public static final String API_ON_PUB = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum/commit";
public static final String API_ON_DEL = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/datum/commit";
public static final String API_GET_PEER = UtilsAndCommons.NACOS_NAMING_CONTEXT + "/raft/peer";
private ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("com.alibaba.nacos.naming.raft.notifier");
return t;
}
});
public static final Lock OPERATE_LOCK = new ReentrantLock();
public static final int PUBLISH_TERM_INCREASE_COUNT = 100;
private volatile Map<String, List<RecordListener>> listeners = new ConcurrentHashMap<>();
private volatile ConcurrentMap<String, Datum> datums = new ConcurrentHashMap<>();
@Autowired
private RaftPeerSet peers;
@Autowired
private SwitchDomain switchDomain;
@Autowired
private GlobalConfig globalConfig;
@Autowired
private RaftProxy raftProxy;
@Autowired
private RaftStore raftStore;
public volatile Notifier notifier = new Notifier();
private boolean initialized = false;
@PostConstruct
public void init() throws Exception {
Loggers.RAFT.info("initializing Raft sub-system");
executor.submit(notifier);
long start = System.currentTimeMillis();
datums = raftStore.loadDatums(notifier);
setTerm(NumberUtils.toLong(raftStore.loadMeta().getProperty("term"), 0L));
Loggers.RAFT.info("cache loaded, datum count: {}, current term: {}", datums.size(), peers.getTerm());
while (true) {
if (notifier.tasks.size() <= 0) {
break;
}
Thread.sleep(1000L);
}
initialized = true;
Loggers.RAFT.info("finish to load data from disk, cost: {} ms.", (System.currentTimeMillis() - start));
GlobalExecutor.registerMasterElection(new MasterElection());
GlobalExecutor.registerHeartbeat(new HeartBeat());
Loggers.RAFT.info("timer started: leader timeout ms: {}, heart-beat timeout ms: {}",
GlobalExecutor.LEADER_TIMEOUT_MS, GlobalExecutor.HEARTBEAT_INTERVAL_MS);
}
public Map<String, List<RecordListener>> getListeners() {
return listeners;
}
public void signalPublish(String key, Record value) throws Exception {
if (!isLeader()) {
JSONObject params = new JSONObject();
params.put("key", key);
params.put("value", value);
Map<String, String> parameters = new HashMap<>(1);
parameters.put("key", key);
raftProxy.proxyPostLarge(getLeader().ip, API_PUB, params.toJSONString(), parameters);
return;
}
try {
OPERATE_LOCK.lock();
long start = System.currentTimeMillis();
final Datum datum = new Datum();
datum.key = key;
datum.value = value;
if (getDatum(key) == null) {
datum.timestamp.set(1L);
} else {
datum.timestamp.set(getDatum(key).timestamp.incrementAndGet());
}
JSONObject json = new JSONObject();
json.put("datum", datum);
json.put("source", peers.local());
onPublish(datum, peers.local());
final String content = JSON.toJSONString(json);
final CountDownLatch latch = new CountDownLatch(peers.majorityCount());
for (final String server : peers.allServersIncludeMyself()) {
if (isLeader(server)) {
latch.countDown();
continue;
}
final String url = buildURL(server, API_ON_PUB);
HttpClient.asyncHttpPostLarge(url, Arrays.asList("key=" + key), content, new AsyncCompletionHandler<Integer>() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
Loggers.RAFT.warn("[RAFT] failed to publish data to peer, datumId={}, peer={}, http code={}",
datum.key, server, response.getStatusCode());
return 1;
}
latch.countDown();
return 0;
}
@Override
public STATE onContentWriteCompleted() {
return STATE.CONTINUE;
}
});
}
if (!latch.await(UtilsAndCommons.RAFT_PUBLISH_TIMEOUT, TimeUnit.MILLISECONDS)) {
// only majority servers return success can we consider this update success
Loggers.RAFT.info("data publish failed, caused failed to notify majority, key={}", key);
throw new IllegalStateException("data publish failed, caused failed to notify majority, key=" + key);
}
long end = System.currentTimeMillis();
Loggers.RAFT.info("signalPublish cost {} ms, key: {}", (end - start), key);
} finally {
OPERATE_LOCK.unlock();
}
}
public void signalDelete(final String key) throws Exception {
OPERATE_LOCK.lock();
try {
if (!isLeader()) {
Map<String, String> params = new HashMap<>(1);
params.put("key", URLEncoder.encode(key, "UTF-8"));
raftProxy.proxy(getLeader().ip, API_DEL, params, HttpMethod.DELETE);
return;
}
JSONObject json = new JSONObject();
// construct datum:
Datum datum = new Datum();
datum.key = key;
json.put("datum", datum);
json.put("source", peers.local());
onDelete(datum, peers.local());
for (final String server : peers.allServersWithoutMySelf()) {
String url = buildURL(server, API_ON_DEL);
HttpClient.asyncHttpDeleteLarge(url, null, JSON.toJSONString(json)
, new AsyncCompletionHandler<Integer>() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
Loggers.RAFT.warn("[RAFT] failed to delete data from peer, datumId={}, peer={}, http code={}", key, server, response.getStatusCode());
return 1;
}
RaftPeer local = peers.local();
local.resetLeaderDue();
return 0;
}
});
}
} finally {
OPERATE_LOCK.unlock();
}
}
public void onPublish(Datum datum, RaftPeer source) throws Exception {
RaftPeer local = peers.local();
if (datum.value == null) {
Loggers.RAFT.warn("received empty datum");
throw new IllegalStateException("received empty datum");
}
if (!peers.isLeader(source.ip)) {
Loggers.RAFT.warn("peer {} tried to publish data but wasn't leader, leader: {}",
JSON.toJSONString(source), JSON.toJSONString(getLeader()));
throw new IllegalStateException("peer(" + source.ip + ") tried to publish " +
"data but wasn't leader");
}
if (source.term.get() < local.term.get()) {
Loggers.RAFT.warn("out of date publish, pub-term: {}, cur-term: {}",
JSON.toJSONString(source), JSON.toJSONString(local));
throw new IllegalStateException("out of date publish, pub-term:"
+ source.term.get() + ", cur-term: " + local.term.get());
}
local.resetLeaderDue();
// if data should be persistent, usually this is always true:
if (KeyBuilder.matchPersistentKey(datum.key)) {
raftStore.write(datum);
}
datums.put(datum.key, datum);
if (isLeader()) {
local.term.addAndGet(PUBLISH_TERM_INCREASE_COUNT);
} else {
if (local.term.get() + PUBLISH_TERM_INCREASE_COUNT > source.term.get()) {
//set leader term:
getLeader().term.set(source.term.get());
local.term.set(getLeader().term.get());
} else {
local.term.addAndGet(PUBLISH_TERM_INCREASE_COUNT);
}
}
raftStore.updateTerm(local.term.get());
notifier.addTask(datum, ApplyAction.CHANGE);
Loggers.RAFT.info("data added/updated, key={}, term={}", datum.key, local.term);
}
public void onDelete(Datum datum, RaftPeer source) throws Exception {
RaftPeer local = peers.local();
if (!peers.isLeader(source.ip)) {
Loggers.RAFT.warn("peer {} tried to publish data but wasn't leader, leader: {}",
JSON.toJSONString(source), JSON.toJSONString(getLeader()));
throw new IllegalStateException("peer(" + source.ip + ") tried to publish data but wasn't leader");
}
if (source.term.get() < local.term.get()) {
Loggers.RAFT.warn("out of date publish, pub-term: {}, cur-term: {}",
JSON.toJSONString(source), JSON.toJSONString(local));
throw new IllegalStateException("out of date publish, pub-term:"
+ source.term + ", cur-term: " + local.term);
}
local.resetLeaderDue();
// do apply
String key = datum.key;
deleteDatum(key);
if (KeyBuilder.matchServiceMetaKey(key)) {
if (local.term.get() + PUBLISH_TERM_INCREASE_COUNT > source.term.get()) {
//set leader term:
getLeader().term.set(source.term.get());
local.term.set(getLeader().term.get());
} else {
local.term.addAndGet(PUBLISH_TERM_INCREASE_COUNT);
}
raftStore.updateTerm(local.term.get());
}
}
public class MasterElection implements Runnable {
@Override
public void run() {
try {
if (!peers.isReady()) {
return;
}
RaftPeer local = peers.local();
local.leaderDueMs -= GlobalExecutor.TICK_PERIOD_MS;
if (local.leaderDueMs > 0) {
return;
}
// reset timeout
local.resetLeaderDue();
local.resetHeartbeatDue();
sendVote();
} catch (Exception e) {
Loggers.RAFT.warn("[RAFT] error while master election {}", e);
}
}
public void sendVote() {
RaftPeer local = peers.get(NetUtils.localServer());
Loggers.RAFT.info("leader timeout, start voting,leader: {}, term: {}",
JSON.toJSONString(getLeader()), local.term);
peers.reset();
local.term.incrementAndGet();
local.voteFor = local.ip;
local.state = RaftPeer.State.CANDIDATE;
Map<String, String> params = new HashMap<String, String>(1);
params.put("vote", JSON.toJSONString(local));
for (final String server : peers.allServersWithoutMySelf()) {
final String url = buildURL(server, API_VOTE);
try {
HttpClient.asyncHttpPost(url, null, params, new AsyncCompletionHandler<Integer>() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
Loggers.RAFT.error("NACOS-RAFT vote failed: {}, url: {}", response.getResponseBody(), url);
return 1;
}
RaftPeer peer = JSON.parseObject(response.getResponseBody(), RaftPeer.class);
Loggers.RAFT.info("received approve from peer: {}", JSON.toJSONString(peer));
peers.decideLeader(peer);
return 0;
}
});
} catch (Exception e) {
Loggers.RAFT.warn("error while sending vote to server: {}", server);
}
}
}
}
public RaftPeer receivedVote(RaftPeer remote) {
if (!peers.contains(remote)) {
throw new IllegalStateException("can not find peer: " + remote.ip);
}
RaftPeer local = peers.get(NetUtils.localServer());
if (remote.term.get() <= local.term.get()) {
String msg = "received illegitimate vote" +
", voter-term:" + remote.term + ", votee-term:" + local.term;
Loggers.RAFT.info(msg);
if (StringUtils.isEmpty(local.voteFor)) {
local.voteFor = local.ip;
}
return local;
}
local.resetLeaderDue();
local.state = RaftPeer.State.FOLLOWER;
local.voteFor = remote.ip;
local.term.set(remote.term.get());
Loggers.RAFT.info("vote {} as leader, term: {}", remote.ip, remote.term);
return local;
}
public class HeartBeat implements Runnable {
@Override
public void run() {
try {
if (!peers.isReady()) {
return;
}
RaftPeer local = peers.local();
local.heartbeatDueMs -= GlobalExecutor.TICK_PERIOD_MS;
if (local.heartbeatDueMs > 0) {
return;
}
local.resetHeartbeatDue();
sendBeat();
} catch (Exception e) {
Loggers.RAFT.warn("[RAFT] error while sending beat {}", e);
}
}
public void sendBeat() throws IOException, InterruptedException {
RaftPeer local = peers.local();
if (local.state != RaftPeer.State.LEADER && !STANDALONE_MODE) {
return;
}
Loggers.RAFT.info("[RAFT] send beat with {} keys.", datums.size());
local.resetLeaderDue();
// build data
JSONObject packet = new JSONObject();
packet.put("peer", local);
JSONArray array = new JSONArray();
if (switchDomain.isSendBeatOnly()) {
Loggers.RAFT.info("[SEND-BEAT-ONLY] {}", String.valueOf(switchDomain.isSendBeatOnly()));
}
if (!switchDomain.isSendBeatOnly()) {
for (Datum datum : datums.values()) {
JSONObject element = new JSONObject();
if (KeyBuilder.matchServiceMetaKey(datum.key)) {
element.put("key", KeyBuilder.briefServiceMetaKey(datum.key));
} else if (KeyBuilder.matchInstanceListKey(datum.key)) {
element.put("key", KeyBuilder.briefInstanceListkey(datum.key));
}
element.put("timestamp", datum.timestamp);
array.add(element);
}
} else {
Loggers.RAFT.info("[RAFT] send beat only.");
}
packet.put("datums", array);
// broadcast
Map<String, String> params = new HashMap<String, String>(1);
params.put("beat", JSON.toJSONString(packet));
String content = JSON.toJSONString(params);
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(out);
gzip.write(content.getBytes("UTF-8"));
gzip.close();
byte[] compressedBytes = out.toByteArray();
String compressedContent = new String(compressedBytes, "UTF-8");
Loggers.RAFT.info("raw beat data size: {}, size of compressed data: {}",
content.length(), compressedContent.length());
for (final String server : peers.allServersWithoutMySelf()) {
try {
final String url = buildURL(server, API_BEAT);
Loggers.RAFT.info("send beat to server " + server);
HttpClient.asyncHttpPostLarge(url, null, compressedBytes, new AsyncCompletionHandler<Integer>() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
Loggers.RAFT.error("NACOS-RAFT beat failed: {}, peer: {}",
response.getResponseBody(), server);
MetricsMonitor.getLeaderSendBeatFailedException().increment();
return 1;
}
peers.update(JSON.parseObject(response.getResponseBody(), RaftPeer.class));
Loggers.RAFT.info("receive beat response from: {}", url);
return 0;
}
@Override
public void onThrowable(Throwable t) {
Loggers.RAFT.error("NACOS-RAFT error while sending heart-beat to peer: {} {}", server, t);
MetricsMonitor.getLeaderSendBeatFailedException().increment();
}
});
} catch (Exception e) {
Loggers.RAFT.error("VIPSRV error while sending heart-beat to peer: {} {}", server, e);
MetricsMonitor.getLeaderSendBeatFailedException().increment();
}
}
}
}
public RaftPeer receivedBeat(JSONObject beat) throws Exception {
final RaftPeer local = peers.local();
final RaftPeer remote = new RaftPeer();
remote.ip = beat.getJSONObject("peer").getString("ip");
remote.state = RaftPeer.State.valueOf(beat.getJSONObject("peer").getString("state"));
remote.term.set(beat.getJSONObject("peer").getLongValue("term"));
remote.heartbeatDueMs = beat.getJSONObject("peer").getLongValue("heartbeatDueMs");
remote.leaderDueMs = beat.getJSONObject("peer").getLongValue("leaderDueMs");
remote.voteFor = beat.getJSONObject("peer").getString("voteFor");
if (remote.state != RaftPeer.State.LEADER) {
Loggers.RAFT.info("[RAFT] invalid state from master, state: {}, remote peer: {}",
remote.state, JSON.toJSONString(remote));
throw new IllegalArgumentException("invalid state from master, state: " + remote.state);
}
if (local.term.get() > remote.term.get()) {
Loggers.RAFT.info("[RAFT] out of date beat, beat-from-term: {}, beat-to-term: {}, remote peer: {}, and leaderDueMs: {}"
, remote.term.get(), local.term.get(), JSON.toJSONString(remote), local.leaderDueMs);
throw new IllegalArgumentException("out of date beat, beat-from-term: " + remote.term.get()
+ ", beat-to-term: " + local.term.get());
}
if (local.state != RaftPeer.State.FOLLOWER) {
Loggers.RAFT.info("[RAFT] make remote as leader, remote peer: {}", JSON.toJSONString(remote));
// mk follower
local.state = RaftPeer.State.FOLLOWER;
local.voteFor = remote.ip;
}
final JSONArray beatDatums = beat.getJSONArray("datums");
local.resetLeaderDue();
local.resetHeartbeatDue();
peers.makeLeader(remote);
Map<String, Integer> receivedKeysMap = new HashMap<String, Integer>(datums.size());
for (Map.Entry<String, Datum> entry : datums.entrySet()) {
receivedKeysMap.put(entry.getKey(), 0);
}
// now check datums
List<String> batch = new ArrayList<String>();
if (!switchDomain.isSendBeatOnly()) {
int processedCount = 0;
Loggers.RAFT.info("[RAFT] received beat with {} keys, RaftCore.datums' size is {}, remote server: {}, term: {}, local term: {}",
beatDatums.size(), datums.size(), remote.ip, remote.term, local.term);
for (Object object : beatDatums) {
processedCount = processedCount + 1;
JSONObject entry = (JSONObject) object;
String key = entry.getString("key");
final String datumKey;
if (KeyBuilder.matchServiceMetaKey(key)) {
datumKey = KeyBuilder.detailServiceMetaKey(key);
} else if (KeyBuilder.matchInstanceListKey(key)) {
datumKey = KeyBuilder.detailInstanceListkey(key);
} else {
// ignore corrupted key:
continue;
}
long timestamp = entry.getLong("timestamp");
receivedKeysMap.put(datumKey, 1);
try {
if (datums.containsKey(datumKey) && datums.get(datumKey).timestamp.get() >= timestamp && processedCount < beatDatums.size()) {
continue;
}
if (!(datums.containsKey(datumKey) && datums.get(datumKey).timestamp.get() >= timestamp)) {
batch.add(datumKey);
}
if (batch.size() < 50 && processedCount < beatDatums.size()) {
continue;
}
String keys = StringUtils.join(batch, ",");
if (batch.size() <= 0) {
continue;
}
Loggers.RAFT.info("get datums from leader: {}, batch size is {}, processedCount is {}, datums' size is {}, RaftCore.datums' size is {}"
, getLeader().ip, batch.size(), processedCount, beatDatums.size(), datums.size());
// update datum entry
String url = buildURL(remote.ip, API_GET) + "?keys=" + URLEncoder.encode(keys, "UTF-8");
HttpClient.asyncHttpGet(url, null, null, new AsyncCompletionHandler<Integer>() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
return 1;
}
List<Datum> datumList = JSON.parseObject(response.getResponseBody(), new TypeReference<List<Datum>>() {
});
for (Datum datum : datumList) {
OPERATE_LOCK.lock();
try {
Datum oldDatum = getDatum(datum.key);
if (oldDatum != null && datum.timestamp.get() <= oldDatum.timestamp.get()) {
Loggers.RAFT.info("[NACOS-RAFT] timestamp is smaller than that of mine, key: {}, remote: {}, local: {}",
datum.key, datum.timestamp, oldDatum.timestamp);
continue;
}
raftStore.write(datum);
if (KeyBuilder.matchServiceMetaKey(datum.key)) {
Datum<Service> serviceDatum = new Datum<>();
serviceDatum.key = datum.key;
serviceDatum.timestamp.set(datum.timestamp.get());
serviceDatum.value = JSON.parseObject(JSON.toJSONString(datum.value), Service.class);
datum = serviceDatum;
}
if (KeyBuilder.matchInstanceListKey(datum.key)) {
Datum<Instances> instancesDatum = new Datum<>();
instancesDatum.key = datum.key;
instancesDatum.timestamp.set(datum.timestamp.get());
instancesDatum.value = JSON.parseObject(JSON.toJSONString(datum.value), Instances.class);
datum = instancesDatum;
}
datums.put(datum.key, datum);
notifier.addTask(datum, ApplyAction.CHANGE);
local.resetLeaderDue();
if (local.term.get() + 100 > remote.term.get()) {
getLeader().term.set(remote.term.get());
local.term.set(getLeader().term.get());
} else {
local.term.addAndGet(100);
}
raftStore.updateTerm(local.term.get());
Loggers.RAFT.info("data updated, key: {}, timestamp: {}, from {}, local term: {}",
datum.key, datum.timestamp, JSON.toJSONString(remote), local.term);
} catch (Throwable e) {
Loggers.RAFT.error("[RAFT-BEAT] failed to sync datum from leader, key: {} {}", datum.key, e);
} finally {
OPERATE_LOCK.unlock();
}
}
TimeUnit.MILLISECONDS.sleep(200);
return 0;
}
});
batch.clear();
} catch (Exception e) {
Loggers.RAFT.error("[NACOS-RAFT] failed to handle beat entry, key: {}", datumKey);
}
}
List<String> deadKeys = new ArrayList<String>();
for (Map.Entry<String, Integer> entry : receivedKeysMap.entrySet()) {
if (entry.getValue() == 0) {
deadKeys.add(entry.getKey());
}
}
for (String deadKey : deadKeys) {
try {
deleteDatum(deadKey);
} catch (Exception e) {
Loggers.RAFT.error("[NACOS-RAFT] failed to remove entry, key={} {}", deadKey, e);
}
}
}
return local;
}
public void listen(String key, RecordListener listener) {
List<RecordListener> listenerList = listeners.get(key);
if (listenerList != null && listenerList.contains(listener)) {
return;
}
if (listenerList == null) {
listenerList = new CopyOnWriteArrayList<>();
listeners.put(key, listenerList);
}
Loggers.RAFT.info("add listener: {}", key);
listenerList.add(listener);
// if data present, notify immediately
for (Datum datum : datums.values()) {
if (!listener.interests(datum.key)) {
continue;
}
try {
listener.onChange(datum.key, datum.value);
} catch (Exception e) {
Loggers.RAFT.error("NACOS-RAFT failed to notify listener", e);
}
}
}
public void unlisten(String key, RecordListener listener) {
if (!listeners.containsKey(key)) {
return;
}
for (RecordListener dl : listeners.get(key)) {
// TODO maybe use equal:
if (dl == listener) {
listeners.get(key).remove(listener);
break;
}
}
}
public void setTerm(long term) {
peers.setTerm(term);
}
public boolean isLeader(String ip) {
return peers.isLeader(ip);
}
public boolean isLeader() {
return peers.isLeader(NetUtils.localServer());
}
public static String buildURL(String ip, String api) {
if (!ip.contains(UtilsAndCommons.IP_PORT_SPLITER)) {
ip = ip + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort();
}
return "http://" + ip + RunningConfig.getContextPath() + api;
}
public Datum<?> getDatum(String key) {
return datums.get(key);
}
public RaftPeer getLeader() {
return peers.getLeader();
}
public List<RaftPeer> getPeers() {
return new ArrayList<>(peers.allPeers());
}
public RaftPeerSet getPeerSet() {
return peers;
}
public void setPeerSet(RaftPeerSet peerSet) {
peers = peerSet;
}
public int datumSize() {
return datums.size();
}
public void addDatum(Datum datum) {
datums.put(datum.key, datum);
notifier.addTask(datum, ApplyAction.CHANGE);
}
public void loadDatum(String key) {
try {
Datum datum = raftStore.load(key);
if (datum == null) {
return;
}
datums.put(key, datum);
} catch (Exception e) {
Loggers.RAFT.error("load datum failed: " + key, e);
}
}
private void deleteDatum(String key) {
Datum deleted = null;
try {
deleted = datums.remove(URLDecoder.decode(key, "UTF-8"));
} catch (UnsupportedEncodingException e) {
Loggers.RAFT.warn("datum key decode failed: {}", key);
}
// FIXME should we ignore the value of 'deleted'?
if (deleted != null) {
raftStore.delete(deleted);
notifier.addTask(deleted, ApplyAction.DELETE);
Loggers.RAFT.info("datum deleted, key: {}", key);
}
}
public boolean isInitialized() {
return initialized || !globalConfig.isDataWarmup();
}
public class Notifier implements Runnable {
private ConcurrentHashMap<String, String> services = new ConcurrentHashMap<>(10 * 1024);
private BlockingQueue<Pair> tasks = new LinkedBlockingQueue<Pair>(1024 * 1024);
public void addTask(Datum datum, ApplyAction action) {
if (services.containsKey(datum.key) && action == ApplyAction.CHANGE) {
return;
}
if (action == ApplyAction.CHANGE) {
services.put(datum.key, StringUtils.EMPTY);
}
tasks.add(Pair.with(datum, 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;
}
Datum datum = (Datum) pair.getValue0();
ApplyAction action = (ApplyAction) pair.getValue1();
services.remove(datum.key);
int count = 0;
if (listeners.containsKey(KeyBuilder.SERVICE_META_KEY_PREFIX)) {
if (KeyBuilder.matchServiceMetaKey(datum.key) && !KeyBuilder.matchSwitchKey(datum.key)) {
for (RecordListener listener : listeners.get(KeyBuilder.SERVICE_META_KEY_PREFIX)) {
try {
if (action == ApplyAction.CHANGE) {
listener.onChange(datum.key, getDatum(datum.key).value);
}
if (action == ApplyAction.DELETE) {
listener.onDelete(datum.key);
}
} catch (Throwable e) {
Loggers.RAFT.error("[NACOS-RAFT] error while notifying listener of key: {} {}", datum.key, e);
}
}
}
}
if (!listeners.containsKey(datum.key)) {
continue;
}
for (RecordListener listener : listeners.get(datum.key)) {
count++;
try {
if (action == ApplyAction.CHANGE) {
listener.onChange(datum.key, getDatum(datum.key).value);
continue;
}
if (action == ApplyAction.DELETE) {
listener.onDelete(datum.key);
continue;
}
} catch (Throwable e) {
Loggers.RAFT.error("[NACOS-RAFT] error while notifying listener of key: {} {}", datum.key, e);
}
}
if (Loggers.RAFT.isDebugEnabled()) {
Loggers.RAFT.debug("[NACOS-RAFT] datum change notified, key: {}, listener count: {}", datum.key, count);
}
} catch (Throwable e) {
Loggers.RAFT.error("[NACOS-RAFT] Error while handling notifying task", e);
}
}
}
}
}

View File

@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.raft;
package com.alibaba.nacos.naming.consistency.persistent.raft;
import com.alibaba.nacos.naming.misc.GlobalExecutor;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
@ -34,7 +35,7 @@ public class RaftPeer {
public volatile long leaderDueMs = RandomUtils.nextLong(0, GlobalExecutor.LEADER_TIMEOUT_MS);
public volatile long heartbeatDueMs = RandomUtils.nextLong(0, GlobalExecutor.HEARTBEAT_INTERVAL_MS);
public volatile long heartbeatDueMs = RandomUtils.nextLong(0, GlobalExecutor.HEARTBEAT_INTERVAL_MS);
public State state = State.FOLLOWER;

View File

@ -13,9 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.raft;
package com.alibaba.nacos.naming.consistency.persistent.raft;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.core.utils.SystemUtils;
import com.alibaba.nacos.naming.boot.RunningConfig;
import com.alibaba.nacos.naming.cluster.ServerListManager;
import com.alibaba.nacos.naming.cluster.servers.Server;
import com.alibaba.nacos.naming.cluster.servers.ServerChangeListener;
import com.alibaba.nacos.naming.misc.HttpClient;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.NetUtils;
@ -24,24 +29,44 @@ import com.ning.http.client.Response;
import org.apache.commons.collections.SortedBag;
import org.apache.commons.collections.bag.TreeBag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.net.HttpURLConnection;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import static com.alibaba.nacos.core.utils.SystemUtils.STANDALONE_MODE;
/**
* @author nacos
*/
public class PeerSet {
@Component
@DependsOn("serverListManager")
public class RaftPeerSet implements ServerChangeListener {
@Autowired
private ServerListManager serverListManager;
private AtomicLong localTerm = new AtomicLong(0L);
private RaftPeer leader = null;
private static Map<String, RaftPeer> peers = new HashMap<String, RaftPeer>();
private Map<String, RaftPeer> peers = new HashMap<String, RaftPeer>();
private static Set<String> sites = new HashSet<>();
private Set<String> sites = new HashSet<>();
public PeerSet() {
private boolean ready = false;
public RaftPeerSet() {
}
@PostConstruct
public void init() {
serverListManager.listen(this);
}
public RaftPeer getLeader() {
@ -55,20 +80,8 @@ public class PeerSet {
return sites;
}
public void add(List<String> servers) {
for (String server : servers) {
RaftPeer peer = new RaftPeer();
peer.ip = server;
peers.put(server, peer);
}
if (STANDALONE_MODE) {
RaftPeer local = local();
local.state = RaftPeer.State.LEADER;
local.voteFor = NetUtils.localServer();
}
public boolean isReady() {
return ready;
}
public void remove(List<String> servers) {
@ -159,7 +172,7 @@ public class PeerSet {
if (!Objects.equals(peer, candidate) && peer.state == RaftPeer.State.LEADER) {
try {
String url = RaftCore.buildURL(peer.ip, RaftCore.API_GET_PEER);
HttpClient.asyncHttpPost(url, null, params, new AsyncCompletionHandler<Integer>() {
HttpClient.asyncHttpGet(url, null, params, new AsyncCompletionHandler<Integer>() {
@Override
public Integer onCompleted(Response response) throws Exception {
if (response.getStatusCode() != HttpURLConnection.HTTP_OK) {
@ -186,6 +199,13 @@ public class PeerSet {
public RaftPeer local() {
RaftPeer peer = peers.get(NetUtils.localServer());
if (peer == null && SystemUtils.STANDALONE_MODE) {
RaftPeer localPeer = new RaftPeer();
localPeer.ip = NetUtils.localServer();
localPeer.term.set(localTerm.get());
peers.put(localPeer.ip, localPeer);
return localPeer;
}
if (peer == null) {
throw new IllegalStateException("unable to find local peer: " + NetUtils.localServer() + ", all peers: "
+ Arrays.toString(peers.keySet().toArray()));
@ -212,20 +232,51 @@ public class PeerSet {
}
public void setTerm(long term) {
RaftPeer local = local();
if (term < local.term.get()) {
return;
}
local.term.set(term);
localTerm.set(term);
}
public long getTerm() {
return local().term.get();
return localTerm.get();
}
public boolean contains(RaftPeer remote) {
return peers.containsKey(remote.ip);
}
@Override
public void onChangeServerList(List<Server> latestMembers) {
Map<String, RaftPeer> tmpPeers = new HashMap<>(8);
for (Server member : latestMembers) {
if (peers.containsKey(member.getKey())) {
tmpPeers.put(member.getKey(), peers.get(member.getKey()));
continue;
}
RaftPeer raftPeer = new RaftPeer();
raftPeer.ip = member.getKey();
// first time meet the local server:
if (NetUtils.localServer().equals(member.getKey())) {
raftPeer.term.set(localTerm.get());
}
tmpPeers.put(member.getKey(), raftPeer);
}
// replace raft peer set:
peers = tmpPeers;
if (RunningConfig.getServerPort() > 0) {
ready = true;
}
Loggers.RAFT.info("raft peers changed: " + latestMembers);
}
@Override
public void onChangeHealthyServerList(List<Server> latestReachableMembers) {
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.raft;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.boot.RunningConfig;
import com.alibaba.nacos.naming.misc.HttpClient;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import java.net.HttpURLConnection;
import java.util.Map;
/**
* @author nacos
*/
@Component
public class RaftProxy {
public void proxyGET(String server, String api, Map<String, String> params) throws Exception {
// do proxy
if (!server.contains(UtilsAndCommons.IP_PORT_SPLITER)) {
server = server + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort();
}
String url = "http://" + server + RunningConfig.getContextPath() + api;
HttpClient.HttpResult result = HttpClient.httpGet(url, null, params);
if (result.code != HttpURLConnection.HTTP_OK) {
throw new IllegalStateException("leader failed, caused by: " + result.content);
}
}
public void proxy(String server, String api, Map<String, String> params, HttpMethod method) throws Exception {
// do proxy
if (!server.contains(UtilsAndCommons.IP_PORT_SPLITER)) {
server = server + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort();
}
String url = "http://" + server + RunningConfig.getContextPath() + api;
HttpClient.HttpResult result;
switch (method) {
case GET:
result = HttpClient.httpGet(url, null, params);
break;
case POST:
result = HttpClient.httpPost(url, null, params);
break;
case DELETE:
result = HttpClient.httpDelete(url, null, params);
break;
default:
throw new RuntimeException("unsupported method:" + method);
}
if (result.code != HttpURLConnection.HTTP_OK) {
throw new IllegalStateException("leader failed, caused by: " + result.content);
}
}
public void proxyPostLarge(String server, String api, String content, Map<String, String> headers) throws Exception {
// do proxy
if (!server.contains(UtilsAndCommons.IP_PORT_SPLITER)) {
server = server + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort();
}
String url = "http://" + server + RunningConfig.getContextPath() + api;
HttpClient.HttpResult result = HttpClient.httpPostLarge(url, headers, content);
if (result.code != HttpURLConnection.HTTP_OK) {
throw new IllegalStateException("leader failed, caused by: " + result.content);
}
}
}

Some files were not shown because too many files have changed in this diff Show More