背景
以前老项目使用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){ }
}
}