基于nacos版本1.1.3.RELEASE. 以一个服务注册的demo为例,看nacos服务的实现.
public class NamingExample {
public static void main(String[] args) throws NacosException {
Properties properties = new Properties();
properties.setProperty("serverAddr", System.getProperty("serverAddr"));
properties.setProperty("namespace", System.getProperty("namespace"));
NamingService naming = NamingFactory.createNamingService(properties);
naming.registerInstance("nacos.test.3", "11.11.11.11", 8888, "TEST1");
naming.registerInstance("nacos.test.3", "2.2.2.2", 9999, "DEFAULT");
System.out.println(naming.getAllInstances("nacos.test.3"));
naming.deregisterInstance("nacos.test.3", "2.2.2.2", 9999, "DEFAULT");
System.out.println(naming.getAllInstances("nacos.test.3"));
naming.subscribe("nacos.test.3", new EventListener() {
@Override
public void onEvent(Event event) {
System.out.println(((NamingEvent)event).getServiceName());
System.out.println(((NamingEvent)event).getInstances());
}
});
}
反射初始化NacosNamingService类,并传入上图构造的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,分为以下几步: 第一步: 初始化NameSpace命名空间,默认是空字符串 第二步: 初始化注册中心的地址 第三步: 初始化web context的base路径 第四步: 初始化日志文件的名称. 第五步: 新创建事件分发处理器EventDispatcher对象. 第六步: 构建NamingProxy对象.设置properties配置对象. 第七步: 构建BeatReactor对象 第八步: 初始化客户端心跳的线程数 第九步: 构建HostReactor对象,已经初始化 第十步: 初始化长轮询的线程数量.
private void init(Properties properties) {
namespace = InitUtils.initNamespaceForNaming(properties);
initServerAddr(properties);
InitUtils.initWebRootContext();
initCacheDir();
initLogName(properties);
eventDispatcher = new EventDispatcher();
serverProxy = new NamingProxy(namespace, endpoint, serverList);
serverProxy.setProperties(properties);
beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties), initPollingThreadCount(properties));
}
然后调用NacosNamingService的registerInstance方法,传入serviceName、ip、port、clusterName,然后创建Instance的实例的,并设置ip、端口、权重、集群名称后,调用registerInstance执行服务注册.
public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException {
//Instance是服务实例的抽象
Instance instance = new Instance();
instance.setIp(ip);
instance.setPort(port);
instance.setWeight(1.0);
instance.setClusterName(clusterName);
registerInstance(serviceName, groupName, instance);
}
这里可以看到Instance的信息是代表一个服务实例.
public class Instance {
/**
* unique id of this instance.
*/
private String instanceId;
/**
* instance ip
*/
private String ip;
/**
* instance port
*/
private int port;
/**
* instance weight
*/
private double weight = 1.0D;
/**
* instance health status
*/
private boolean healthy = true;
/**
* If instance is enabled to accept request
*/
private boolean enabled = true;
/**
* If instance is ephemeral
* @since 1.0.0
*/
private boolean ephemeral = true;
/**
* cluster information of instance
*/
private String clusterName;
/**
* Service information of instance
*/
private String serviceName;
/**
* user extended attributes
*/
private Map<String, String> metadata = new HashMap<String, String>();
调用注册实例的时候,首先会判断实例信息是否是零时存储的,如果为true,会将beatInfo加入心跳的周期执行线程池中,默认5s执行一次心跳将自己服务信息的集群的注册中心上送心跳信息.
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
if (instance.isEphemeral()) {
BeatInfo beatInfo = new BeatInfo();
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
long instanceInterval = instance.getInstanceHeartBeatInterval();
beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);
beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
}
serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}
接着执行serverProxy的注册service的流程,就是注册中心的serverList的 /nacos/v1/ns/instance这个url的POSt接口,进行服务的注册,(服务端的后面详细分析).可以看出传递的参数跟beat的信息差不多,
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>(9);
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", JSON.toJSONString(instance.getMetadata()));
reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);
}
然后看下NamingService的订阅逻辑,这里传入参数是要订阅的服务名称,以及EventLister的监听回调.
public void subscribe(String serviceName, EventListener listener) throws NacosException {
subscribe(serviceName, new ArrayList<String>(), listener);
}
这里可以看到通过eventDispatcher的addListener,监听集群的serviceName
public void subscribe(String serviceName, String groupName, List<String> clusters, EventListener listener) throws NacosException {
eventDispatcher.addListener(hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ",")), StringUtils.join(clusters, ","), listener);
}
首先这里hostReactor是主要获取serviceInfo信息的获取和更新的,它也是实现Runnable接口,下面是run方法的代码,
@Override
public void run() {
try {
ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
if (serviceObj == null) {
updateServiceNow(serviceName, clusters);
executor.schedule(this, DEFAULT_DELAY, TimeUnit.MILLISECONDS);
return;
}
if (serviceObj.getLastRefTime() <= lastRefTime) {
updateServiceNow(serviceName, clusters);
serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
} else {
// if serviceName already updated by push, we should not override it
// since the push data may be different from pull through force push
refreshOnly(serviceName, clusters);
}
lastRefTime = serviceObj.getLastRefTime();
if (!eventDispatcher.isSubscribed(serviceName, clusters) && !futureMap.containsKey(ServiceInfo.getKey(serviceName, clusters))) {
// abort the update task:
NAMING_LOGGER.info("update task is stopped, service:" + serviceName + ", clusters:" + clusters);
return;
}
executor.schedule(this, serviceObj.getCacheMillis(), TimeUnit.MILLISECONDS);
} catch (Throwable e) {
NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e);
}
}
}
EventDispatcher是初始化事件监听器,并且执行Notifier线程.
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());
}
Notifier线程主要是执行监听回调处理逻辑, 回调所有的lister的onEvent的 方法.
private class Notifier implements Runnable {
@Override
public void run() {
while (true) {
ServiceInfo serviceInfo = null;
try {
serviceInfo = changedServices.poll(5, TimeUnit.MINUTES);
} catch (Exception ignore) {
}
if (serviceInfo == null) {
continue;
}
try {
List<EventListener> listeners = observerMap.get(serviceInfo.getKey());
if (!CollectionUtils.isEmpty(listeners)) {
for (EventListener listener : listeners) {
List<Instance> hosts = Collections.unmodifiableList(serviceInfo.getHosts());
listener.onEvent(new NamingEvent(serviceInfo.getName(), serviceInfo.getGroupName(), serviceInfo.getClusters(), hosts));
}
}
} catch (Exception e) {
NAMING_LOGGER.error("[NA] notify error for service: "
+ serviceInfo.getName() + ", clusters: " + serviceInfo.getClusters(), e);
}
}
}
}
总结
这篇主要是对nacos的注册中心的简单介绍,主要涉及服务的的注册和监听函数的回调.