Merge branch 'feature_cmdb' into 0.7.0

# Conflicts:
#	api/pom.xml
#	api/src/main/java/com/alibaba/nacos/api/exception/NacosException.java
#	client/pom.xml
#	client/src/main/java/com/alibaba/nacos/client/config/impl/HttpSimpleClient.java
#	client/src/main/java/com/alibaba/nacos/client/naming/net/NamingProxy.java
#	common/pom.xml
#	config/pom.xml
#	console/pom.xml
#	core/pom.xml
#	distribution/conf/application.properties
#	distribution/conf/nacos-logback.xml
#	distribution/pom.xml
#	distribution/release-nacos.xml
#	example/pom.xml
#	naming/pom.xml
#	naming/src/main/java/com/alibaba/nacos/naming/web/ApiCommands.java
#	pom.xml
#	test/pom.xml
This commit is contained in:
nkorange 2018-12-14 17:15:32 +08:00
commit d2e4f4e6b4
56 changed files with 2130 additions and 460 deletions

View File

@ -0,0 +1,52 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.api.cmdb.pojo;
import java.util.Map;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class Entity {
private String type;
private String name;
private Map<String, String> labels;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getLabels() {
return labels;
}
public void setLabels(Map<String, String> labels) {
this.labels = labels;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.cmdb.pojo;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class EntityEvent {
private EntityEventType type;
private String entityName;
private String entityType;
public EntityEventType getType() {
return type;
}
public void setType(EntityEventType type) {
this.type = type;
}
public String getEntityName() {
return entityName;
}
public void setEntityName(String entityName) {
this.entityName = entityName;
}
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.api.cmdb.pojo;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public enum EntityEventType {
/**
*
*/
ENTITY_ADD_OR_UPDATE,
/**
*
*/
ENTITY_REMOVE
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.api.cmdb.pojo;
import java.util.Set;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class Label {
private String name;
private Set<String> values;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<String> getValues() {
return values;
}
public void setValues(Set<String> values) {
this.values = values;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.api.cmdb.pojo;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public enum PreservedEntityTypes {
/**
*
*/
ip,
/**
*
*/
service
}

View File

@ -0,0 +1,97 @@
/*
* 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.cmdb.spi;
import com.alibaba.nacos.api.cmdb.pojo.Entity;
import com.alibaba.nacos.api.cmdb.pojo.EntityEvent;
import com.alibaba.nacos.api.cmdb.pojo.Label;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Service to visit CMDB store
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public interface CmdbService {
/**
* Get all label names stored in CMDB
*
* @return label name set
*/
Set<String> getLabelNames();
/**
* Get all possible entity types in CMDB
*
* @return all entity types
*/
Set<String> getEntityTypes();
/**
* Get label info
*
* @param labelName label name
* @return label info
*/
Label getLabel(String labelName);
/**
* Get label value of label name of ip
*
* @param entityType entity type
* @param entityValue entity value
* @param labelName target label name
* @return label value
*/
String getLabelValue(String entityValue, String entityType, String labelName);
/**
* Get all label value of ip
*
* @param entityType entity type
* @param entityValue entity value
* @return all label values
*/
Map<String, String> getLabelValues(String entityValue, String entityType);
/**
* Dump all entities in CMDB
*
* @return all entities
*/
Map<String, Map<String, Entity>> getAllEntities();
/**
* get label change events
*
* @param timestamp start time of generated events
* @return label events
*/
List<EntityEvent> getEntityEvents(long timestamp);
/**
* Get single entity
*
* @param entityName name of entity
* @param entityType type of entity
* @return
*/
Entity getEntity(String entityName, String entityType);
}

View File

@ -20,6 +20,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.selector.AbstractSelector;
import java.util.List;
@ -186,6 +187,17 @@ public interface NamingService {
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize) throws NacosException;
/**
* Get all service names from server
*
* @param pageNo page index
* @param pageSize page size
* @param selector selector to filter the resource
* @return list of service names
* @throws NacosException
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException;
/**
* Get all subscribed services of current client
*

View File

@ -15,8 +15,6 @@
*/
package com.alibaba.nacos.api.naming.pojo;
import com.alibaba.nacos.api.common.Constants;
import java.util.HashMap;
import java.util.Map;

View File

@ -15,6 +15,8 @@
*/
package com.alibaba.nacos.api.naming.pojo;
import com.alibaba.nacos.api.selector.AbstractSelector;
import java.util.HashMap;
import java.util.Map;
@ -39,15 +41,20 @@ public class Service {
private String app;
/**
* Service group is meant to classify services into different sets.
* Service group is meant to classify services into different sets
*/
private String group;
/**
* Health check mode.
* Health check mode
*/
private String healthCheckMode;
/**
* Selector name of this service
*/
private AbstractSelector selector;
private Map<String, String> metadata = new HashMap<String, String>();
public Service(String name) {
@ -105,4 +112,12 @@ public class Service {
public void addMetadata(String key, String value) {
this.metadata.put(key, value);
}
public AbstractSelector getSelector() {
return selector;
}
public void setSelector(AbstractSelector selector) {
this.selector = selector;
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.selector;
/**
* Abstract selector that only contains a type
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public abstract class AbstractSelector {
/**
* The type of this selector, each child class should announce its own unique type.
*/
private String type;
public String getType() {
return type;
}
protected void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.api.selector;
/**
* The selector to filter resource with flexible expression.
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class ExpressionSelector extends AbstractSelector {
/**
* Label expression of this selector.
*/
private String expression;
public ExpressionSelector() {
this.setType(SelectorType.label.name());
}
public String getExpression() {
return expression;
}
public void setExpression(String expression) {
this.expression = expression;
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.selector;
/**
* The types of selector accepted by Nacos
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public enum SelectorType {
/**
* not match any type
*/
unknown,
/**
* not filter out any entity
*/
none,
/**
* select by label
*/
label
}

View File

@ -20,7 +20,7 @@ import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.config.utils.IOUtils;
import com.alibaba.nacos.client.config.utils.MD5;
import com.alibaba.nacos.client.utils.ParamUtil;
import com.alibaba.nacos.common.util.UuidUtil;
import com.alibaba.nacos.common.util.UuidUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@ -36,6 +36,7 @@ import java.util.Map;
* Http tool
*
* @author Nacos
*
*/
public class HttpSimpleClient {

View File

@ -24,6 +24,7 @@ import com.alibaba.nacos.api.naming.pojo.Cluster;
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.selector.AbstractSelector;
import com.alibaba.nacos.client.naming.beat.BeatInfo;
import com.alibaba.nacos.client.naming.beat.BeatReactor;
import com.alibaba.nacos.client.naming.core.Balancer;
@ -263,6 +264,11 @@ public class NacosNamingService implements NamingService {
return serverProxy.getServiceList(pageNo, pageSize);
}
@Override
public ListView<String> getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException {
return serverProxy.getServiceList(pageNo, pageSize, selector);
}
@Override
public List<ServiceInfo> getSubscribeServices() {
return new ArrayList<ServiceInfo>(hostReactor.getServiceInfoMap().values());

View File

@ -21,8 +21,11 @@ import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.api.selector.AbstractSelector;
import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.api.selector.ExpressionSelector;
import com.alibaba.nacos.client.naming.utils.*;
import com.alibaba.nacos.common.util.UuidUtil;
import com.alibaba.nacos.common.util.UuidUtils;
import java.io.IOException;
import java.io.StringReader;
@ -95,7 +98,7 @@ public class NamingProxy {
List<String> headers = Arrays.asList("Client-Version", UtilAndComs.VERSION,
"Accept-Encoding", "gzip,deflate,sdch",
"Connection", "Keep-Alive",
"RequestId", UuidUtil.generateUuid());
"RequestId", UuidUtils.generateUuid());
HttpClient.HttpResult result = HttpClient.httpGet(urlString, headers, null, UtilAndComs.ENCODING);
if (HttpURLConnection.HTTP_OK != result.code) {
@ -193,11 +196,6 @@ public class NamingProxy {
return reqAPI(UtilAndComs.NACOS_URL_BASE + "/instance/list", params, "GET");
}
private String doRegDom(Map<String, String> params) throws Exception {
String api = UtilAndComs.NACOS_URL_BASE + "/api/regService";
return reqAPI(api, params);
}
public boolean serverHealthy() {
try {
@ -210,11 +208,28 @@ public class NamingProxy {
}
public ListView<String> getServiceList(int pageNo, int pageSize) throws NacosException {
return getServiceList(pageNo, pageSize, null);
}
public ListView<String> getServiceList(int pageNo, int pageSize, 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));
if (selector != null) {
switch (SelectorType.valueOf(selector.getType())) {
case none:
break;
case label:
ExpressionSelector expressionSelector = (ExpressionSelector) selector;
params.put("selector", JSON.toJSONString(expressionSelector));
break;
default:
break;
}
}
String result = reqAPI(UtilAndComs.NACOS_URL_BASE + "/service/list", params);
JSONObject json = JSON.parseObject(result);
@ -249,6 +264,7 @@ public class NamingProxy {
public String reqAPI(String api, Map<String, String> params) throws NacosException {
List<String> snapshot = serversFromEndpoint;
if (!CollectionUtils.isEmpty(serverList)) {
snapshot = serverList;

View File

@ -18,6 +18,8 @@ package com.alibaba.nacos.client;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.api.selector.ExpressionSelector;
import org.junit.Ignore;
import org.junit.Test;
@ -46,6 +48,10 @@ public class NamingTest {
namingService.registerInstance("dungu.test.1", instance);
ExpressionSelector expressionSelector = new ExpressionSelector();
expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'dubbo'");
ListView<String> serviceList = namingService.getServicesOfServer(1, 10, expressionSelector);
Thread.sleep(1000000000L);
}

106
cmdb/pom.xml Normal file
View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nacos-all</artifactId>
<groupId>com.alibaba.nacos</groupId>
<version>0.5.0-cmdb-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nacos-cmdb</artifactId>
<packaging>jar</packaging>
<name>nacos-cmdb ${project.version}</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacos-common</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacos-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
<argLine>-Dnacos.standalone=true</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.alibaba.nacos.cmdb.CmdbApp</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application.properties</exclude>
</excludes>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,30 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.cmdb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
@SpringBootApplication
public class CmdbApp {
public static void main(String[] args) {
SpringApplication.run(CmdbApp.class, args);
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.cmdb.controllers;
import com.alibaba.nacos.cmdb.core.SwitchAndOptions;
import com.alibaba.nacos.cmdb.memory.CmdbProvider;
import com.alibaba.nacos.cmdb.utils.UtilsAndCommons;
import com.alibaba.nacos.common.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
@RestController
@RequestMapping(UtilsAndCommons.NACOS_CMDB_CONTEXT + "/ops")
public class OperationController {
@Autowired
private SwitchAndOptions switches;
@Autowired
private CmdbProvider cmdbProvider;
@RequestMapping(value = "/updateSwitch", method = RequestMethod.POST)
public String updateSwitch(HttpServletRequest request) throws Exception {
String entry = WebUtils.required(request, "entry");
String value = WebUtils.required(request, "value");
switch (entry) {
case "dumpTaskInterval":
switches.setDumpTaskInterval(Integer.parseInt(value));
break;
case "eventTaskInterval":
switches.setEventTaskInterval(Integer.parseInt(value));
break;
case "loadDataAtStart":
switches.setLoadDataAtStart(Boolean.parseBoolean(value));
break;
default:
break;
}
return "ok";
}
@RequestMapping(value = "/queryLabel", method = RequestMethod.GET)
public String queryLabel(HttpServletRequest request) throws Exception {
String entry = WebUtils.required(request, "entry");
String label = WebUtils.required(request, "label");
return cmdbProvider.queryLabel(entry, "ip", label);
}
}

View File

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.controllers;
package com.alibaba.nacos.cmdb.core;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class CmdbController {
public class CmdbManager {
}

View File

@ -0,0 +1,59 @@
/*
* 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.cmdb.core;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
@Component
public class SwitchAndOptions {
@Value("${nacos.cmdb.dumpTaskInterval}")
private int dumpTaskInterval;
@Value("${nacos.cmdb.eventTaskInterval}")
private int eventTaskInterval;
@Value("${nacos.cmdb.loadDataAtStart}")
private boolean loadDataAtStart;
public int getDumpTaskInterval() {
return dumpTaskInterval;
}
public void setDumpTaskInterval(int dumpTaskInterval) {
this.dumpTaskInterval = dumpTaskInterval;
}
public int getEventTaskInterval() {
return eventTaskInterval;
}
public void setEventTaskInterval(int eventTaskInterval) {
this.eventTaskInterval = eventTaskInterval;
}
public boolean isLoadDataAtStart() {
return loadDataAtStart;
}
public void setLoadDataAtStart(boolean loadDataAtStart) {
this.loadDataAtStart = loadDataAtStart;
}
}

View File

@ -0,0 +1,192 @@
/*
* 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.cmdb.memory;
import com.alibaba.nacos.api.cmdb.spi.CmdbService;
import com.alibaba.nacos.api.cmdb.pojo.Entity;
import com.alibaba.nacos.api.cmdb.pojo.EntityEvent;
import com.alibaba.nacos.api.cmdb.pojo.Label;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.cmdb.core.SwitchAndOptions;
import com.alibaba.nacos.cmdb.service.CmdbReader;
import com.alibaba.nacos.cmdb.service.CmdbWriter;
import com.alibaba.nacos.cmdb.utils.Loggers;
import com.alibaba.nacos.cmdb.utils.UtilsAndCommons;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
@Component
public class CmdbProvider implements CmdbReader, CmdbWriter {
@Autowired
private SwitchAndOptions switches;
private CmdbService cmdbService;
ServiceLoader<CmdbService> serviceLoader = ServiceLoader.load(CmdbService.class);
private Map<String, Map<String, Entity>> entityMap = new ConcurrentHashMap<>();
private Map<String, Label> labelMap = new ConcurrentHashMap<>();
private Set<String> entityTypeSet = new HashSet<>();
private List<EntityEvent> eventList = new ArrayList<>();
private long eventTimestamp = System.currentTimeMillis();
public CmdbProvider() throws NacosException {
}
private void initCmdbService() throws NacosException {
Iterator<CmdbService> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
cmdbService = iterator.next();
}
if (cmdbService == null && switches.isLoadDataAtStart()) {
throw new NacosException(NacosException.SERVER_ERROR, "Cannot initialize CmdbService!");
}
}
public void load() {
if (!switches.isLoadDataAtStart()) {
return;
}
// TODO load data on disk:
// init label map:
Set<String> labelNames = cmdbService.getLabelNames();
if (labelNames == null || labelNames.isEmpty()) {
Loggers.MAIN.warn("[LOAD] init label names failed!");
} else {
for (String labelName : labelNames) {
// If get null label, it's still ok. We will try it later when we meet this label:
labelMap.put(labelName, cmdbService.getLabel(labelName));
}
}
// init entity type set:
entityTypeSet = cmdbService.getEntityTypes();
// init entity map:
entityMap = cmdbService.getAllEntities();
}
@PostConstruct
public void init() throws NacosException {
initCmdbService();
load();
UtilsAndCommons.GLOBAL_EXECUTOR.schedule(new CmdbDumpTask(), switches.getDumpTaskInterval(), TimeUnit.SECONDS);
UtilsAndCommons.GLOBAL_EXECUTOR.schedule(new CmdbEventTask(), switches.getEventTaskInterval(), TimeUnit.SECONDS);
}
@Override
public Entity queryEntity(String entityName, String entityType) {
if (!entityMap.containsKey(entityType)) {
return null;
}
return entityMap.get(entityType).get(entityName);
}
@Override
public String queryLabel(String entityName, String entityType, String labelName) {
Entity entity = queryEntity(entityName, entityType);
if (entity == null) {
return null;
}
return entity.getLabels().get(labelName);
}
@Override
public List<Entity> queryEntitiesByLabel(String labelName, String labelValue) {
throw new UnsupportedOperationException("Not available now!");
}
public void removeEntity(String entityName, String entityType) {
if (!entityMap.containsKey(entityType)) {
return;
}
entityMap.get(entityType).remove(entityName);
}
public void updateEntity(Entity entity) {
if (!entityTypeSet.contains(entity.getType())) {
return;
}
entityMap.get(entity.getType()).put(entity.getName(), entity);
}
public class CmdbDumpTask implements Runnable {
@Override
public void run() {
try {
// refresh entity map:
entityMap = cmdbService.getAllEntities();
} catch (Exception e) {
Loggers.MAIN.error("CMDB-DUMP {}", "dump failed!", e);
} finally {
UtilsAndCommons.GLOBAL_EXECUTOR.schedule(this, switches.getDumpTaskInterval(), TimeUnit.SECONDS);
}
}
}
public class CmdbEventTask implements Runnable {
@Override
public void run() {
try {
long current = System.currentTimeMillis();
List<EntityEvent> events = cmdbService.getEntityEvents(eventTimestamp);
eventTimestamp = current;
if (events != null && !events.isEmpty()) {
for (EntityEvent event : events) {
switch (event.getType()) {
case ENTITY_REMOVE:
removeEntity(event.getEntityName(), event.getEntityType());
break;
case ENTITY_ADD_OR_UPDATE:
updateEntity(cmdbService.getEntity(event.getEntityName(), event.getEntityType()));
break;
default:
break;
}
}
}
} catch (Exception e) {
Loggers.MAIN.error("CMDB-EVENT {}", "event task failed!", e);
} finally {
UtilsAndCommons.GLOBAL_EXECUTOR.schedule(this, switches.getEventTaskInterval(), TimeUnit.SECONDS);
}
}
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.cmdb.service;
import com.alibaba.nacos.api.cmdb.pojo.Entity;
import java.util.List;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public interface CmdbReader {
/**
* Get entity
*
* @param entityName name of entity
* @param entityType type of entity
* @return entity
*/
Entity queryEntity(String entityName, String entityType);
/**
* Get label of entity
*
* @param entityName name of entity
* @param entityType type of entity
* @param labelName label name
* @return label value
*/
String queryLabel(String entityName, String entityType, String labelName);
/**
* Get entities of selected label
*
* @param labelName name of label
* @param labelValue value of label
* @return list of entiy
*/
List<Entity> queryEntitiesByLabel(String labelName, String labelValue);
}

View File

@ -0,0 +1,22 @@
/*
* 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.cmdb.service;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public interface CmdbWriter {
}

View File

@ -0,0 +1,27 @@
/*
* 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.cmdb.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author nacos
*/
public class Loggers {
public static final Logger MAIN = LoggerFactory.getLogger("com.alibaba.nacos.cmdb.main");
}

View File

@ -0,0 +1,46 @@
/*
* 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.cmdb.utils;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class UtilsAndCommons {
public static final String NACOS_SERVER_VERSION = "/v1";
public static final String NACOS_CMDB_CONTEXT = NACOS_SERVER_VERSION + "/cmdb";
public static final ScheduledExecutorService GLOBAL_EXECUTOR;
static {
GLOBAL_EXECUTOR
= new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("nacos.cmdb.global.executor");
t.setDaemon(true);
return t;
}
});
}
}

View File

@ -0,0 +1,6 @@
server.port=8848
server.servlet.context-path=/nacos
nacos.cmdb.dumpTaskInterval=3600
nacos.cmdb.eventTaskInterval=10
nacos.cmdb.loadDataAtStart=true

View File

@ -35,6 +35,12 @@
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

View File

@ -20,7 +20,7 @@ import java.util.UUID;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class UuidUtil {
public class UuidUtils {
public static String generateUuid() {
return UUID.randomUUID().toString();

View File

@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.web;
package com.alibaba.nacos.common.util;
import org.apache.commons.lang3.StringUtils;
@ -22,9 +21,9 @@ import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
/**
* @author nacos
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class BaseServlet {
public class WebUtils {
public static String required(HttpServletRequest req, String key) {
String value = req.getParameter(key);
@ -67,5 +66,4 @@ public class BaseServlet {
encode = encode.contains(",") ? encode.substring(0, encode.indexOf(",")) : encode;
return encode.contains(";") ? encode.substring(0, encode.indexOf(";")) : encode;
}
}

View File

@ -63,6 +63,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} -Djava.ext.dirs=${BASE_DIR}/plugins/cmdb"
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/nacos-server.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"

View File

@ -4,7 +4,9 @@ server.contextPath=/nacos
server.servlet.contextPath=/nacos
server.port=8848
#spring.datasource.platform=mysql
nacos.cmdb.dumpTaskInterval=3600
nacos.cmdb.eventTaskInterval=10
nacos.cmdb.loadDataAtStart=true
#db.num=2
#db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true

View File

@ -4,6 +4,23 @@
<springProperty scope="context" name="logPath" source="nacos.logs.path" defaultValue="${nacos.home}/logs"/>
<property name="LOG_HOME" value="${logPath}"/>
<appender name="cmdb-main"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${nacos.home}/logs/cmdb-main.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${nacos.home}/logs/cmdb-main.log.%d{yyyy-MM-dd}.%i</fileNamePattern>
<maxFileSize>2GB</maxFileSize>
<MaxHistory>15</MaxHistory>
<totalSizeCap>7GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<Pattern>%date %level %msg%n%n</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%date %level %msg%n%n</Pattern>
@ -454,6 +471,11 @@
</encoder>
</appender>
<logger name="com.alibaba.nacos.cmdb.main" additivity="false">
<level value="INFO"/>
<appender-ref ref="cmdb-main"/>
</logger>
<logger name="com.alibaba.nacos.naming.main" additivity="false">
<level value="INFO"/>
<appender-ref ref="async-naming-server"/>

View File

@ -154,6 +154,10 @@
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-cmdb</artifactId>
</dependency>
</dependencies>

View File

@ -15,11 +15,11 @@
*/
package com.alibaba.nacos.naming.acl;
import com.alibaba.nacos.common.util.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.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.BaseServlet;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -56,13 +56,13 @@ public class AuthChecker {
}
public void doAuth(Map<String, String[]> params, HttpServletRequest req) throws Exception {
String dom = BaseServlet.optional(req, "name", "");
String dom = WebUtils.optional(req, "name", "");
if (StringUtils.isEmpty(dom)) {
dom = BaseServlet.optional(req, "dom", "");
dom = WebUtils.optional(req, "dom", "");
}
if (StringUtils.isEmpty(dom)) {
dom = BaseServlet.optional(req, "tag", "");
dom = WebUtils.optional(req, "tag", "");
}
Domain domObj;
@ -101,7 +101,7 @@ public class AuthChecker {
}
// if token failed, try AuthInfo
AuthInfo authInfo = AuthInfo.fromString(auth, BaseServlet.getAcceptEncoding(req));
AuthInfo authInfo = AuthInfo.fromString(auth, WebUtils.getAcceptEncoding(req));
if (authInfo == null) {
throw new IllegalAccessException("invalid token or malformed auth info");
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.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>
*/
@Component
public class SpringContext implements ApplicationContextAware {
@Autowired
static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getAppContext() {
return context;
}
}

View File

@ -20,6 +20,8 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.naming.pojo.Cluster;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.Service;
import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.common.util.WebUtils;
import com.alibaba.nacos.naming.core.Domain;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.core.IpAddress;
@ -30,9 +32,10 @@ import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.pojo.ClusterInfo;
import com.alibaba.nacos.naming.pojo.IpAddressInfo;
import com.alibaba.nacos.naming.pojo.ServiceDetailInfo;
import com.alibaba.nacos.naming.selector.LabelSelector;
import com.alibaba.nacos.naming.selector.NoneSelector;
import com.alibaba.nacos.naming.view.ServiceDetailView;
import com.alibaba.nacos.naming.view.ServiceView;
import com.alibaba.nacos.naming.web.BaseServlet;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
@ -63,9 +66,9 @@ public class CatalogController {
JSONObject result = new JSONObject();
int page = Integer.parseInt(BaseServlet.required(request, "startPg"));
int pageSize = Integer.parseInt(BaseServlet.required(request, "pgSize"));
String keyword = BaseServlet.optional(request, "keyword", StringUtils.EMPTY);
int page = Integer.parseInt(WebUtils.required(request, "startPg"));
int pageSize = Integer.parseInt(WebUtils.required(request, "pgSize"));
String keyword = WebUtils.optional(request, "keyword", StringUtils.EMPTY);
List<Domain> doms = new ArrayList<>();
int total = domainsManager.getPagedDom(page - 1, pageSize, keyword, doms);
@ -106,7 +109,7 @@ public class CatalogController {
@RequestMapping(value = "/serviceDetail")
public ServiceDetailView serviceDetail(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String serviceName = WebUtils.required(request, "serviceName");
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {
throw new NacosException(NacosException.NOT_FOUND, "serivce " + serviceName + " is not found!");
@ -125,6 +128,18 @@ public class CatalogController {
service.setHealthCheckMode(HealthCheckMode.client.name());
}
service.setMetadata(domain.getMetadata());
switch (SelectorType.valueOf(domain.getSelector().getType())) {
case label:
service.setSelector((LabelSelector) domain.getSelector());
break;
case none:
case unknown:
default:
service.setSelector((NoneSelector) domain.getSelector());
break;
}
detailView.setService(service);
List<Cluster> clusters = new ArrayList<>();
@ -149,10 +164,10 @@ public class CatalogController {
@RequestMapping(value = "/instanceList")
public JSONObject instanceList(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String clusterName = BaseServlet.required(request, "clusterName");
int page = Integer.parseInt(BaseServlet.required(request, "startPg"));
int pageSize = Integer.parseInt(BaseServlet.required(request, "pgSize"));
String serviceName = WebUtils.required(request, "serviceName");
String clusterName = WebUtils.required(request, "clusterName");
int page = Integer.parseInt(WebUtils.required(request, "startPg"));
int pageSize = Integer.parseInt(WebUtils.required(request, "pgSize"));
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {

View File

@ -18,13 +18,13 @@ package com.alibaba.nacos.naming.controllers;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.common.util.WebUtils;
import com.alibaba.nacos.naming.core.Cluster;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.BaseServlet;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
@ -48,12 +48,12 @@ public class ClusterController {
@RequestMapping(value = {"/update", "/add"}, method = RequestMethod.POST)
public String update(HttpServletRequest request) throws Exception {
String clusterName = BaseServlet.required(request, "clusterName");
String serviceName = BaseServlet.required(request, "serviceName");
String healthChecker = BaseServlet.required(request, "healthChecker");
String metadata = BaseServlet.optional(request, "metadata", StringUtils.EMPTY);
String checkPort = BaseServlet.required(request, "checkPort");
String useInstancePort4Check = BaseServlet.required(request, "useInstancePort4Check");
String clusterName = WebUtils.required(request, "clusterName");
String serviceName = WebUtils.required(request, "serviceName");
String healthChecker = WebUtils.required(request, "healthChecker");
String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY);
String checkPort = WebUtils.required(request, "checkPort");
String useInstancePort4Check = WebUtils.required(request, "useInstancePort4Check");
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {

View File

@ -17,13 +17,13 @@ package com.alibaba.nacos.naming.controllers;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.util.WebUtils;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.ApiCommands;
import com.alibaba.nacos.naming.web.BaseServlet;
import com.alibaba.nacos.naming.web.MockHttpRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
@ -49,8 +49,8 @@ public class InstanceController extends ApiCommands {
Map<String, String[]> params = new HashMap<>(request.getParameterMap());
MockHttpRequest mockHttpRequest = MockHttpRequest.buildRequest(params);
String serviceJson = BaseServlet.optional(request, "service", StringUtils.EMPTY);
String clusterJson = BaseServlet.optional(request, "cluster", StringUtils.EMPTY);
String serviceJson = WebUtils.optional(request, "service", StringUtils.EMPTY);
String clusterJson = WebUtils.optional(request, "cluster", StringUtils.EMPTY);
// set service info:
if (StringUtils.isNotEmpty(serviceJson)) {
@ -77,7 +77,7 @@ public class InstanceController extends ApiCommands {
mockHttpRequest.addParameter("serviceMetadata", service.getString("metadata"));
} else {
mockHttpRequest.addParameter("dom", BaseServlet.required(request, "serviceName"));
mockHttpRequest.addParameter("dom", WebUtils.required(request, "serviceName"));
}
// set cluster info:
@ -114,9 +114,9 @@ public class InstanceController extends ApiCommands {
return deRegService(request);
}
@RequestMapping(value = "/instance/update", method = RequestMethod.POST)
@RequestMapping(value = {"/instance/update", "instance"}, method = RequestMethod.POST)
public String update(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String serviceName = WebUtils.required(request, "serviceName");
Map<String, String[]> params = new HashMap<>(request.getParameterMap());
MockHttpRequest mockHttpRequest = MockHttpRequest.buildRequest(params);
mockHttpRequest.addParameter("dom", serviceName);
@ -136,10 +136,10 @@ public class InstanceController extends ApiCommands {
@RequestMapping(value = "/instance", method = RequestMethod.GET)
public JSONObject queryDetail(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String cluster = BaseServlet.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
String ip = BaseServlet.required(request, "ip");
int port = Integer.parseInt(BaseServlet.required(request, "port"));
String serviceName = WebUtils.required(request, "serviceName");
String cluster = WebUtils.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
String ip = WebUtils.required(request, "ip");
int port = Integer.parseInt(WebUtils.required(request, "port"));
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {

View File

@ -15,14 +15,19 @@
*/
package com.alibaba.nacos.naming.controllers;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.naming.pojo.Service;
import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.common.util.WebUtils;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.BaseServlet;
import com.alibaba.nacos.naming.selector.LabelSelector;
import com.alibaba.nacos.naming.selector.NoneSelector;
import com.alibaba.nacos.naming.selector.Selector;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -31,9 +36,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
@ -45,17 +48,18 @@ public class ServiceController {
@Autowired
protected DomainsManager domainsManager;
@RequestMapping(value = "/create", method = RequestMethod.PUT)
@RequestMapping(value = "", method = RequestMethod.PUT)
public String create(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String serviceName = WebUtils.required(request, "serviceName");
if (domainsManager.getDomain(serviceName) != null) {
throw new IllegalArgumentException("specified service already exists, serviceName : " + serviceName);
}
float protectThreshold = NumberUtils.toFloat(BaseServlet.optional(request, "protectThreshold", "0"));
String healthCheckMode = BaseServlet.optional(request, "healthCheckMode", "client");
String metadata = BaseServlet.optional(request, "metadata", StringUtils.EMPTY);
float protectThreshold = NumberUtils.toFloat(WebUtils.optional(request, "protectThreshold", "0"));
String healthCheckMode = WebUtils.optional(request, "healthCheckMode", "client");
String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY);
String selector = WebUtils.optional(request, "selector", StringUtils.EMPTY);
Map<String, String> metadataMap = new HashMap<>(16);
if (StringUtils.isNotBlank(metadata)) {
metadataMap = UtilsAndCommons.parseMetadata(metadata);
@ -68,6 +72,7 @@ public class ServiceController {
domObj.setEnabled(true);
domObj.setEnableClientBeat(HealthCheckMode.client.name().equals(healthCheckMode.toLowerCase()));
domObj.setMetadata(metadataMap);
domObj.setSelector(parseSelector(selector));
// now valid the dom. if failed, exception will be thrown
domObj.setLastModifiedMillis(System.currentTimeMillis());
@ -79,10 +84,10 @@ public class ServiceController {
return "ok";
}
@RequestMapping(value = "/remove", method = RequestMethod.DELETE)
@RequestMapping(value = "", method = RequestMethod.DELETE)
public String remove(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String serviceName = WebUtils.required(request, "serviceName");
VirtualClusterDomain service = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (service == null) {
@ -98,41 +103,79 @@ public class ServiceController {
return "ok";
}
@RequestMapping(value = "/detail")
public Service detail(HttpServletRequest request) throws Exception {
@RequestMapping(value = "", method = RequestMethod.GET)
public JSONObject detail(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String serviceName = WebUtils.required(request, "serviceName");
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {
throw new NacosException(NacosException.NOT_FOUND, "serivce " + serviceName + " is not found!");
}
Service service = new Service(serviceName);
service.setName(serviceName);
service.setProtectThreshold(domain.getProtectThreshold());
service.setHealthCheckMode(HealthCheckMode.none.name());
if (domain.getEnableHealthCheck()) {
service.setHealthCheckMode(HealthCheckMode.server.name());
}
if (domain.getEnableClientBeat()) {
service.setHealthCheckMode(HealthCheckMode.client.name());
}
service.setMetadata(domain.getMetadata());
JSONObject res = new JSONObject();
res.put("name", serviceName);
res.put("protectThreshold", domain.getProtectThreshold());
return service;
res.put("healthCheckMode", HealthCheckMode.none.name());
if (domain.getEnableHealthCheck()) {
res.put("healthCheckMode", HealthCheckMode.server.name());
}
if (domain.getEnableClientBeat()) {
res.put("healthCheckMode", HealthCheckMode.client.name());
}
res.put("metadata", domain.getMetadata());
res.put("selector", domain.getSelector());
return res;
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
public JSONObject list(HttpServletRequest request) throws Exception {
int pageNo = NumberUtils.toInt(BaseServlet.required(request, "pageNo"));
int pageSize = NumberUtils.toInt(BaseServlet.required(request, "pageSize"));
int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo"));
int pageSize = NumberUtils.toInt(WebUtils.required(request, "pageSize"));
String selectorString = WebUtils.optional(request, "selector", StringUtils.EMPTY);
List<String> doms = domainsManager.getAllDomNamesList();
if (StringUtils.isNotBlank(selectorString)) {
JSONObject selectorJson = JSON.parseObject(selectorString);
switch (SelectorType.valueOf(selectorJson.getString("type"))) {
case label:
String expression = selectorJson.getString("expression");
if (StringUtils.isBlank(expression)) {
break;
}
expression = StringUtils.deleteWhitespace(expression);
// Now we only support the following expression:
// INSTANCE.metadata.xxx = 'yyy' or
// SERVICE.metadata.xxx = 'yyy'
String[] terms = expression.split("=");
String[] factors = terms[0].split("\\.");
switch (factors[0]) {
case "INSTANCE":
doms = filterInstanceMetadata(doms, factors[factors.length - 1], terms[1].replace("'", ""));
break;
case "SERVICE":
doms = filterServiceMetadata(doms, factors[factors.length - 1], terms[1].replace("'", ""));
break;
default:
break;
}
break;
default:
break;
}
}
int start = (pageNo - 1) * pageSize;
int end = start + pageSize;
List<String> doms = domainsManager.getAllDomNamesList();
if (start < 0) {
start = 0;
}
@ -150,14 +193,14 @@ public class ServiceController {
}
@RequestMapping(value = "/update", method = RequestMethod.POST)
@RequestMapping(value = "", method = RequestMethod.POST)
public String update(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
float protectThreshold = NumberUtils.toFloat(BaseServlet.required(request, "protectThreshold"));
String healthCheckMode = BaseServlet.required(request, "healthCheckMode");
String metadata = BaseServlet.optional(request, "metadata", StringUtils.EMPTY);
String serviceName = WebUtils.required(request, "serviceName");
float protectThreshold = NumberUtils.toFloat(WebUtils.required(request, "protectThreshold"));
String healthCheckMode = WebUtils.required(request, "healthCheckMode");
String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY);
String selector = WebUtils.optional(request, "selector", StringUtils.EMPTY);
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {
@ -184,6 +227,8 @@ public class ServiceController {
Map<String, String> metadataMap = UtilsAndCommons.parseMetadata(metadata);
domain.setMetadata(metadataMap);
domain.setSelector(parseSelector(selector));
domain.setLastModifiedMillis(System.currentTimeMillis());
domain.recalculateChecksum();
domain.valid();
@ -192,4 +237,60 @@ public class ServiceController {
return "ok";
}
private List<String> filterInstanceMetadata(List<String> serivces, String key, String value) {
List<String> filteredServices = new ArrayList<>();
for (String service : serivces) {
VirtualClusterDomain serviceObj = (VirtualClusterDomain) domainsManager.getDomain(service);
if (serviceObj == null) {
continue;
}
for (IpAddress address : serviceObj.allIPs()) {
if (value.equals(address.getMetadata().get(key))) {
filteredServices.add(service);
break;
}
}
}
return filteredServices;
}
private List<String> filterServiceMetadata(List<String> serivces, String key, String value) {
List<String> filteredServices = new ArrayList<>();
for (String service : serivces) {
VirtualClusterDomain serviceObj = (VirtualClusterDomain) domainsManager.getDomain(service);
if (serviceObj == null) {
continue;
}
if (value.equals(serviceObj.getMetadata().get(key))) {
filteredServices.add(service);
}
}
return filteredServices;
}
private Selector parseSelector(String selectorJsonString) throws NacosException {
if (StringUtils.isBlank(selectorJsonString)) {
return new NoneSelector();
}
JSONObject selectorJson = JSON.parseObject(selectorJsonString);
switch (SelectorType.valueOf(selectorJson.getString("type"))) {
case none:
return new NoneSelector();
case label:
String expression = selectorJson.getString("expression");
Set<String> labels = LabelSelector.parseExpression(expression);
LabelSelector labelSelector = new LabelSelector();
labelSelector.setExpression(expression);
labelSelector.setLabels(labels);
return labelSelector;
default:
throw new NacosException(NacosException.INVALID_PARAM, "not match any type of selector!");
}
}
}

View File

@ -49,8 +49,6 @@ public class Cluster extends com.alibaba.nacos.api.naming.pojo.Cluster implement
private int defIPPort = -1;
private boolean useIPPort4Check = true;
@JSONField(name = "nodegroup")
private String legacySyncConfig;
@ -332,14 +330,6 @@ public class Cluster extends com.alibaba.nacos.api.naming.pojo.Cluster implement
this.defCkport = defCkport;
}
public boolean isUseIPPort4Check() {
return useIPPort4Check;
}
public void setUseIPPort4Check(boolean useIPPort4Check) {
this.useIPPort4Check = useIPPort4Check;
}
public void update(Cluster cluster) {
if (!healthChecker.equals(cluster.getHealthChecker())) {
@ -367,9 +357,9 @@ public class Cluster extends com.alibaba.nacos.api.naming.pojo.Cluster implement
sitegroup = cluster.getSitegroup();
}
if (useIPPort4Check != cluster.isUseIPPort4Check()) {
Loggers.SRV_LOG.info("[CLUSTER-UPDATE] " + cluster.getDom().getName() + ":" + cluster.getName() + ", useIPPort4Check: " + useIPPort4Check + " -> " + cluster.isUseIPPort4Check());
useIPPort4Check = cluster.isUseIPPort4Check();
if (isUseIPPort4Check() != cluster.isUseIPPort4Check()) {
Loggers.SRV_LOG.info("[CLUSTER-UPDATE] " + cluster.getDom().getName() + ":" + cluster.getName() + ", useIPPort4Check: " + isUseIPPort4Check() + " -> " + cluster.isUseIPPort4Check());
setUseIPPort4Check(cluster.isUseIPPort4Check());
}
metadata = cluster.getMetadata();

View File

@ -43,9 +43,8 @@ import java.util.concurrent.locks.ReentrantLock;
*/
@Component
public class DomainsManager {
private Map<String, Domain> domMap = new ConcurrentHashMap<>();
private Map<String, Domain> raftDomMap = new ConcurrentHashMap<>();
private static Map<String, Set<Domain>> appName2Doms = new ConcurrentHashMap<>();
private LinkedBlockingDeque<DomainKey> toBeUpdatedDomsQueue = new LinkedBlockingDeque<>(1024 * 1024);
@ -290,19 +289,11 @@ public class DomainsManager {
public void easyAddIP4Dom(String domName, List<IpAddress> ips, long timestamp, long term) throws Exception {
VirtualClusterDomain dom = (VirtualClusterDomain) chooseDomMap().get(domName);
if (dom == null) {
throw new IllegalArgumentException("dom doesn't exist: " + domName);
}
// set default port and site info if missing
for (IpAddress ip : ips) {
if (ip.getPort() == 0) {
ip.setPort(dom.getClusterMap().get(ip.getClusterName()).getDefIPPort());
}
}
Datum datum1 = RaftCore.getDatum(UtilsAndCommons.getIPListStoreKey(dom));
String oldJson = StringUtils.EMPTY;
@ -672,10 +663,6 @@ public class DomainsManager {
return condition;
}
public Map<String, Domain> getDomMap() {
return new HashMap<String, Domain>(domMap);
}
private static class DomainKey {
private String domName;
private String serverIP;

View File

@ -28,6 +28,8 @@ import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.push.PushService;
import com.alibaba.nacos.naming.raft.RaftCore;
import com.alibaba.nacos.naming.raft.RaftListener;
import com.alibaba.nacos.naming.selector.Selector;
import com.alibaba.nacos.naming.selector.NoneSelector;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.lang3.RandomStringUtils;
@ -46,6 +48,14 @@ public class VirtualClusterDomain implements Domain, RaftListener {
private static final String DOMAIN_NAME_SYNTAX = "[0-9a-zA-Z\\.:_-]+";
public static final int MINIMUM_IP_DELETE_TIMEOUT = 60 * 1000;
@JSONField(serialize = false)
private ClientBeatProcessor clientBeatProcessor = new ClientBeatProcessor();
@JSONField(serialize = false)
private ClientBeatCheckTask clientBeatCheckTask = new ClientBeatCheckTask(this);
private String name;
private String token;
private List<String> owners = new ArrayList<>();
@ -53,19 +63,13 @@ public class VirtualClusterDomain implements Domain, RaftListener {
private Boolean enableHealthCheck = true;
private Boolean enabled = true;
private Boolean enableClientBeat = false;
private Selector selector = new NoneSelector();
public static final int MINIMUM_IP_DELETE_TIMEOUT = 60 * 1000;
/**
* IP will be deleted if it has not send beat for some time, default timeout is half an hour .
*/
private long ipDeleteTimeout = 30 * 1000;
@JSONField(serialize = false)
private ClientBeatProcessor clientBeatProcessor = new ClientBeatProcessor();
@JSONField(serialize = false)
private ClientBeatCheckTask clientBeatCheckTask = new ClientBeatCheckTask(this);
private volatile long lastModifiedMillis = 0L;
private boolean useSpecifiedURL = false;
@ -140,6 +144,14 @@ public class VirtualClusterDomain implements Domain, RaftListener {
this.metadata = metadata;
}
public Selector getSelector() {
return selector;
}
public void setSelector(Selector selector) {
this.selector = selector;
}
public VirtualClusterDomain() {
}
@ -204,10 +216,6 @@ public class VirtualClusterDomain implements Domain, RaftListener {
continue;
}
if (ip.getPort() == 0) {
ip.setPort(getLegacyCkPort());
}
if (StringUtils.isEmpty(ip.getClusterName())) {
ip.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);
}
@ -372,9 +380,6 @@ public class VirtualClusterDomain implements Domain, RaftListener {
domain.put("protectThreshold", vDom.getProtectThreshold());
int totalCkRTMillis = 0;
int validCkRTCount = 0;
List<Object> clustersList = new ArrayList<Object>();
for (Map.Entry<String, Cluster> entry : vDom.getClusterMap().entrySet()) {
@ -397,15 +402,6 @@ public class VirtualClusterDomain implements Domain, RaftListener {
return JSON.toJSONString(domain);
}
/**
* the legacy check port is the default check port for old domain format
*/
@JSONField(serialize = false)
public int getLegacyCkPort() {
return clusterMap.get(UtilsAndCommons.DEFAULT_CLUSTER_NAME).getDefCkport();
}
@Override
public String getName() {
return name;
@ -503,6 +499,8 @@ public class VirtualClusterDomain implements Domain, RaftListener {
enabled = vDom.getEnabled();
}
selector = vDom.getSelector();
metadata = vDom.getMetadata();
updateOrAddCluster(vDom.getClusterMap().values());

View File

@ -24,6 +24,8 @@ import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.core.Domain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.healthcheck.JsonAdapter;
import com.alibaba.nacos.naming.selector.Selector;
import com.alibaba.nacos.naming.selector.SelectorJsonAdapter;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
@ -123,6 +125,11 @@ public class UtilsAndCommons {
ParserConfig.getGlobalInstance()
.putDeserializer(AbstractHealthChecker.class, JsonAdapter.getInstance());
SerializeConfig.getGlobalInstance()
.put(Selector.class, SelectorJsonAdapter.getInstance());
ParserConfig.getGlobalInstance()
.putDeserializer(Selector.class, SelectorJsonAdapter.getInstance());
// write null values, otherwise will cause compatibility issues
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullStringAsEmpty.getMask();
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullListAsEmpty.getMask();

View File

@ -0,0 +1,294 @@
/*
* 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.selector;
import com.alibaba.nacos.api.cmdb.pojo.PreservedEntityTypes;
import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.api.selector.ExpressionSelector;
import com.alibaba.nacos.cmdb.service.CmdbReader;
import com.alibaba.nacos.naming.boot.SpringContext;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.exception.NacosException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import java.util.*;
/**
* A selector to implement a so called same-label-prior rule for service discovery.
* <h2>Backgroup</h2>
* Consider service providers are deployed in two sites i.e. site A and site B, and consumers
* of this service provider are also deployed in site A and site B. So the consumers may want to
* visit the service provider in current site, thus consumers in site A visit service providers
* in site A and consumers in site B visit service providers in site B. This is quite useful to
* reduce the transfer delay of RPC. This is called same-site-prior strategy.
* <h2>Same Label Prior</h2>
* The same-site-prior strategy covers many circumstances in large companies and we can abstract
* it to a higher level strategy: same-label-prior.
* <p>
* So the idea is that presumed we have built a self-defined or integrated a third-party idc CMDB
* which stores all the labels of all IPs. Then we can filter provider IPs by the consumer IP and
* we only return the providers who have the same label values with consumer. We can define the
* labels we want to include in the comparison.
* <p>
* If no provider has the same label value with the consumer, we fall back to give all providers
* to the consumer. Note that this fallback strategy may also be abstracted in future to introduce
* more kinds of behaviors.
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @see CmdbReader
*/
public class LabelSelector extends ExpressionSelector implements Selector {
private CmdbReader cmdbReader;
/**
* The labels relevant to this the selector.
*
* @see com.alibaba.nacos.api.cmdb.pojo.Label
*/
private Set<String> labels;
private static final Set<String> SUPPORTED_INNER_CONNCETORS = new HashSet<>();
private static final Set<String> SUPPORTED_OUTER_CONNCETORS = new HashSet<>();
private static final String CONSUMER_PREFIX = "CONSUMER.label.";
private static final String PROVIDER_PREFIX = "PROVIDER.label.";
private static final char CEQUAL = '=';
private static final char CAND = '&';
static {
SUPPORTED_INNER_CONNCETORS.add(String.valueOf(CEQUAL));
SUPPORTED_OUTER_CONNCETORS.add(String.valueOf(CAND));
}
public Set<String> getLabels() {
return labels;
}
public void setLabels(Set<String> labels) {
this.labels = labels;
}
public LabelSelector() {
setType(SelectorType.label.name());
ApplicationContext context = SpringContext.getAppContext();
cmdbReader = context.getBean(CmdbReader.class);
}
public static Set<String> parseExpression(String expression) throws NacosException {
return ExpressionInterpreter.parseExpression(expression);
}
@Override
public List<IpAddress> select(String consumer, List<IpAddress> providers) {
if (labels.isEmpty()) {
return providers;
}
List<IpAddress> ipAddressList = new ArrayList<>();
for (IpAddress ipAddress : providers) {
boolean matched = true;
for (String labelName : getLabels()) {
String consumerLabelValue = cmdbReader.queryLabel(consumer, PreservedEntityTypes.ip.name(), labelName);
if (StringUtils.isNotBlank(consumerLabelValue) &&
!StringUtils.equals(consumerLabelValue,
cmdbReader.queryLabel(ipAddress.getIp(), PreservedEntityTypes.ip.name(), labelName))) {
matched = false;
break;
}
}
if (matched) {
ipAddressList.add(ipAddress);
}
}
if (ipAddressList.isEmpty()) {
return providers;
}
return ipAddressList;
}
/**
* Expression interpreter for label selector.
* <p>
* For now it supports very limited set of syntax rules.
*/
public static class ExpressionInterpreter {
/**
* Parse the label expression.
* <p>
* Currently we support the very single type of expression:
* <pre>
* consumer.labelA = provider.labelA & consumer.labelB = provider.labelB
* </pre>
* Later we will implement a interpreter to parse this expression in a standard LL parser way.
*
* @param expression the label expression to parse
* @return collection of labels
*/
public static Set<String> parseExpression(String expression) throws NacosException {
if (StringUtils.isBlank(expression)) {
return new HashSet<>();
}
expression = StringUtils.deleteWhitespace(expression);
List<String> elements = getTerms(expression);
Set<String> gotLabels = new HashSet<>();
int index = 0;
index = checkInnerSyntax(elements, index);
if (index == -1) {
throw new NacosException(NacosException.INVALID_PARAM, "parse expression failed!");
}
gotLabels.add(elements.get(index++).split(PROVIDER_PREFIX)[1]);
while (index < elements.size()) {
index = checkOuterSyntax(elements, index);
if (index >= elements.size()) {
return gotLabels;
}
if (index == -1) {
throw new NacosException(NacosException.INVALID_PARAM, "parse expression failed!");
}
gotLabels.add(elements.get(index++).split(PROVIDER_PREFIX)[1]);
}
return gotLabels;
}
public static List<String> getTerms(String expression) {
List<String> terms = new ArrayList<>();
Set<Character> characters = new HashSet<>();
characters.add(CEQUAL);
characters.add(CAND);
char[] chars = expression.toCharArray();
int lastIndex = 0;
for (int index = 0; index < chars.length; index++) {
char ch = chars[index];
if (characters.contains(ch)) {
terms.add(expression.substring(lastIndex, index));
terms.add(expression.substring(index, index+1));
index ++;
lastIndex = index;
}
}
terms.add(expression.substring(lastIndex, chars.length));
return terms;
}
private static int skipEmpty(List<String> elements, int start) {
while (start < elements.size() && StringUtils.isBlank(elements.get(start))) {
start++;
}
return start;
}
private static int checkOuterSyntax(List<String> elements, int start) {
int index = start;
index = skipEmpty(elements, index);
if (index >= elements.size()) {
return index;
}
if (!SUPPORTED_OUTER_CONNCETORS.contains(elements.get(index++))) {
return -1;
}
return checkInnerSyntax(elements, index);
}
private static int checkInnerSyntax(List<String> elements, int start) {
int index = start;
index = skipEmpty(elements, index);
if (index >= elements.size()) {
return -1;
}
if (!elements.get(index).startsWith(CONSUMER_PREFIX)) {
return -1;
}
String labelConsumer = elements.get(index++).split(CONSUMER_PREFIX)[1];
index = skipEmpty(elements, index);
if (index >= elements.size()) {
return -1;
}
if (!SUPPORTED_INNER_CONNCETORS.contains(elements.get(index++))) {
return -1;
}
index = skipEmpty(elements, index);
if (index >= elements.size()) {
return -1;
}
if (!elements.get(index).startsWith(PROVIDER_PREFIX)) {
return -1;
}
String labelProvider = elements.get(index).split(PROVIDER_PREFIX)[1];
if (!labelConsumer.equals(labelProvider)) {
return -1;
}
return index;
}
}
public static void main(String[] args) throws NacosException {
String expression = "CONSUMER.label.A=PROVIDER.label.A &CONSUMER.label.B=PROVIDER.label.B";
expression = StringUtils.deleteWhitespace(expression);
System.out.println(ExpressionInterpreter.getTerms(expression));
System.out.println(LabelSelector.parseExpression(expression));
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.selector;
import com.alibaba.nacos.api.selector.SelectorType;
import com.alibaba.nacos.naming.core.IpAddress;
import java.util.List;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class NoneSelector extends com.alibaba.nacos.api.selector.AbstractSelector implements Selector {
public NoneSelector() {
this.setType(SelectorType.none.name());
}
@Override
public List<IpAddress> select(String consumer, List<IpAddress> providers) {
return providers;
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.selector;
import com.alibaba.nacos.naming.core.IpAddress;
import java.util.List;
/**
* Selector defines a rule for load-balance for service discovery.
* <p>
* Every service in Nacos can apply an existing selector and uses it to give the consumer
* a subset of selected providers.
* <p>
* This selector itself does not implement any specific behavior of load-balance, every
* real life selector should extend this class and implement the select method.
* <p>
* Every extended selector should also register its type to class SelectorType so Nacos
* recognizes it and can correctly create this type of selector.
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
* @see com.alibaba.nacos.api.selector.SelectorType
* @see SelectorJsonAdapter
*/
public interface Selector {
/**
* Get the type of this selector
*
* @return type of selector
*/
String getType();
/**
* Select qualified instances from providers
*
* @param consumer consumer address
* @param providers candidate provider addresses
* @return selected provider addresses
*/
List<IpAddress> select(String consumer, List<IpAddress> providers);
}

View File

@ -0,0 +1,96 @@
/*
* 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.selector;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.nacos.api.selector.AbstractSelector;
import com.alibaba.nacos.api.selector.SelectorType;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* Json adapter for class Selector.
* <p>
* When deserializing object for class Selector, we should consider to convert the selector to
* its runtime child class object. And this adapter helps us to finish it.
*
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class SelectorJsonAdapter implements ObjectDeserializer, ObjectSerializer {
private static SelectorJsonAdapter INSTANCE = new SelectorJsonAdapter();
private SelectorJsonAdapter() {
}
public static SelectorJsonAdapter getInstance() {
return INSTANCE;
}
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
JSONObject jsonObj = (JSONObject) parser.parse();
if (jsonObj == null) {
return null;
}
String checkType = jsonObj.getString("type");
if (StringUtils.equals(checkType, SelectorType.label.name())) {
return (T) JSON.parseObject(jsonObj.toJSONString(), LabelSelector.class);
}
if (StringUtils.equals(checkType, SelectorType.none.name())) {
return (T) JSON.parseObject(jsonObj.toJSONString(), NoneSelector.class);
}
return null;
}
@Override
public int getFastMatchToken() {
return 0;
}
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
SerializeWriter writer = serializer.getWriter();
if (object == null) {
writer.writeNull();
return;
}
Selector selector = (Selector) object;
writer.writeFieldValue(',', "type", selector.getType());
if (StringUtils.equals(selector.getType(), SelectorType.label.name())) {
LabelSelector labelSelector = (LabelSelector) selector;
writer.writeFieldValue(',', "labels", JSON.toJSONString(labelSelector.getLabels()));
}
}
}

View File

@ -34,7 +34,6 @@ import java.util.concurrent.ConcurrentMap;
/**
* @author <a href="mailto:zpf.073@gmail.com">nkorange</a>
*/
public class AuthFilter implements Filter {
@Autowired

View File

@ -19,29 +19,24 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.util.IoUtils;
import com.alibaba.nacos.common.util.WebUtils;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.misc.NetUtils;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.raft.Datum;
import com.alibaba.nacos.naming.raft.RaftCore;
import com.alibaba.nacos.naming.raft.RaftListener;
import com.alibaba.nacos.naming.raft.RaftPeer;
import com.alibaba.nacos.naming.raft.RaftStore;
import com.alibaba.nacos.naming.raft.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author nacos
*/
@ -57,7 +52,7 @@ public class RaftCommands {
public JSONObject vote(HttpServletRequest request, HttpServletResponse response) throws Exception {
RaftPeer peer = RaftCore.MasterElection.receivedVote(
JSON.parseObject(BaseServlet.required(request, "vote"), RaftPeer.class));
JSON.parseObject(WebUtils.required(request, "vote"), RaftPeer.class));
return JSON.parseObject(JSON.toJSONString(peer));
}
@ -101,7 +96,7 @@ public class RaftCommands {
@NeedAuth
@RequestMapping("/reloadDatum")
public String reloadDatum(HttpServletRequest request, HttpServletResponse response) throws Exception {
String key = BaseServlet.required(request, "key");
String key = WebUtils.required(request, "key");
RaftStore.load(key);
return "ok";
}
@ -148,7 +143,7 @@ public class RaftCommands {
response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request));
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Content-Encode", "gzip");
RaftCore.signalDelete(BaseServlet.required(request, "key"));
RaftCore.signalDelete(WebUtils.required(request, "key"));
return "ok";
}
@ -159,7 +154,7 @@ public class RaftCommands {
response.setHeader("Content-Type", "application/json; charset=" + getAcceptEncoding(request));
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Content-Encode", "gzip");
String keysString = BaseServlet.required(request, "keys");
String keysString = WebUtils.required(request, "keys");
String[] keys = keysString.split(",");
List<Datum> datums = new ArrayList<Datum>();

View File

@ -460,6 +460,7 @@
<module>common</module>
<module>distribution</module>
<module>console</module>
<module>cmdb</module>
</modules>
<!-- 所有的子项目默认依赖 -->
@ -521,6 +522,11 @@
<artifactId>nacos-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacos-cmdb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacos-console</artifactId>

View File

@ -64,7 +64,6 @@ public class RegisterInstance_ITCase {
@Test
@Ignore
public void regService() throws NacosException, InterruptedException {
String serviceName = "dungu.test.99";
naming.registerInstance(serviceName, "127.0.0.1", 80, "c1");
naming.registerInstance(serviceName, "127.0.0.2", 80, "c2");

View File

@ -296,30 +296,9 @@ public class RestAPI_ITCase {
Assert.assertEquals(NamingBase.TEST_PORT_4_DOM_1, hosts.getJSONObject(0).getString("port"));
}
@Test
public void srvIPXT() throws Exception {
ResponseEntity<String> response = request("/nacos/v1/ns/api/srvIPXT",
Params.newParams()
.appendParam("dom", NamingBase.TEST_DOM_1)
.done(), String.class);
assertTrue(response.getStatusCode().is2xxSuccessful());
JSONObject json = JSON.parseObject(response.getBody());
Assert.assertEquals(NamingBase.TEST_DOM_1, json.getString("dom"));
JSONArray hosts = json.getJSONArray("hosts");
Assert.assertNotNull(hosts);
Assert.assertEquals(1, hosts.size());
Assert.assertEquals(NamingBase.TEST_IP_4_DOM_1, hosts.getJSONObject(0).getString("ip"));
Assert.assertEquals(NamingBase.TEST_PORT_4_DOM_1, hosts.getJSONObject(0).getString("port"));
}
@Test
public void remvIP4Dom() throws Exception {
ResponseEntity<String> response = request("/nacos/v1/ns/api/addIP4Dom",
Params.newParams()
.appendParam("dom", NamingBase.TEST_DOM_1)

View File

@ -15,9 +15,12 @@
*/
package com.alibaba.nacos.test.naming;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.api.selector.ExpressionSelector;
import com.alibaba.nacos.naming.NamingApp;
import org.junit.Assert;
import org.junit.Before;
@ -29,7 +32,9 @@ import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.alibaba.nacos.test.naming.NamingBase.*;
@ -57,40 +62,6 @@ public class SelectInstances_ITCase {
}
}
@Test
public void getAllInstances() throws Exception {
// final String serviceName = "dungu.test.100";
// naming.registerInstance(serviceName, "127.0.0.1", TEST_PORT);
//
//
//
// Thread thread = new Thread(new Runnable() {
// @Override
// public void run() {
// try {
// TimeUnit.SECONDS.sleep(10);
// naming.deregisterInstance(serviceName, "127.0.0.1", TEST_PORT);
// System.out.println("deregister ok!");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// });
//
// thread.start();
//
//
// while (true) {
//
// System.out.println(new Date());
// System.out.println(naming.getAllInstances("dungu.test.100"));
//
// TimeUnit.SECONDS.sleep(1);
// }
}
/**
* 获取所有健康的Instance
*
@ -388,5 +359,47 @@ public class SelectInstances_ITCase {
Assert.assertTrue(verifyInstanceList(instances, instancesGet));
}
@Test
public void getServiceListWithSelector() throws NacosException, InterruptedException {
String serviceName = randomDomainName();
Instance instance = new Instance();
instance.setIp("128.0.0.1");
instance.setPort(999);
instance.setServiceName(serviceName);
Map<String, String> metadata = new HashMap<String, String>();
metadata.put("registerSource", "dubbo");
instance.setMetadata(metadata);
naming.registerInstance(serviceName, instance);
naming.registerInstance(serviceName, "127.0.0.1", 80, "c1");
naming.registerInstance(serviceName, "127.0.0.2", 80, "c2");
TimeUnit.SECONDS.sleep(10);
ExpressionSelector expressionSelector = new ExpressionSelector();
expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'dubbo'");
ListView<String> serviceList = naming.getServicesOfServer(1, 10, expressionSelector);
Assert.assertTrue(serviceList.getData().contains(serviceName));
serviceName = randomDomainName();
instance.setServiceName(serviceName);
metadata.put("registerSource", "spring");
instance.setMetadata(metadata);
naming.registerInstance(serviceName, instance);
TimeUnit.SECONDS.sleep(10);
expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'spring'");
serviceList = naming.getServicesOfServer(1, 10, expressionSelector);
Assert.assertTrue(serviceList.getData().contains(serviceName));
}
}