庖丁解牛手刃nacos 服务上线下线通知。

背景

以前老项目使用Eureka 做服务注册中心。
其中有一项重要的功能就是要监听某几项微服务的宕机情况。在宕机的时候需要做一些缓存的清理工作。
最近想切换到Nacos上来。但是经过仔细研究,发现nacos虽然可以订阅微服务的上线下线通知。但是却不知道是哪个实例进行了改变。不得不多,这个消息通知有点蛋疼。没有办法只能自己来实现了。辛亏是开源的。哈哈。

nacos基础版本

从github上下载1.3.0 的源码(release),不要下载master的源码,master的版本有很多bug。
下载地址:https://github.com/alibaba/nacos/archive/1.3.0.zip

修改nacos pom文件,

注释一些插件

       <profile>
            <id>release-sign-artifacts</id>
            <activation>
                <property>
                    <name>performRelease</name>
                    <value>true</value>
                </property>
            </activation>

            <properties>
                <maven.javadoc.skip>false</maven.javadoc.skip>
            </properties>

            <build>
<!--                <plugins>-->
<!--                    <plugin>-->
<!--                        <artifactId>maven-failsafe-plugin</artifactId>-->
<!--                        <version>${maven-failsafe-plugin.version}</version>-->
<!--                        <configuration>-->
<!--                            <argLine>@{failsafeArgLine}</argLine>-->
<!--                            <argLine>-Dnacos.standalone=true</argLine>-->
<!--                            <includes>-->
<!--                                <include>**/*CITCase.java</include>-->
<!--                            </includes>-->
<!--                        </configuration>-->
<!--                        <executions>-->
<!--                            <execution>-->
<!--                                <goals>-->
<!--                                    <goal>integration-test</goal>-->
<!--                                    <goal>verify</goal>-->
<!--                                </goals>-->
<!--                            </execution>-->
<!--                        </executions>-->
<!--                    </plugin>-->
<!--                </plugins>-->
            </build>
        </profile>
        <profile>
            <id>it-test</id>
            <build>
<!--                <plugins>-->
<!--                    <plugin>-->
<!--                        <artifactId>maven-failsafe-plugin</artifactId>-->
<!--                        <version>${maven-failsafe-plugin.version}</version>-->
<!--                        <configuration>-->
<!--                            <argLine>@{failsafeArgLine}</argLine>-->
<!--                            <argLine>-Dnacos.standalone=true</argLine>-->
<!--                            <includes>-->
<!--                                <include>**/*ITCase.java</include>-->
<!--                            </includes>-->
<!--                        </configuration>-->
<!--                        <executions>-->
<!--                            <execution>-->
<!--                                <goals>-->
<!--                                    <goal>integration-test</goal>-->
<!--                                    <goal>verify</goal>-->
<!--                                </goals>-->
<!--                            </execution>-->
<!--                        </executions>-->
<!--                    </plugin>-->
<!--                </plugins>-->
            </build>
        </profile>
        <profile>
            <id>sonar-apache</id>
            <properties>
                <!-- URL of the ASF SonarQube server -->
                <sonar.host.url>https://builds.apache.org/analysis</sonar.host.url>
            </properties>
        </profile>
    </profiles>

修改maven仓库提交地址和ID

  <distributionManagement>
        <snapshotRepository>
            <!-- 这里的ID一定要在maven setting文件中存在于server下的ID -->
            <id>snapshots</id>
            <url>http://192.168.1.239:8081/nexus/content/repositories/snapshots/</url>
        </snapshotRepository>
        <repository>
            <id>releases</id>
            <url>http://192.168.1.239:8081/nexus/content/repositories/releases/</url>
        </repository>
    </distributionManagement>

注意:snapshots 和releases 必须要再maven 的setting.xml 中的节点中对应。

修改NamingEvent

com.alibaba.nacos.api.naming.listener.NamingEvent
增加private int eventType;


package com.alibaba.nacos.api.naming.listener;

import java.util.List;

import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.fasterxml.jackson.annotation.JsonIgnore;

import static org.apache.commons.lang3.StringUtils.isEmpty;

/**
 * Naming Event
 *
 * @author nkorange
 */
public class NamingEvent implements Event {
    public static final int  EVENT_ADD=0;
    public static final int  EVENT_MOD=1;
    public static final int  EVENT_REMV=2;
    private String serviceName;

    private String groupName;

    private String clusters;
    private int eventType;

    private List<Instance> instances;

    public NamingEvent(String serviceName, List<Instance> instances) {
        this.serviceName = serviceName;
        this.instances = instances;
        this.eventType=eventType;
    }

    public NamingEvent(String serviceName, String groupName, String clusters,int eventType, List<Instance> instances) {
        this.serviceName = serviceName;
        this.groupName = groupName;
        this.clusters = clusters;
        this.eventType=eventType;
        this.instances = instances;
    }

    @JsonIgnore
    public String getKey() {
        return getKey(serviceName, clusters);
    }
    @JsonIgnore
    public static String getKey(String name, String clusters) {

        if (!isEmpty(clusters)) {
            return name + Constants.SERVICE_INFO_SPLITER + clusters;
        }

        return name;
    }

    public int getEventType() {
        return eventType;
    }

    public void setEventType(int eventType) {
        this.eventType = eventType;
    }

    public String getServiceName() {
        return serviceName;
    }

    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    public List<Instance> getInstances() {
        return instances;
    }

    public void setInstances(List<Instance> instances) {
        this.instances = instances;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public String getClusters() {
        return clusters;
    }

    public void setClusters(String clusters) {
        this.clusters = clusters;
    }
}

修改EventDispatcher

com.alibaba.nacos.client.naming.core.EventDispatcher


package com.alibaba.nacos.client.naming.core;

import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;

import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;

/**
 * @author xuanyin
 */
public class EventDispatcher {

    private ExecutorService executor = null;

    private BlockingQueue<NamingEvent> changedServices = new LinkedBlockingQueue<NamingEvent>();

    private ConcurrentMap<String, List<EventListener>> observerMap
        = new ConcurrentHashMap<String, List<EventListener>>();

    public EventDispatcher() {

        executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r, "com.alibaba.nacos.naming.client.listener");
                thread.setDaemon(true);

                return thread;
            }
        });

        executor.execute(new Notifier());
    }

    public void addListener(ServiceInfo serviceInfo, String clusters, EventListener listener) {

        NAMING_LOGGER.info("[LISTENER] adding " + serviceInfo.getName() + " with " + clusters + " to listener map");
        List<EventListener> observers = Collections.synchronizedList(new ArrayList<EventListener>());
        observers.add(listener);

        observers = observerMap.putIfAbsent(ServiceInfo.getKey(serviceInfo.getName(), clusters), observers);
        if (observers != null) {
            observers.add(listener);
        }

        serviceChanged(new NamingEvent(serviceInfo.getName(),serviceInfo.getGroupName(),serviceInfo.getClusters(),0,serviceInfo.getHosts()));
    }

    public void removeListener(String serviceName, String clusters, EventListener listener) {

        NAMING_LOGGER.info("[LISTENER] removing " + serviceName + " with " + clusters + " from listener map");

        List<EventListener> observers = observerMap.get(ServiceInfo.getKey(serviceName, clusters));
        if (observers != null) {
            Iterator<EventListener> iter = observers.iterator();
            while (iter.hasNext()) {
                EventListener oldListener = iter.next();
                if (oldListener.equals(listener)) {
                    iter.remove();
                }
            }
            if (observers.isEmpty()) {
                observerMap.remove(ServiceInfo.getKey(serviceName, clusters));
            }
        }
    }

    public boolean isSubscribed(String serviceName, String clusters) {
        return observerMap.containsKey(ServiceInfo.getKey(serviceName, clusters));
    }

    public List<ServiceInfo> getSubscribeServices() {
        List<ServiceInfo> serviceInfos = new ArrayList<ServiceInfo>();
        for (String key : observerMap.keySet()) {
            serviceInfos.add(ServiceInfo.fromKey(key));
        }
        return serviceInfos;
    }

    public void serviceChanged(NamingEvent namingEvent) {
        if (namingEvent == null) {
            return;
        }

        changedServices.add(namingEvent);
    }

    private class Notifier implements Runnable {
        @Override
        public void run() {
            while (true) {
                NamingEvent namingEvent = null;
                try {
                    namingEvent = changedServices.poll(5, TimeUnit.MINUTES);
                } catch (Exception ignore) {
                }

                if (namingEvent == null) {
                    continue;
                }

                try {
                    List<EventListener> listeners = observerMap.get(namingEvent.getKey());

                    if (!CollectionUtils.isEmpty(listeners)) {
                        for (EventListener listener : listeners) {
                            listener.onEvent(namingEvent);
                        }
                    }

                } catch (Exception e) {
                    NAMING_LOGGER.error("[NA] notify error for service: "
                        + namingEvent.getServiceName() + ", clusters: " + namingEvent.getClusters(), e);
                }
            }
        }
    }
}

验证

package com.alibaba.nacos.example.spring.cloud;

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.listener.NamingEvent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;

/**

  • @author xiaojing
    */
    @SpringBootApplication
    @EnableDiscoveryClient
    public class NacosListenerApplication {

    public static void main(String[] args) {
    SpringApplication.run(NacosListenerApplication.class, args);

     //创建一个nacos实例
     try {
         NamingService naming = NamingFactory.createNamingService("192.168.33.103:8848");
         //订阅一个服务
         String serviceName1 = "service-consumer";
         naming.subscribe(serviceName1, event -> {
             if (event instanceof NamingEvent) {
    
                 System.out.println("订阅到数据"+serviceName1);
                 System.out.println(((NamingEvent) event).getInstances());
             }
         });
         String serviceName2 = "service-provider";
         naming.subscribe(serviceName2, event -> {
             if (event instanceof NamingEvent) {
    
                 System.out.println("订阅到数据"+serviceName2);
                 NamingEvent event1=(NamingEvent) event;
                 System.out.println(event1.getEventType()+"=="+event1.getServiceName()+event1.getGroupName()+"=="+event1.getClusters()+"=="+event1.getInstances());
                 try {
                     System.out.println("all:"+naming.getAllInstances(serviceName2));
                 } catch (NacosException e) {
                     e.printStackTrace();
                 }
             }
         });
         System.out.println("订阅完成,准备等数来");
         Thread.sleep(Integer.MAX_VALUE);
     }catch (Exception e){
    
     }
    

    }

}

修改后的源码下载

以上程序源码下载地址

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值