Annuaire d'articles
prémisse
Nacos prend en charge le déploiement autonome et le déploiement en cluster
- Pour le mode autonome, Nacos communique simplement avec lui-même ;
- Pour le mode cluster, chaque membre Nacos du cluster doit communiquer entre eux.
Cela soulève donc une question, comment gérer les informations des nœuds membres Nacos dans le cluster, et c'est le mécanisme d'adressage interne de Nacos.
conception
Qu'il s'agisse du mode autonome ou du mode cluster, la différence fondamentale est de savoir si le nombre de nœuds membres Nacos est unique ou multiple
-
Il faut pouvoir percevoir le changement du nœud : si le nœud a augmenté ou diminué ;
-
Quelles sont les dernières informations actuelles sur la liste des membres ;
-
Comment gérer les informations de la liste des membres ;
-
Comment prendre en charge rapidement le nouveau et meilleur mode de gestion de la liste des membres, etc.
Recherche de membre
Compte tenu des exigences ci-dessus, une interface MemberLookup est abstraite
package com.alibaba.nacos.core.cluster;
import com.alibaba.nacos.api.exception.NacosException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/**
* Member node addressing mode.
*
* @author <a href="mailto:[email protected]">liaochuntao</a>
*/
public interface MemberLookup {
/**
* start.
*
* @throws NacosException NacosException
*/
void start() throws NacosException;
/**
* is using address server.
*
* @return using address server or not.
*/
boolean useAddressServer();
/**
* Inject the ServerMemberManager property.
*
* @param memberManager {@link ServerMemberManager}
*/
void injectMemberManager(ServerMemberManager memberManager);
/**
* The addressing pattern finds cluster nodes.
*
* @param members {@link Collection}
*/
void afterLookup(Collection<Member> members);
/**
* Addressing mode closed.
*
* @throws NacosException NacosException
*/
void destroy() throws NacosException;
/**
* Some data information about the addressing pattern.
*
* @return {@link Map}
*/
default Map<String, Object> info() {
return Collections.emptyMap();
}
}
-
ServerMemberManager
Il stocke toutes les informations de liste de nœuds membres connues par le nœud, fournit des opérations d'ajout, de suppression, de modification et de requête pour les nœuds membres, et maintient une liste MemberLookup pour faciliter la commutation dynamique des méthodes d'adressage des nœuds membres. -
MemberLookup
L'interface est très simple et il n'y a que deux interfaces principales :injectMemberManager
etafterLookup
, la première est utilisée pourServerMemberManager
injecter dansMemberLookup
afin de faciliter l'utilisationServerMemberManager
des capacités de stockage et de requête, et la secondeafterLookup
est une interface d'événement. Lorsqu'ilMemberLookup
est nécessaire de mettre à jour les informations des nœuds membres , le dernier courant Les informations de la liste des nœuds membres du nœud sont notifiées via cette fonctionServerMemberManager
, et la méthode de gestion de nœud spécifique est masquée dans l'MemberLookup
implémentation spécifique.
implémentation interne
Adressage autonome StandaloneMemberLookup
Le mode d'adressage du mode autonome est très simple, en fait, il s'agit de trouver sa propre information de combinaison IP:PORT, puis de la formater comme une information de nœud, d'appeler afterLookup et de stocker l'information dans ServerMemberManager.
package com.alibaba.nacos.core.cluster.lookup;
import com.alibaba.nacos.core.cluster.AbstractMemberLookup;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.InetUtils;
import java.util.Collections;
/**
* Member node addressing mode in stand-alone mode.
*
* @author <a href="mailto:[email protected]">liaochuntao</a>
*/
public class StandaloneMemberLookup extends AbstractMemberLookup {
@Override
public void doStart() {
String url = InetUtils.getSelfIP() + ":" + EnvUtil.getPort();
afterLookup(MemberUtil.readServerConf(Collections.singletonList(url)));
}
@Override
public boolean useAddressServer() {
return false;
}
}
Adressage de fichier FileConfigMemberLookup
Le mode d'adressage de fichier est l'implémentation d'adressage par défaut en mode cluster Nacos .
Le mode d'adressage des fichiers est très simple, en effet chaque nœud Nacos doit maintenir un fichier appelé cluster.conf.
192.168.16.101:8847
192.168.16.102
192.168.16.103
Par défaut, ce fichier n'a besoin que de remplir les informations IP de chaque nœud membre, et le port sélectionnera automatiquement le port par défaut de Nacos 8848. Si vous avez modifié les informations de port de Nacos pour des besoins particuliers, vous devez ajouter le réseau Informations complètes sur l'adresse de l'itinéraire (IP:PORT).
Lorsque le nœud Nacos démarre, il lit le contenu du fichier, puis analyse l'adresse IP du fichier dans une liste de nœuds et appelle afterLookup pour l'enregistrer dans ServerMemberManager.
/**
* Cluster.conf file managed cluster member node addressing pattern.
*
* @author <a href="mailto:[email protected]">liaochuntao</a>
*/
public class FileConfigMemberLookup extends AbstractMemberLookup {
private static final String DEFAULT_SEARCH_SEQ = "cluster.conf";
private FileWatcher watcher = new FileWatcher() {
@Override
public void onChange(FileChangeEvent event) {
readClusterConfFromDisk();
}
@Override
public boolean interest(String context) {
return StringUtils.contains(context, DEFAULT_SEARCH_SEQ);
}
};
@Override
public void doStart() throws NacosException {
readClusterConfFromDisk();
// Use the inotify mechanism to monitor file changes and automatically
// trigger the reading of cluster.conf
try {
WatchFileCenter.registerWatcher(EnvUtil.getConfPath(), watcher);
} catch (Throwable e) {
Loggers.CLUSTER.error("An exception occurred in the launch file monitor : {}", e.getMessage());
}
}
@Override
public boolean useAddressServer() {
return false;
}
@Override
public void destroy() throws NacosException {
WatchFileCenter.deregisterWatcher(EnvUtil.getConfPath(), watcher);
}
private void readClusterConfFromDisk() {
Collection<Member> tmpMembers = new ArrayList<>();
try {
List<String> tmp = EnvUtil.readClusterConf();
tmpMembers = MemberUtil.readServerConf(tmp);
} catch (Throwable e) {
Loggers.CLUSTER
.error("nacos-XXXX [serverlist] failed to get serverlist from disk!, error : {}", e.getMessage());
}
afterLookup(tmpMembers);
}
}
S'il s'avère que le cluster s'agrandit et se rétrécit, vous devez modifier le fichier cluster.conf sous chaque nœud Nacos, puis le centre de surveillance des modifications de fichier à l'intérieur de Nacos découvrira automatiquement la modification du fichier, relira le contenu du fichier, chargera les informations de la liste IP et mettre à jour le nœud nouvellement ajouté (FileWatcher)
Cependant, ce mode d'adressage par défaut présente un inconvénient : les coûts d'exploitation et de maintenance sont relativement élevés. Comme vous pouvez l'imaginer, lorsque vous ajoutez un nouveau nœud Nacos, vous devez modifier manuellement le fichier cluster.conf sous chaque nœud Nacos. travail, ou un peu plus avancé, utilisez des outils de déploiement automatisés tels que ansible pour pousser le fichier cluster.conf pour remplacer votre propre opération manuelle. Un problème—chaque nœud Nacos a un fichier cluster.conf. Si fichier de l'un des nœuds échoue, cela entraînera une incohérence des données dans la liste des nœuds membres entre les clusters. Par conséquent, un autre mode d'adressage étendu - mode d'adressage du serveur d'adresses
Recherche de serveur d'adresses AddressServerMemberLookup
Le mode d'adressage du serveur d'adresses est une sorte de gestion des informations des nœuds membres du cluster officiellement recommandé par Nacos. Ce mode utilise un simple serveur Web pour gérer les informations de contenu du fichier cluster.conf. De cette manière, le personnel d'exploitation et de maintenance n'a besoin que pour gérer cela, chaque nœud membre Nacos n'a besoin que de demander périodiquement les dernières informations de la liste des nœuds membres du cluster à partir de ce nœud Web.
public class AddressServerMemberLookup extends AbstractMemberLookup {
private final GenericType<String> genericType = new GenericType<String>() {
};
public String domainName;
public String addressPort;
public String addressUrl;
public String envIdUrl;
public String addressServerUrl;
private volatile boolean isAddressServerHealth = true;
private int addressServerFailCount = 0;
private int maxFailCount = 12;
private final NacosRestTemplate restTemplate = HttpClientBeanHolder.getNacosRestTemplate(Loggers.CORE);
private volatile boolean shutdown = false;
private static final String HEALTH_CHECK_FAIL_COUNT_PROPERTY = "maxHealthCheckFailCount";
private static final String DEFAULT_HEALTH_CHECK_FAIL_COUNT = "12";
private static final String DEFAULT_SERVER_DOMAIN = "jmenv.tbsite.net";
private static final String DEFAULT_SERVER_POINT = "8080";
private static final int DEFAULT_SERVER_RETRY_TIME = 5;
private static final long DEFAULT_SYNC_TASK_DELAY_MS = 5_000L;
private static final String ADDRESS_SERVER_DOMAIN_ENV = "address_server_domain";
private static final String ADDRESS_SERVER_DOMAIN_PROPERTY = "address.server.domain";
private static final String ADDRESS_SERVER_PORT_ENV = "address_server_port";
private static final String ADDRESS_SERVER_PORT_PROPERTY = "address.server.port";
private static final String ADDRESS_SERVER_URL_ENV = "address_server_url";
private static final String ADDRESS_SERVER_URL_PROPERTY = "address.server.url";
private static final String ADDRESS_SERVER_RETRY_PROPERTY = "nacos.core.address-server.retry";
@Override
public void doStart() throws NacosException {
this.maxFailCount = Integer.parseInt(EnvUtil.getProperty(HEALTH_CHECK_FAIL_COUNT_PROPERTY, DEFAULT_HEALTH_CHECK_FAIL_COUNT));
initAddressSys();
run();
}
@Override
public boolean useAddressServer() {
return true;
}
private void initAddressSys() {
String envDomainName = System.getenv(ADDRESS_SERVER_DOMAIN_ENV);
if (StringUtils.isBlank(envDomainName)) {
domainName = EnvUtil.getProperty(ADDRESS_SERVER_DOMAIN_PROPERTY, DEFAULT_SERVER_DOMAIN);
} else {
domainName = envDomainName;
}
String envAddressPort = System.getenv(ADDRESS_SERVER_PORT_ENV);
if (StringUtils.isBlank(envAddressPort)) {
addressPort = EnvUtil.getProperty(ADDRESS_SERVER_PORT_PROPERTY, DEFAULT_SERVER_POINT);
} else {
addressPort = envAddressPort;
}
String envAddressUrl = System.getenv(ADDRESS_SERVER_URL_ENV);
if (StringUtils.isBlank(envAddressUrl)) {
addressUrl = EnvUtil.getProperty(ADDRESS_SERVER_URL_PROPERTY, EnvUtil.getContextPath() + "/" + "serverlist");
} else {
addressUrl = envAddressUrl;
}
addressServerUrl = "http://" + domainName + ":" + addressPort + addressUrl;
envIdUrl = "http://" + domainName + ":" + addressPort + "/env";
Loggers.CORE.info("ServerListService address-server port:" + addressPort);
Loggers.CORE.info("ADDRESS_SERVER_URL:" + addressServerUrl);
}
@SuppressWarnings("PMD.UndefineMagicConstantRule")
private void run() throws NacosException {
// With the address server, you need to perform a synchronous member node pull at startup
// Repeat three times, successfully jump out
boolean success = false;
Throwable ex = null;
int maxRetry = EnvUtil.getProperty(ADDRESS_SERVER_RETRY_PROPERTY, Integer.class, DEFAULT_SERVER_RETRY_TIME);
for (int i = 0; i < maxRetry; i++) {
try {
syncFromAddressUrl();
success = true;
break;
} catch (Throwable e) {
ex = e;
Loggers.CLUSTER.error("[serverlist] exception, error : {}", ExceptionUtil.getAllExceptionMsg(ex));
}
}
if (!success) {
throw new NacosException(NacosException.SERVER_ERROR, ex);
}
GlobalExecutor.scheduleByCommon(new AddressServerSyncTask(), DEFAULT_SYNC_TASK_DELAY_MS);
}
@Override
public void destroy() throws NacosException {
shutdown = true;
}
@Override
public Map<String, Object> info() {
Map<String, Object> info = new HashMap<>(4);
info.put("addressServerHealth", isAddressServerHealth);
info.put("addressServerUrl", addressServerUrl);
info.put("envIdUrl", envIdUrl);
info.put("addressServerFailCount", addressServerFailCount);
return info;
}
private void syncFromAddressUrl() throws Exception {
RestResult<String> result = restTemplate
.get(addressServerUrl, Header.EMPTY, Query.EMPTY, genericType.getType());
if (result.ok()) {
isAddressServerHealth = true;
Reader reader = new StringReader(result.getData());
try {
afterLookup(MemberUtil.readServerConf(EnvUtil.analyzeClusterConf(reader)));
} catch (Throwable e) {
Loggers.CLUSTER.error("[serverlist] exception for analyzeClusterConf, error : {}",
ExceptionUtil.getAllExceptionMsg(e));
}
addressServerFailCount = 0;
} else {
addressServerFailCount++;
if (addressServerFailCount >= maxFailCount) {
isAddressServerHealth = false;
}
Loggers.CLUSTER.error("[serverlist] failed to get serverlist, error code {}", result.getCode());
}
}
class AddressServerSyncTask implements Runnable {
@Override
public void run() {
if (shutdown) {
return;
}
try {
syncFromAddressUrl();
} catch (Throwable ex) {
addressServerFailCount++;
if (addressServerFailCount >= maxFailCount) {
isAddressServerHealth = false;
}
Loggers.CLUSTER.error("[serverlist] exception, error : {}", ExceptionUtil.getAllExceptionMsg(ex));
} finally {
GlobalExecutor.scheduleByCommon(this, DEFAULT_SYNC_TASK_DELAY_MS);
}
}
}
}
Par conséquent, le mode serveur d'adresses simplifie considérablement le coût de la gestion des nœuds de cluster Nacos.Dans le même temps, le serveur d'adresses est un programme Web très simple et la stabilité de son programme peut être bien garantie.
futur point d'expansion
Expansion et contraction automatiques des nœuds de cluster
À l'heure actuelle, la gestion des nœuds de cluster dans Nacos est encore une opération manuelle. Par conséquent, à l'avenir, il est prévu de réaliser la fonction de gestion automatique des nœuds de cluster en fonction du mode d'adressage. Lorsqu'un nouveau nœud est mis en ligne, un seul des nœuds de cluster d'origine doivent être connus. Avec une seule information de nœud, vous pouvez rejoindre avec succès le cluster Nacos d'origine dans un certain laps de temps ; en même temps, vous pouvez également découvrir par vous-même les nœuds qui ne survivent pas et supprimer automatiquement à partir de la liste des nœuds disponibles dans le cluster. L'implémentation logique de cette partie est en fait similaire au protocole Consul's Gossip.