Nacos具备微服务注册和发现,以及配置管理的功能,本文详情描述【注册和发现】的原理,以及基本使用,包括如何进行跨group注册和发现。
一、Nacos配置
XML
spring:
cloud:
nacos:
discovery:
server-addr: nacosIP地址:8849
namespace: dev
group: Product
config:
server-addr: nacosIP地址:8849
namespace: dev
group: Product
file-extension: yaml
refresh-enabled: true
enable-remote-sync-config: true
name: product.yaml
XML
spring:
cloud:
nacos:
discovery:
server-addr: nacosIP地址:8849
namespace: dev
group: Order
config:
server-addr: nacosIP地址:8849
namespace: dev
group: Order
file-extension: yaml
refresh-enabled: true
enable-remote-sync-config: true
name: order.yaml
extension-configs:
- data-id: config-global.yaml
group: Order
refresh: true
一、NacosServiceDiscovery 中 getInstances 获取注册在nacos上的服务实例清单
java
public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
String group = this.discoveryProperties.getGroup();
List<Instance> instances = this.namingService().selectInstances(serviceId, group, true);
return hostToServiceInstanceList(instances, serviceId);
}
但是原始功能不支持跨group服务发现,所以我们需要重写 getInstances方法。
二、NacosServiceDiscoveryV2 继承 NacosServiceDiscovery,并重写 getInstances
java
import com.alibaba.cloud.nacos.NacosServiceInstance;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.nacos.api.exception.NacosException;
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.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.cloud.client.ServiceInstance;
import java.util.*;
import static com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty;
import static java.util.Objects.isNull;
/**
* @description: naocs服务发现重写
*/
public class NacosServiceDiscoveryV2 extends NacosServiceDiscovery {
private final NacosDiscoveryPropertiesV2 discoveryProperties;
private final NacosShareProperties nacosShareProperties;
private final NacosServiceManager nacosServiceManager;
public NacosServiceDiscoveryV2(NacosDiscoveryPropertiesV2 discoveryProperties, NacosShareProperties nacosShareProperties, NacosServiceManager nacosServiceManager) {
super(discoveryProperties, nacosServiceManager);
this.discoveryProperties = discoveryProperties;
this.nacosShareProperties = nacosShareProperties;
this.nacosServiceManager = nacosServiceManager;
}
/**
* Return all instances for the given service.
* @param serviceId id of service
* @return list of instances
* @throws NacosException nacosException
*/
@Override
public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
String group = discoveryProperties.getGroup();
List<Instance> instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, group, true);
if (isEmpty(instances)) {
Map<String, Set<String>> namespaceGroupMap = nacosShareProperties.getNamespaceGroupMap();
Map<String, NamingService> namespace2NamingServiceMap = discoveryProperties.shareNamingServiceInstances();
for (Map.Entry<String, NamingService> entry : namespace2NamingServiceMap.entrySet()) {
String namespace;
NamingService namingService;
if (isNull(namespace = entry.getKey()) || isNull(namingService = entry.getValue()))
continue;
Set<String> groupNames = namespaceGroupMap.get(namespace);
List<Instance> shareInstances;
if (isEmpty(groupNames)) {
shareInstances = namingService.selectInstances(serviceId, group, true);
if (CollectionUtils.isNotEmpty(shareInstances))
break;
} else {
shareInstances = new ArrayList<>();
for (String groupName : groupNames) {
List<Instance> subShareInstances = namingService.selectInstances(serviceId, groupName, true);
if (CollectionUtils.isNotEmpty(subShareInstances)) {
shareInstances.addAll(subShareInstances);
}
}
}
if (CollectionUtils.isNotEmpty(shareInstances)) {
instances = shareInstances;
break;
}
}
}
return hostToServiceInstanceList(instances, serviceId);
}
/**
* Return the names of all services.
* @return list of service names
* @throws NacosException nacosException
*/
public List<String> getServices() throws NacosException {
String group = discoveryProperties.getGroup();
ListView<String> services = discoveryProperties.namingServiceInstance()
.getServicesOfServer(1, Integer.MAX_VALUE, group);
return services.getData();
}
public static List<ServiceInstance> hostToServiceInstanceList(
List<Instance> instances, String serviceId) {
List<ServiceInstance> result = new ArrayList<>(instances.size());
for (Instance instance : instances) {
ServiceInstance serviceInstance = hostToServiceInstance(instance, serviceId);
if (serviceInstance != null) {
result.add(serviceInstance);
}
}
return result;
}
public static ServiceInstance hostToServiceInstance(Instance instance,
String serviceId) {
if (instance == null || !instance.isEnabled() || !instance.isHealthy()) {
return null;
}
NacosServiceInstance nacosServiceInstance = new NacosServiceInstance();
nacosServiceInstance.setHost(instance.getIp());
nacosServiceInstance.setPort(instance.getPort());
nacosServiceInstance.setServiceId(serviceId);
Map<String, String> metadata = new HashMap<>();
metadata.put("nacos.instanceId", instance.getInstanceId());
metadata.put("nacos.weight", instance.getWeight() + "");
metadata.put("nacos.healthy", instance.isHealthy() + "");
metadata.put("nacos.cluster", instance.getClusterName() + "");
metadata.putAll(instance.getMetadata());
nacosServiceInstance.setMetadata(metadata);
if (metadata.containsKey("secure")) {
boolean secure = Boolean.parseBoolean(metadata.get("secure"));
nacosServiceInstance.setSecure(secure);
}
return nacosServiceInstance;
}
private NamingService namingService() {
return nacosServiceManager
.getNamingService(discoveryProperties.getNacosProperties());
}
}
原理:
1、根据 serviceId 和 group 获取当前实例
2、获取所有group清单,并根据group找到group下所有实例
3、将group清单,加入到自己的实例清单中。
三、NacosDiscoveryPropertiesV2 继承 NacosDiscoveryProperties,服务发现属性重写 shareNamingServiceInstances、getNacosProperties方法
java
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import static com.alibaba.nacos.api.PropertyKeyConst.*;
import static java.util.Objects.nonNull;
/**
* @description: naocs服务发现属性重写
*/
public class NacosDiscoveryPropertiesV2 extends NacosDiscoveryProperties {
private static final Logger log = LoggerFactory.getLogger(NacosDiscoveryPropertiesV2.class);
private final NacosShareProperties nacosShareProperties;
private static final Map<String, NamingService> NAMESPACE_TO_NAMING_SERVICE_MAP = new ConcurrentHashMap<>();
public NacosDiscoveryPropertiesV2(NacosShareProperties nacosShareProperties) {
super();
this.nacosShareProperties = nacosShareProperties;
}
public Map<String, NamingService> shareNamingServiceInstances() {
if (!NAMESPACE_TO_NAMING_SERVICE_MAP.isEmpty()) {
return new HashMap<>(NAMESPACE_TO_NAMING_SERVICE_MAP);
}
List<NacosShareProperties.NacosShareEntity> entities = Optional.ofNullable(nacosShareProperties)
.map(NacosShareProperties::getEntities).orElse(Collections.emptyList());
entities.stream().filter(entity -> nonNull(entity) && nonNull(entity.getNamespace()))
.filter(distinctByKey(NacosShareProperties.NacosShareEntity::getNamespace))
.forEach(entity -> {
try {
NamingService namingService = NacosFactory.createNamingService(getNacosProperties(entity.getNamespace()));
if (namingService != null) {
NAMESPACE_TO_NAMING_SERVICE_MAP.put(entity.getNamespace(), namingService);
}
} catch (Exception e) {
log.error("create naming service error! properties={}, e=", this, e);
}
});
return new HashMap<>(NAMESPACE_TO_NAMING_SERVICE_MAP);
}
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
private Properties getNacosProperties(String namespace) {
Properties properties = new Properties();
properties.put(SERVER_ADDR, getServerAddr());
properties.put(USERNAME, Objects.toString(getUsername(), ""));
properties.put(PASSWORD, Objects.toString(getPassword(), ""));
properties.put(NAMESPACE, namespace);
properties.put(UtilAndComs.NACOS_NAMING_LOG_NAME, getLogName());
String endpoint = getEndpoint();
if (endpoint.contains(":")) {
int index = endpoint.indexOf(":");
properties.put(ENDPOINT, endpoint.substring(0, index));
properties.put(ENDPOINT_PORT, endpoint.substring(index + 1));
}
else {
properties.put(ENDPOINT, endpoint);
}
properties.put(ACCESS_KEY, getAccessKey());
properties.put(SECRET_KEY, getSecretKey());
properties.put(CLUSTER_NAME, getClusterName());
properties.put(NAMING_LOAD_CACHE_AT_START, getNamingLoadCacheAtStart());
// enrichNacosDiscoveryProperties(properties);
return properties;
}
}
四、共享share属性类 NacosShareProperties
java
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.Objects.nonNull;
/**
* <pre>
* @description: 共享nacos属性
* </pre>
*/
@Configuration
@ConfigurationProperties(prefix = "nacos.share")
public class NacosShareProperties {
private final Map<String, Set<String>> NAMESPACE_TO_GROUP_NAME_MAP = new ConcurrentHashMap<>();
/**
* 共享nacos实体列表
*/
private List<NacosShareEntity> entities;
public List<NacosShareEntity> getEntities() {
return entities;
}
public void setEntities(List<NacosShareEntity> entities) {
this.entities = entities;
}
public Map<String, Set<String>> getNamespaceGroupMap() {
if (CollectionUtils.isEmpty(entities)) {
return new HashMap<>();
}
entities.stream().filter(entity -> nonNull(entity) && nonNull(entity.getNamespace()))
.forEach(entity -> {
Set<String> groupNames = NAMESPACE_TO_GROUP_NAME_MAP.computeIfAbsent(entity.getNamespace(), k -> new HashSet<>());
if (nonNull(entity.getGroupNames()))
groupNames.addAll(entity.getGroupNames());
});
return new HashMap<>(NAMESPACE_TO_GROUP_NAME_MAP);
}
@Override
public String toString() {
return "NacosShareProperties{" +
"entities=" + entities +
'}';
}
/**
* 共享nacos实体
*/
public static final class NacosShareEntity {
/**
* 命名空间
*/
private String namespace;
/**
* 分组
*/
private List<String> groupNames;
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public List<String> getGroupNames() {
return groupNames;
}
public void setGroupNames(List<String> groupNames) {
this.groupNames = groupNames;
}
@Override
public String toString() {
return "NacosShareEntity{" +
"namespace='" + namespace + '\'' +
", groupNames=" + groupNames +
'}';
}
}
}
五、整合配置类 NacosDiscoveryAutoConfigurationV2
java
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Auther: yangjian
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
@AutoConfigureBefore({NacosDiscoveryAutoConfiguration.class})
public class NacosDiscoveryAutoConfigurationV2 {
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryPropertiesV2 nacosProperties(NacosShareProperties nacosShareProperties) {
return new NacosDiscoveryPropertiesV2(nacosShareProperties);
}
@Bean
@ConditionalOnMissingBean
public NacosServiceDiscovery nacosServiceDiscovery(NacosDiscoveryPropertiesV2 discoveryPropertiesV2
, NacosShareProperties nacosShareProperties, NacosServiceManager nacosServiceManager) {
return new NacosServiceDiscoveryV2(discoveryPropertiesV2, nacosShareProperties, nacosServiceManager);
}
}