Nacos具备微服务注册和发现,以及配置管理的功能,本文详情描述【注册和发现】的原理,以及基本使用,包括如何进行跨group注册和发现。
一、Nacos配置
spring:cloud:nacos:discovery:server-addr: nacosIP地址:8849namespace: devgroup: Productconfig:server-addr: nacosIP地址:8849namespace: devgroup: Productfile-extension: yamlrefresh-enabled: trueenable-remote-sync-config: truename: product.yaml
spring:cloud:nacos:discovery:server-addr: nacosIP地址:8849namespace: devgroup: Orderconfig:server-addr: nacosIP地址:8849namespace: devgroup: Orderfile-extension: yamlrefresh-enabled: trueenable-remote-sync-config: truename: order.yamlextension-configs:- data-id: config-global.yamlgroup: Orderrefresh: true
一、NacosServiceDiscovery 中 getInstances 获取注册在nacos上的服务实例清单
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
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*/@Overridepublic 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方法
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
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);}@Overridepublic 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;}@Overridepublic String toString() {return "NacosShareEntity{" +"namespace='" + namespace + '\'' +", groupNames=" + groupNames +'}';}}
}
五、整合配置类 NacosDiscoveryAutoConfigurationV2
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@ConditionalOnMissingBeanpublic NacosDiscoveryPropertiesV2 nacosProperties(NacosShareProperties nacosShareProperties) {return new NacosDiscoveryPropertiesV2(nacosShareProperties);}@Bean@ConditionalOnMissingBeanpublic NacosServiceDiscovery nacosServiceDiscovery(NacosDiscoveryPropertiesV2 discoveryPropertiesV2, NacosShareProperties nacosShareProperties, NacosServiceManager nacosServiceManager) {return new NacosServiceDiscoveryV2(discoveryPropertiesV2, nacosShareProperties, nacosServiceManager);}
}