2. nacos之服务注册

目录

(1) 创建NamingService

(2) 服务注册

(3)naming处理服务注册请求

(3.1)构造service实例 

(3.2)保存instance到service实例中

(3.3)服务变化时通过udp发送通知


示例代码:

public class MainProvider {
    public static void main(String[] args) throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
        properties.put(PropertyKeyConst.USERNAME, "nacos");
        properties.put(PropertyKeyConst.PASSWORD, "nacos");

        Instance instance = new Instance();
        instance.setIp("127.0.0.1");
        instance.setPort(800);
        instance.setWeight(2);
        Map<String, String> map = new HashMap<String, String>();
        map.put("netType", "external");
        map.put("version", "2.0");
        instance.setMetadata(map);

        NamingService namingService = NacosFactory.createNamingService(properties);
        // 注册服务,并开启定时心跳任务,默认5s心跳间隔
        namingService.registerInstance("nacos.service.provider", instance);

        while (true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

(1) 创建NamingService

 public static NamingService createNamingService(Properties properties) throws NacosException {
        return NamingFactory.createNamingService(properties);
    }
 public static NamingService createNamingService(Properties properties) throws NacosException {
        try {
            Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
            Constructor constructor = driverImplClass.getConstructor(Properties.class);
            NamingService vendorImpl = (NamingService) constructor.newInstance(properties);
            return vendorImpl;
        } catch (Throwable e) {
            throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
        }
    }

通过反射实例化NacosNamingService

 public NacosNamingService(Properties properties) throws NacosException {
        init(properties);
    }

    private void init(Properties properties) throws NacosException {
        ValidatorUtils.checkInitParam(properties);
        this.namespace = InitUtils.initNamespaceForNaming(properties);
        InitUtils.initSerialization();
        initServerAddr(properties);
        InitUtils.initWebRootContext(properties);
        initCacheDir();
        initLogName(properties);
        // 服务代理
        this.serverProxy = new NamingProxy(this.namespace, this.endpoint, this.serverList, properties);
        // 心跳工具类型
        this.beatReactor = new BeatReactor(this.serverProxy, initClientBeatThreadCount(properties));
        this.hostReactor = new HostReactor(this.serverProxy, beatReactor, this.cacheDir, isLoadCacheAtStart(properties),
                isPushEmptyProtect(properties), initPollingThreadCount(properties));
    }

(2) 服务注册

    @Override
    public void registerInstance(String serviceName, Instance instance) throws NacosException {
        // 填充默认group
        registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
    }

    @Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
        // 检查instance 配置是否正确
        NamingUtils.checkInstanceIsLegal(instance);
        String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
        // 默认都是临时节点,客户端定时发送心跳
        // 持久化节点,客户端不发送心跳
        if (instance.isEphemeral()) {
            // 构造心跳信息
            BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
            // 开启定时心跳任务,默认5s间隔
            beatReactor.addBeatInfo(groupedServiceName, beatInfo);
        }
        // 服务注册
        serverProxy.registerService(groupedServiceName, groupName, instance);
    }

默认的instance都是临时节点,在注册的时候,如果是临时节点,就开启定时心跳任务,每5s发送一次心跳

接着来分析服务注册

 public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {

        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName,
                instance);

        // 构造请求参数
        final Map<String, String> params = new HashMap<String, String>(16);
        params.put(CommonParams.NAMESPACE_ID, namespaceId);
        params.put(CommonParams.SERVICE_NAME, serviceName);
        params.put(CommonParams.GROUP_NAME, groupName);
        params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
        params.put("ip", instance.getIp());
        params.put("port", String.valueOf(instance.getPort()));
        params.put("weight", String.valueOf(instance.getWeight()));
        params.put("enable", String.valueOf(instance.isEnabled()));
        params.put("healthy", String.valueOf(instance.isHealthy()));
        params.put("ephemeral", String.valueOf(instance.isEphemeral()));
        params.put("metadata", JacksonUtils.toJson(instance.getMetadata()));

        // 发送请求
        reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
    }
public static String webContext = "/nacos";

public static String nacosUrlBase = webContext + "/v1/ns";

public static String nacosUrlInstance = nacosUrlBase + "/instance";

 即请求的url是/nacos/v1/ns/instance, post请求

public String reqApi(String api, Map<String, String> params, String method) throws NacosException {
        // 构造空的body
        return reqApi(api, params, Collections.EMPTY_MAP, method);
    }

    public String reqApi(String api, Map<String, String> params, Map<String, String> body, String method)
            throws NacosException {
        // 追加参数serverList, 这里的serverList,就是namingServer的地址,
        // properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
        return reqApi(api, params, body, getServerList(), method);
    }
public String reqApi(String api, Map<String, String> params, Map<String, String> body, List<String> servers,
            String method) throws NacosException {
        // 追加namespace参数
        params.put(CommonParams.NAMESPACE_ID, getNamespaceId());

        if (CollectionUtils.isEmpty(servers) && StringUtils.isBlank(nacosDomain)) {
            throw new NacosException(NacosException.INVALID_PARAM, "no server available");
        }

        NacosException exception = new NacosException();
        // serverAddr中只有一个服务地址,就赋值给nacosDomain
        // 只配置了一个namingServer
        if (StringUtils.isNotBlank(nacosDomain)) {
            // 发送请求,最多失败重试3次
            for (int i = 0; i < maxRetry; i++) {
                try {
                    return callServer(api, params, body, nacosDomain, method);
                } catch (NacosException e) {
                    exception = e;
                    if (NAMING_LOGGER.isDebugEnabled()) {
                        NAMING_LOGGER.debug("request {} failed.", nacosDomain, e);
                    }
                }
            }
        }
        // 多个namingserver
        else {
            Random random = new Random(System.currentTimeMillis());
            // 先随机选择一个server进行发送
            int index = random.nextInt(servers.size());

            for (int i = 0; i < servers.size(); i++) {
                // 根据index选择选取的服务
                String server = servers.get(index);
                try {
                    // 发送请求
                    return callServer(api, params, body, server, method);
                } catch (NacosException e) {
                    exception = e;
                    if (NAMING_LOGGER.isDebugEnabled()) {
                        NAMING_LOGGER.debug("request {} failed.", server, e);
                    }
                }
                // 如果发送失败,就尝试向下个个server发送,移动index
                index = (index + 1) % servers.size();
            }
        }

        NAMING_LOGGER.error("request: {} failed, servers: {}, code: {}, msg: {}", api, servers, exception.getErrCode(),
                exception.getErrMsg());

        throw new NacosException(exception.getErrCode(),
                "failed to req API:" + api + " after all servers(" + servers + ") tried: " + exception.getMessage());

    }

如果配置的namingserver就只有一个,如果失败就会重试两次,一共三次,如果配置的namingserver是多个,先随机选择一个server发送,如果发送失败,就将选取下一个server

   public String callServer(String api, Map<String, String> params, Map<String, String> body, String curServer,
            String method) throws NacosException {
        logger.info("向[{}]发送请求:{}", curServer, api);

        long start = System.currentTimeMillis();
        long end = 0;
        // 注入安全认证信息
        injectSecurityInfo(params);
        // 构建请求头
        Header header = builderHeader();

        // 构造完整的地址
        String url;
        if (curServer.startsWith(UtilAndComs.HTTPS) || curServer.startsWith(UtilAndComs.HTTP)) {
            url = curServer + api;
        } else {
            if (!IPUtil.containsPort(curServer)) {
                curServer = curServer + IPUtil.IP_PORT_SPLITER + serverPort;
            }
            url = NamingHttpClientManager.getInstance().getPrefix() + curServer + api;
        }

        try {
            // 发送form格式的请求
            HttpRestResult<String> restResult = nacosRestTemplate
                    .exchangeForm(url, header, Query.newInstance().initParams(params), body, method, String.class);
            end = System.currentTimeMillis();

            MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(restResult.getCode()))
                    .observe(end - start);

            if (restResult.ok()) {
                return restResult.getData();
            }
            if (HttpStatus.SC_NOT_MODIFIED == restResult.getCode()) {
                return StringUtils.EMPTY;
            }
            throw new NacosException(restResult.getCode(), restResult.getMessage());
        } catch (Exception e) {
            NAMING_LOGGER.error("[NA] failed to request", e);
            throw new NacosException(NacosException.SERVER_ERROR, e);
        }
    }
 public <T> HttpRestResult<T> exchangeForm(String url, Header header, Query query, Map<String, String> bodyValues,
            String httpMethod, Type responseType) throws Exception {
        RequestHttpEntity requestHttpEntity = new RequestHttpEntity(
                header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), query, bodyValues);
        return execute(url, httpMethod, requestHttpEntity, responseType);
    }

最终是通过application/x-www-form-urlencoded;charset=UTF-8格式发送出去的

(3)naming处理服务注册请求

服务端注册的请求是/nacos/v1/ns/instance, post请求

nacos-naming中com.alibaba.nacos.naming.controllers.InstanceController#register来接收处理服务注册请求

@CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {
        final String namespaceId = WebUtils
                .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
        final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        NamingUtils.checkServiceNameFormat(serviceName);
        // 根据请求参数重新构造instance
        final Instance instance = parseInstance(request);

        logger.info("接收到注册请求:{}", instance.toJson());
        // 调用serviceManager
        serviceManager.registerInstance(namespaceId, serviceName, instance);
        return "ok";
    }
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
        //1. 如果service不存在,就初始化一个service实例,并监听服务变化
        createEmptyService(namespaceId, serviceName, instance.isEphemeral());
        // 获取服务实例
        Service service = getService(namespaceId, serviceName);

        if (service == null) {
            throw new NacosException(NacosException.INVALID_PARAM,
                    "service not found, namespace: " + namespaceId + ", service: " + serviceName);
        }
        //2. 添加服务实例,并触发相关通知
        addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
    }

(3.1)构造service实例 

public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
        createServiceIfAbsent(namespaceId, serviceName, local, null);
    }

    public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
            throws NacosException {
        // 从serviceMap中尝试获取
        Service service = getService(namespaceId, serviceName);
        if (service == null) {

            Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
            service = new Service();
            service.setName(serviceName);
            service.setNamespaceId(namespaceId);
            service.setGroupName(NamingUtils.getGroupName(serviceName));
            // now validate the service. if failed, exception will be thrown
            service.setLastModifiedMillis(System.currentTimeMillis());
            service.recalculateChecksum();
            if (cluster != null) {
                cluster.setService(service);
                service.getClusterMap().put(cluster.getName(), cluster);
            }
            service.validate();
            // 添加服务并且初始化服务
            putServiceAndInit(service);
            // local=instance.isEphemeral()=true
            if (!local) {
                addOrReplaceService(service);
            }
        }
    }

  private void putServiceAndInit(Service service) throws NacosException {
        // 保存到本地serviceMap中
        putService(service);
        service = getService(service.getNamespaceId(), service.getName());
        service.init();
        // 监听服务变化
        consistencyService
                .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
        consistencyService
                .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
        Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
    }
    public void putService(Service service) {
        // 操作serviceMap下的key是串行的,
        if (!serviceMap.containsKey(service.getNamespaceId())) {
            synchronized (putServiceLock) {
                if (!serviceMap.containsKey(service.getNamespaceId())) {
                    // ConcurrentSkipListMap跳表
                    serviceMap.put(service.getNamespaceId(), new ConcurrentSkipListMap<>());
                }
            }
        }
        serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service);
    }

serviceMap的定义如下

private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();

外层map的key是namespaceId, 内层map的key是seriveName, 内层的值是ConcurrentSkipListMap, 并发跳表

先初始化serivce,添加了一个服务心跳检查的任务

 /**
     * Init service.
     */
    public void init() {
        // 针对这个服务的健康检查
        HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
        // 
        for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
            entry.getValue().setService(this);
            entry.getValue().init();
        }
    }

然后向consistencyService一致性服务中注册了服务变化的监听器,后面会用到 

 consistencyService的定义

@Resource(name = "consistencyDelegate")
private ConsistencyService consistencyService; 
@Service("consistencyDelegate")
public class DelegateConsistencyServiceImpl implements ConsistencyService {}

 这里的consistencyService是DelegateConsistencyServiceImpl实例

com.alibaba.nacos.naming.consistency.DelegateConsistencyServiceImpl#listen

 public void listen(String key, RecordListener listener) throws NacosException {

        // this special key is listened by both:
        if (KeyBuilder.SERVICE_META_KEY_PREFIX.equals(key)) {
            persistentConsistencyService.listen(key, listener);
            ephemeralConsistencyService.listen(key, listener);
            return;
        }

        mapConsistencyService(key).listen(key, listener);
    }
   private ConsistencyService mapConsistencyService(String key) {
        // 通过key来提取是否是临时节点,然后返回对应的consistencyService
        // 是临时节点:ephemeralConsistencyService --> DistroConsistencyServiceImpl
        // 不是临时节点:persistentConsistencyService --> PersistentConsistencyServiceDelegateImpl
        return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
    }
   public void listen(String key, RecordListener listener) throws NacosException {
        if (!listeners.containsKey(key)) {
            listeners.put(key, new ConcurrentLinkedQueue<>());
        }

        if (listeners.get(key).contains(listener)) {
            return;
        }

        listeners.get(key).add(listener);
    }

(3.2)保存instance到service实例中

   public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
            throws NacosException {

        // key="com.alibaba.nacos.naming.iplist.ephemeral." + namespaceId + "##" + serviceName
        String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);

        Service service = getService(namespaceId, serviceName);
        // 对同一个service操作串行化
        synchronized (service) {
            List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);

            Instances instances = new Instances();
            instances.setInstanceList(instanceList);
            // 保存到一致性服务中
            consistencyService.put(key, instances);
        }
    }

 com.alibaba.nacos.naming.consistency.DelegateConsistencyServiceImpl#put

public void put(String key, Record value) throws NacosException {
        mapConsistencyService(key).put(key, value);
    }

com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl#put

public void put(String key, Record value) throws NacosException {
        // 保存数据,并通知监听器
        onPut(key, value);
        // 分布式同步
        distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,
                globalConfig.getTaskDispatchPeriod() / 2);
    }

分布式同步暂时不去分析,

    public void onPut(String key, Record value) {

        if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
            Datum<Instances> datum = new Datum<>();
            datum.value = (Instances) value;
            datum.key = key;
            datum.timestamp.incrementAndGet();
            dataStore.put(key, datum);
        }

        if (!listeners.containsKey(key)) {
            return;
        }

        notifier.addTask(key, DataOperation.CHANGE);
    }

onPut()方法,先将数据保存到dataStore中,然后key有listener的话,就去调用notifier.addTask()

这里listener不为空,为什么呢?在3.1中的putServiceAndInit()方法中,将service作为listener添加进去了

private void putServiceAndInit(Service service) throws NacosException {
    // 保存到本地serviceMap中
    putService(service);
    service = getService(service.getNamespaceId(), service.getName());
    service.init();
    // 监听服务变化
    consistencyService
            .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
    consistencyService
            .listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
    Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
} 

(3.3)服务变化时通过udp发送通知

接着上面的

notifier.addTask(key, DataOperation.CHANGE); 

com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.Notifier#addTask

      public void addTask(String datumKey, DataOperation action) {
            // 已经有变化记录了,就不再发送通知了
            if (services.containsKey(datumKey) && action == DataOperation.CHANGE) {
                return;
            }
            if (action == DataOperation.CHANGE) {
                // 如果是服务变化,就将服务名称添加到services中
                services.put(datumKey, StringUtils.EMPTY);
            }
            // 封装key和action为pair,添加到queue中
            tasks.offer(Pair.with(datumKey, action));
        }

services和tasks的定义

private ConcurrentHashMap<String, String> services = new ConcurrentHashMap<>(10 * 1024);

private BlockingQueue<Pair<String, DataOperation>> tasks = new ArrayBlockingQueue<>(1024 * 1024);

 而Notifier implements Runnable,那Notifier什么时候启动的呢?

com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl#init


    @PostConstruct
    public void init() {
        GlobalExecutor.submitDistroNotifyTask(notifier);
    }

这个方法加了@PostConstruce注解,所以在构造函数执行之后就去将notifier提交到线程池

com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.Notifier#run

     public void run() {
            Loggers.DISTRO.info("distro notifier started");

            for (; ; ) {
                try {
                    // 从队列中找到
                    Pair<String, DataOperation> pair = tasks.take();
                    handle(pair);
                } catch (Throwable e) {
                    Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);
                }
            }
        }
private void handle(Pair<String, DataOperation> pair) {
            try {
                // key
                String datumKey = pair.getValue0();
                DataOperation action = pair.getValue1();
                // 从services中移除相关key
                services.remove(datumKey);

                int count = 0;
                // 没有listener
                if (!listeners.containsKey(datumKey)) {
                    return;
                }

                for (RecordListener listener : listeners.get(datumKey)) {
                    logger.info("通知监听者{}, 事件:[{}]{}", listener, datumKey, action);

                    count++;

                    try {
                        // 服务变化
                        if (action == DataOperation.CHANGE) {
                            listener.onChange(datumKey, dataStore.get(datumKey).value);
                            continue;
                        }
                        // 服务删除
                        if (action == DataOperation.DELETE) {
                            listener.onDelete(datumKey);
                            continue;
                        }
                    } catch (Throwable e) {
                        Loggers.DISTRO.error("[NACOS-DISTRO] error while notifying listener of key: {}", datumKey, e);
                    }
                }

                if (Loggers.DISTRO.isDebugEnabled()) {
                    Loggers.DISTRO
                            .debug("[NACOS-DISTRO] datum change notified, key: {}, listener count: {}, action: {}",
                                    datumKey, count, action.name());
                }
            } catch (Throwable e) {
                Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);
            }
        }
    }

3.2中分析了,会将service实例注册成为listener, action=change

com.alibaba.nacos.naming.core.Service#onChange

public void onChange(String key, Instances value) throws Exception {
        logger.info("#Service.onChange# [{}]:{}", key, value);
        Loggers.SRV_LOG.info("[NACOS-RAFT] datum is changed, key: {}, value: {}", key, value);

        for (Instance instance : value.getInstanceList()) {

            if (instance == null) {
                // Reject this abnormal instance list:
                throw new RuntimeException("got null instance " + key);
            }
            // 修正实例权重
            if (instance.getWeight() > 10000.0D) {
                instance.setWeight(10000.0D);
            }

            if (instance.getWeight() < 0.01D && instance.getWeight() > 0.0D) {
                instance.setWeight(0.01D);
            }
        }
        // 更新服务实例的ips
        updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key));
        // 重新计算checksum
        recalculateChecksum();
    }
public void updateIPs(Collection<Instance> instances, boolean ephemeral) {
        Map<String, List<Instance>> ipMap = new HashMap<>(clusterMap.size());
        for (String clusterName : clusterMap.keySet()) {
            ipMap.put(clusterName, new ArrayList<>());
        }

        // 将instance集合按cluster分类保存在ipMap中,key为clusterName
        for (Instance instance : instances) {
            try {
                if (instance == null) {
                    Loggers.SRV_LOG.error("[NACOS-DOM] received malformed ip: null");
                    continue;
                }

                if (StringUtils.isEmpty(instance.getClusterName())) {
                    instance.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);
                }

                if (!clusterMap.containsKey(instance.getClusterName())) {
                    Loggers.SRV_LOG
                            .warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",
                                    instance.getClusterName(), instance.toJson());
                    Cluster cluster = new Cluster(instance.getClusterName(), this);
                    cluster.init();
                    getClusterMap().put(instance.getClusterName(), cluster);
                }

                List<Instance> clusterIPs = ipMap.get(instance.getClusterName());
                if (clusterIPs == null) {
                    clusterIPs = new LinkedList<>();
                    ipMap.put(instance.getClusterName(), clusterIPs);
                }

                clusterIPs.add(instance);
            } catch (Exception e) {
                Loggers.SRV_LOG.error("[NACOS-DOM] failed to process ip: " + instance, e);
            }
        }
        // 更新
        for (Map.Entry<String, List<Instance>> entry : ipMap.entrySet()) {
            //make every ip mine
            List<Instance> entryIPs = entry.getValue();
            // 更新每个cluster下的instance集合
            clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral);
        }

        setLastModifiedMillis(System.currentTimeMillis());
        // 发送push通知
        getPushService().serviceChanged(this);

        // 打印日志
        StringBuilder stringBuilder = new StringBuilder();

        for (Instance instance : allIPs()) {
            stringBuilder.append(instance.toIpAddr()).append("_").append(instance.isHealthy()).append(",");
        }

        Loggers.EVT_LOG.info("[IP-UPDATED] namespace: {}, service: {}, ips: {}", getNamespaceId(), getName(),
                stringBuilder.toString());

    }

这里就是把instance集合按clusterName分类,然后去更新cluster先的实例信息,涉及新增instance,删除instance,instance属性发生变更等。

然后会调用pushService发送变更事件

public PushService getPushService() {
        return ApplicationUtils.getBean(PushService.class);
    }
 public void serviceChanged(Service service) {
        // merge some change events to reduce the push frequency:
        // 合并变化事件,如果已经有了,就不在发送事件,减少push的频率
        if (futureMap
                .containsKey(UtilsAndCommons.assembleFullServiceName(service.getNamespaceId(), service.getName()))) {
            return;
        }

        this.applicationContext.publishEvent(new ServiceChangeEvent(this, service));
    }

通过applicationContext.publishEvent(ServiceChangeEvent)发送出去,而pushservice又自己监听了这个事件

com.alibaba.nacos.naming.push.PushService#onApplicationEvent

public void onApplicationEvent(ServiceChangeEvent event) {
        Service service = event.getService();
        String serviceName = service.getName();
        String namespaceId = service.getNamespaceId();
        // 使用udp线程池提交任务
        Future future = GlobalExecutor.scheduleUdpSender(() -> {
            try {
                Loggers.PUSH.info(serviceName + " is changed, add it to push queue.");
                // 根据namespaceid+serviceName找到对应的client
                ConcurrentMap<String, PushClient> clients = clientMap
                        .get(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName));
                if (MapUtils.isEmpty(clients)) {
                    return;
                }

                Map<String, Object> cache = new HashMap<>(16);
                long lastRefTime = System.nanoTime();
                for (PushClient client : clients.values()) {
                    //认为是pushclient超过缓存时间阈值了,默认10s
                    if (client.zombie()) {
                        Loggers.PUSH.debug("client is zombie: " + client.toString());
                        clients.remove(client.toString());
                        Loggers.PUSH.debug("client is zombie: " + client.toString());
                        continue;
                    }

                    Receiver.AckEntry ackEntry;
                    Loggers.PUSH.debug("push serviceName: {} to client: {}", serviceName, client.toString());
                    String key = getPushCacheKey(serviceName, client.getIp(), client.getAgent());
                    byte[] compressData = null;
                    Map<String, Object> data = null;
                    // getDefaultPushCacheMillis()默认10s
                    if (switchDomain.getDefaultPushCacheMillis() >= 20000 && cache.containsKey(key)) {
                        org.javatuples.Pair pair = (org.javatuples.Pair) cache.get(key);
                        compressData = (byte[]) (pair.getValue0());
                        data = (Map<String, Object>) pair.getValue1();

                        Loggers.PUSH.debug("[PUSH-CACHE] cache hit: {}:{}", serviceName, client.getAddrStr());
                    }

                    if (compressData != null) {
                        ackEntry = prepareAckEntry(client, compressData, data, lastRefTime);
                    } else {
                        // 构造ackEntry,里面包含了要发送的udp数据包
                        ackEntry = prepareAckEntry(client, prepareHostsData(client), lastRefTime);
                        if (ackEntry != null) {
                            // 保存数据包到缓存
                            cache.put(key, new org.javatuples.Pair<>(ackEntry.origin.getData(), ackEntry.data));
                        }
                    }

                    Loggers.PUSH.info("serviceName: {} changed, schedule push for: {}, agent: {}, key: {}",
                            client.getServiceName(), client.getAddrStr(), client.getAgent(),
                            (ackEntry == null ? null : ackEntry.key));
                    // 通过udp发送出去
                    udpPush(ackEntry);
                }
            } catch (Exception e) {
                Loggers.PUSH.error("[NACOS-PUSH] failed to push serviceName: {} to client, error: {}", serviceName, e);

            } finally {
                // 发送完成,或者出现异常,从futureMap移除
                futureMap.remove(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName));
            }

        }, 1000, TimeUnit.MILLISECONDS);

        // 将future保存到futureMaap中,
        futureMap.put(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName), future);

    }
private static Receiver.AckEntry udpPush(Receiver.AckEntry ackEntry) {
        if (ackEntry == null) {
            Loggers.PUSH.error("[NACOS-PUSH] ackEntry is null.");
            return null;
        }
        // 到达最大重试次数
        if (ackEntry.getRetryTimes() > MAX_RETRY_TIMES) {
            Loggers.PUSH.warn("max re-push times reached, retry times {}, key: {}", ackEntry.retryTimes, ackEntry.key);
            ackMap.remove(ackEntry.key);
            udpSendTimeMap.remove(ackEntry.key);
            failedPush += 1;
            return ackEntry;
        }

        try {
            if (!ackMap.containsKey(ackEntry.key)) {
                totalPush++;
            }
            // 保存到ackMap中
            ackMap.put(ackEntry.key, ackEntry);
            // 保存到udpSendTimeMap中
            udpSendTimeMap.put(ackEntry.key, System.currentTimeMillis());

            Loggers.PUSH.info("send udp packet: " + ackEntry.key);
            // 发生udp数据包
            udpSocket.send(ackEntry.origin);
            // 增加发送次数
            ackEntry.increaseRetryTime();

            // 10s后,如果ackMap中,还有ackentry.key, 就重试发送
            GlobalExecutor.scheduleRetransmitter(new Retransmitter(ackEntry),
                    TimeUnit.NANOSECONDS.toMillis(ACK_TIMEOUT_NANOS), TimeUnit.MILLISECONDS);

            return ackEntry;
        } catch (Exception e) {
            Loggers.PUSH.error("[NACOS-PUSH] failed to push data: {} to client: {}, error: {}", ackEntry.data,
                    ackEntry.origin.getAddress().getHostAddress(), e);
            ackMap.remove(ackEntry.key);
            udpSendTimeMap.remove(ackEntry.key);
            failedPush += 1;

            return null;
        }
    }

这里将服务变化的通知,通过udp发送出去,那pushclient 从哪里来的呢,其实是从服务发现时维护的。下一篇会分析:3. nacos之服务发现_ZXH240651200的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nacos是阿里巴巴开源的一个动态服务发现、配置管理和服务管理平台,它可以帮助我们实现服务注册与发现。下面简单介绍一下Nacos如何实现服务注册: 1. 引入Nacos SDK 在我们的项目中引入Nacos SDK,以Java项目为例,可以使用Maven或Gradle等工具在pom.xml或build.gradle文件中添加如下依赖: ```xml <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>${nacos.version}</version> </dependency> ``` 2. 创建Nacos实例 在代码中创建一个Nacos实例,用于连接Nacos服务端。可以通过以下代码实现: ```java import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; public class NacosInstance { public static void main(String[] args) throws NacosException { // Nacos服务端的地址 String serverAddr = "localhost:8848"; // 配置Nacos连接参数 Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); // 创建Nacos实例 NamingService naming = NacosFactory.createNamingService(properties); } } ``` 3. 注册服务 使用创建好的Nacos实例进行服务注册,可以通过以下代码实现: ```java // 服务名称 String serviceName = "example-service"; // 服务ip地址 String ip = "localhost"; // 服务端口号 int port = 8080; // 注册服务 naming.registerInstance(serviceName, ip, port); ``` 4. 发现服务 使用创建好的Nacos实例进行服务发现,可以通过以下代码实现: ```java // 服务名称 String serviceName = "example-service"; // 获取服务列表 List<Instance> instances = naming.getAllInstances(serviceName); // 遍历服务列表 for (Instance instance : instances) { System.out.println(instance.getIp() + ":" + instance.getPort()); } ``` 以上就是Nacos实现服务注册的简单介绍。在实际应用中,我们还可以使用Nacos实现动态配置管理、服务健康检查等功能,来提高应用的可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值