Caching mechanisms
Caching is a mechanism that trades space for time, and if you have to get a list of services that can be called from a remote registry every time, you put a lot of network and traffic pressure on the registry. Therefore, Dubbo’s registry implements a generic caching mechanism, implemented in the Abstract AbstractRegistry. The AbstractRegistry class structure is shown below
Consumers or service governance center will do local cache after obtaining registration information. There will be a copy in memory, stored in the Properties object, and a persistent file on disk, referenced by the File object. The AbstractRegistry abstract class has the following definition.
// Local disk cache, where the special key value.registries records the list of registry centers, and the others are the list of notified service providers
private final Properties properties = new Properties();
private URL registryUrl;
// Local disk cache file
private File file;
private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<>();
private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<>();
Copy the code
The cache notified in memory is a ConcurrentHashMap. A Map is nested in it, and the key of an outer Map is a consumer URL and the key of an inner Map is a category. It can be providers, consumers, routes, and Configurators. Value is the corresponding list of services, starting with the special empty:// prefix for urls without service providers.
Cache loading
At service initialization, the AbstractRegistry constructor reads the persistent registration data from the local disk file into Properties and loads it into memory.
private void loadProperties(a) {
if(file ! =null && file.exists()) {
InputStream in = null;
try {
in = new FileInputStream(file);
properties.load(in);
if (logger.isInfoEnabled()) {
logger.info("Load registry cache file " + file + ", data: "+ properties); }}catch (Throwable e) {
logger.warn("Failed to load registry cache file " + file, e);
} finally {
if(in ! =null) {
try {
in.close();
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
}
}
}
Copy the code
Properties holds the urls of all service providers, using URL#serviceKey() as the key, provider list, routing rule list, configuration rule list, and so on as value. Because values are lists, use Spaces to separate multiple values. There is also a special key.registies that holds all registry addresses, and if the registry is disconnected or down during startup, the Dubbo framework automatically loads Invokers from the local cache.
Save and update the cache
Caches can be saved synchronously or asynchronously. Asynchrony saves asynchronously using the thread pool, and if an exception occurs during execution, the thread pool is called again for continuous retry
private void saveProperties(URL url) {
if (file == null) {
return;
}
try {
StringBuilder buf = new StringBuilder();
Map<String, List<URL>> categoryNotified = notified.get(url);
if(categoryNotified ! =null) {
for (List<URL> us : categoryNotified.values()) {
for (URL u : us) {
if (buf.length() > 0) {
buf.append(URL_SEPARATOR);
}
buf.append(u.toFullString());
}
}
}
properties.setProperty(url.getServiceKey(), buf.toString());
long version = lastCacheChanged.incrementAndGet();
if (syncSaveFile) {
doSaveProperties(version);
} else {
registryCacheExecutor.execute(newSaveProperties(version)); }}catch(Throwable t) { logger.warn(t.getMessage(), t); }}Copy the code
The abstractregistring #notify method encapsulates the logic for updating the cache and files. This method is called when the client first subscribes to obtain the full amount of data, or when the client subsequently acquires new data due to the subscription
protected void notify(List<URL> urls) {
if (CollectionUtils.isEmpty(urls)) {
return;
}
for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
URL url = entry.getKey();
if(! UrlUtils.isMatch(url, urls.get(0))) {
continue;
}
Set<NotifyListener> listeners = entry.getValue();
if(listeners ! =null) {
for (NotifyListener listener : listeners) {
try {
notify(url, listener, filterEmpty(url, urls));
} catch (Throwable t) {
logger.error("Failed to notify registry event, urls: " + urls + ", cause: " + t.getMessage(), t);
}
}
}
}
}
Copy the code