Skip to content

随记

多尝试后再问人。

场景:有一个网关入口,在网关的方法里,需要调用其他微服务的接口,A,B,C 是我们的服务,D,E 是其他项目组的服务,在我们的微服务A,B,C里有可能要调用其他项目组的服务。

  • A,B,C 是我们的服务注册到nacos默认的 命名空间->组->服务名
  • D,E 服务注册到nacos默认的 命名空间->组->服务名

场景

有一个网关入口,在网关的方法里,需要调用其他微服务的接口,A,B,C 是我们的服务,D,E 是其他项目组的服务,在我们的微服务A,B,C里有可能要调用其他项目组的服务。

  • A,B,C 是我们的服务注册到nacos默认的 命名空间->组->服务名
  • D,E 服务注册到nacos默认的 命名空间->组->服务名

问题

  • 如果本地开发时,我们的服务使用同样的命名空间->组->服务名,那肯定会影响线上服务的正常调用。
  • 如果本地开发时,不使用D,E服务的命名空间->组->服务名,就没有办法调用到该服务
  • 如果本地开发时,使用D,E服务的命名空间->组->服务名,但是由于D,E的开发人员节点也注册上去,从而调用到他们的服务,从而导致项目组服务调用失败。

思考

  • 服务调用默认都是按命名空间->组->服务名查找的,如果我的服务在自己的组里,那么就不会影响线上服务的正常调用。
  • 如果我能跨组调用服务,那么我即便在自己的组下,也能调用到其他项目组的服务。
  • 在跨组调用基础上,如果我能有限筛选服务示例对应的ip,那么我既能跨组调用到其他项目组的服务,又能保证我调用到的服务节点是服务器的,而不是某开发本地的。
  • 与线上服务公用一个nacos服务,本地少起一个服务不香吗
  • 如果我能跨组调用服务,那么我本地的很多服务就不用自己启动了

1.增加配置类

  • NacosDiscoveryAutoConfigurationV2
java
package com.sxszck.shafulin.config.nacos;

import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
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;

/**
 * 参考文档 https://blog.csdn.net/m0_56287495/article/details/129240577
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
@AutoConfigureBefore({NacosDiscoveryAutoConfiguration.class})
public class NacosDiscoveryAutoConfigurationV2 {

    @Bean
    @ConditionalOnMissingBean
    public NacosServiceDiscovery nacosServiceDiscovery(NacosDiscoveryProperties nacosDiscoveryProperties, NacosServiceManager nacosServiceManager) {
        return new NacosServiceDiscoveryV2(nacosDiscoveryProperties, nacosServiceManager);
    }
}
  • NacosServiceDiscoveryV2
java
package com.sxszck.shafulin.config.nacos;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
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 org.apache.commons.collections.CollectionUtils;
import org.springframework.cloud.client.ServiceInstance;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 参考文档 https://blog.csdn.net/m0_56287495/article/details/129240577
 */
public class NacosServiceDiscoveryV2 extends NacosServiceDiscovery {
    /**
     * 优先选择服务器ip
     */
    public static final Map<String,List<String>> IP_MAP = new HashMap<>();

    static {
        // D 服务
        IP_MAP.put("D-server", Arrays.asList("10.12.18.165"));
        // E 服务
        IP_MAP.put("E-server", Arrays.asList("10.12.18.173"));
    }
    public NacosServiceDiscoveryV2(NacosDiscoveryProperties discoveryProperties, NacosServiceManager nacosServiceManager) {
        super(discoveryProperties, nacosServiceManager);
        this.discoveryProperties = discoveryProperties;
        this.nacosServiceManager = nacosServiceManager;
    }

    private NacosDiscoveryProperties discoveryProperties;
    private NacosServiceManager nacosServiceManager;

  	//
    @Override
    public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
        String group = this.discoveryProperties.getGroup();
      	// 优先保证同分组下的服务调用
        List<Instance> instances = this.namingService().selectInstances(serviceId, group, true);
        if (CollectionUtils.isEmpty(instances)) {
          	// 如果同分组下找不到服务,那么就从默认分组下找服务
            instances = this.namingService().selectInstances(serviceId, "DEFAULT_GROUP", true);
        }
        if (IP_MAP.containsKey(serviceId)) {
            List<String> ipList = IP_MAP.get(serviceId);
            List<Instance> newInstances = instances.stream()
                    .filter(item -> ipList.contains(item.getIp()))
                    .collect(Collectors.toList());
            // 能查到服务器的ip,那么就返回
            if (CollectionUtils.isNotEmpty(newInstances)){
                return hostToServiceInstanceList(newInstances, serviceId);
            }
        }
        return hostToServiceInstanceList(instances, serviceId);
    }
    @Override
    public List<String> getServices() throws NacosException {
        String group = this.discoveryProperties.getGroup();
        ListView<String> services = this.namingService().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());
        Iterator var3 = instances.iterator();

        while (var3.hasNext()) {
            Instance instance = (Instance) var3.next();
            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()) {
            NacosServiceInstance nacosServiceInstance = new NacosServiceInstance();
            nacosServiceInstance.setHost(instance.getIp());
            nacosServiceInstance.setPort(instance.getPort());
            nacosServiceInstance.setServiceId(serviceId);
            nacosServiceInstance.setInstanceId(instance.getInstanceId());
            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() + "");
            if (instance.getMetadata() != null) {
                metadata.putAll(instance.getMetadata());
            }

            metadata.put("nacos.ephemeral", String.valueOf(instance.isEphemeral()));
            nacosServiceInstance.setMetadata(metadata);
            if (metadata.containsKey("secure")) {
                boolean secure = Boolean.parseBoolean((String) metadata.get("secure"));
                nacosServiceInstance.setSecure(secure);
            }

            return nacosServiceInstance;
        } else {
            return null;
        }
    }

    private NamingService namingService() {
        return this.nacosServiceManager.getNamingService();
    }
}

2.给服务启动器添加命令行传参

为什么要用这种方式传参呢?

  • 优先级最高容易生效
  • 不会修改代码配置文件,避免被版本控制提交
  • 不会修改代码,避免被版本控制提交
  • 配置文件不做任何更改
  1. 编辑启动器
  2. 覆盖配置(即命令行传参)
  3. 覆盖参数 属性名:spring.cloud.nacos.discovery.group,属性值:自己的姓名
  4. 启动服务

3.查看Nacos上的服务

在服务列表能看到你的服务,即启动成功

4.验证方式

启动网关服务,如果你的服务调用顺序是

  • 情况1: 网关服务->服务A/B/C,服务D/E (只启动网关服务即可)
  • 情况2: 网关服务->服务A->服务B,服务D/E (只启动网关,A服务即可)

结论:总之,如果你只是调用某个外部服务,外部服务的调用方也必须启动,不然调用不到你本地

参考文档

Nacos服务跨分组调用

Released under the MIT License.