package com.networknt.cluster;
import com.networknt.balance.LoadBalance;
import com.networknt.registry.NotifyListener;
import com.networknt.registry.Registry;
import com.networknt.registry.URL;
import com.networknt.registry.URLImpl;
import com.networknt.service.SingletonServiceFactory;
import com.networknt.utility.ConcurrentHashSet;
import com.networknt.utility.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* This is the only concrete implementation of cluster interface. It basically integrates
* service discovery, service registry and load balance together to provide a common way
* to convert a protocal, service id and request key to a url that can be addressed and
* invoked.
*
* Created by stevehu on 2017-01-27.
*/
public class LightCluster implements Cluster {
private static Logger logger = LoggerFactory.getLogger(LightCluster.class);
private static Registry registry = (Registry) SingletonServiceFactory.getBean(Registry.class);
private static LoadBalance loadBalance = (LoadBalance)SingletonServiceFactory.getBean(LoadBalance.class);
private static Set<URL> subscribedSet = new ConcurrentHashSet<>();
private static Map<String, List<URL>> serviceMap = new ConcurrentHashMap<>();
public LightCluster() {
if(logger.isInfoEnabled()) logger.info("A LightCluster instance is started");
}
/**
* Implement serviceToUrl with client side service discovery.
*
* @param protocol String
* @param serviceName String
* @param requestKey String
* @return String
*/
@Override
public String serviceToUrl(String protocol, String serviceName, String requestKey) {
if(logger.isDebugEnabled()) logger.debug("protocol = " + protocol + " serviceName = " + serviceName);
// lookup in serviceMap first, if not there, then subscribe and discover.
List<URL> urls = serviceMap.get(serviceName);
if(logger.isDebugEnabled()) logger.debug("cached serviceName " + serviceName + " urls = " + urls);
if(urls == null) {
URL subscribeUrl = URLImpl.valueOf("light://localhost/" + serviceName);
if(logger.isDebugEnabled()) logger.debug("subscribeUrl = " + subscribeUrl);
// you only need to subscribe once.
if(!subscribedSet.contains(subscribeUrl)) {
registry.subscribe(subscribeUrl, new ClusterNotifyListener());
subscribedSet.add(subscribeUrl);
}
urls = registry.discover(subscribeUrl);
if(logger.isDebugEnabled()) logger.debug("discovered urls = " + urls);
}
URL url = loadBalance.select(urls, requestKey);
if(logger.isDebugEnabled()) logger.debug("final url after load balance = " + url);
// construct a url in string
return protocol + "://" + url.getHost() + ":" + url.getPort();
}
static class ClusterNotifyListener implements NotifyListener {
@Override
public void notify(URL registryUrl, List<URL> urls) {
if(logger.isDebugEnabled()) logger.debug("notify is called in ClusterNotifyListener registryUrl = " + registryUrl + " urls = " + urls);
if(urls != null && urls.size() > 0) serviceMap.put(urls.get(0).getPath(), urls);
}
}
}