Merge pull request #902 from alibaba/feature_naming_group
Feature naming group
This commit is contained in:
commit
e4255e1281
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
}
|
@ -23,7 +23,7 @@ import com.alibaba.nacos.api.exception.NacosException;
|
||||
/**
|
||||
* Naming Factory
|
||||
*
|
||||
* @author dungu.zpf
|
||||
* @author nkorange
|
||||
*/
|
||||
public class NamingFactory {
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -18,7 +18,7 @@ package com.alibaba.nacos.api.naming.listener;
|
||||
/**
|
||||
* Event Interface
|
||||
*
|
||||
* @author dungu.zpf
|
||||
* @author nkorange
|
||||
*/
|
||||
public interface Event {
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import com.alibaba.nacos.api.naming.pojo.Instance;
|
||||
/**
|
||||
* Naming Event
|
||||
*
|
||||
* @author dungu.zpf
|
||||
* @author nkorange
|
||||
*/
|
||||
public class NamingEvent implements Event {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -21,7 +21,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Cluster
|
||||
*
|
||||
* @author dungu.zpf
|
||||
* @author nkorange
|
||||
*/
|
||||
public class Cluster {
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import java.util.List;
|
||||
/**
|
||||
* ListView
|
||||
*
|
||||
* @author dungu.zpf
|
||||
* @author nkorange
|
||||
*/
|
||||
public class ListView<T> {
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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> {
|
||||
|
||||
|
@ -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> {
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
||||
|
@ -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> {
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -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";
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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>-->
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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}"
|
||||
|
@ -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
|
||||
|
@ -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"/>
|
||||
|
Binary file not shown.
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user