In Nacos source code, we mainly read three parts:
- The service registry
- Obtaining the service address
- Perception of service address changes
Let’s analyze how Nacos is implemented based on these three aspects.
When will Spring Cloud complete service registration
The principle of Nacos service registration is analyzed from source code level
Nacos provides an SDK and an Open API to implement service registration. We have used the Open API to register a service address, where serviceName indicates the serviceName and IP /port indicates the address of the service.
For service registration, the externally provided service interface requests the address nacos/v1/ns/instance, and the implementation code is in the InstanceController class of the Nacos-Naming module.
- Get the serviceName(serviceName) and namespaceId (namespaceId) from the request parameters.
- Call registerInstance to register the instance
`
/**
* Register new instance.
*
* @param request http request
* @return 'ok' if success
* @throws Exception any error during register
*/
@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
final Instance instance = HttpRequestInstanceBuilder.newBuilder()
.setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();
getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
return "ok";
}
Copy the code
In this example, the serviceName is actually spring-Cloud-nacos-sample, and the namespaceId is public. Next we’ll focus on the registerInstance method, whose main logic is:
- Creating an empty service (the service information shown in the “List of Services” section of the Nacos console) actually initializes a serviceMap, which is a ConcurrentHashMap collection.
- GetService gets a service object from the serviceMap based on the namespaceId and serviceName.
- Call addInstance to add a service instance
/** * Register an instance to a service in AP mode. * * <p>This method creates service or cluster silently if they don't exist. * * @param namespaceId id of namespace * @param serviceName service name * @param instance instance to register * @throws Exception any error occurred in the process */ public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException { createEmptyService(namespaceId, serviceName, instance.isEphemeral()); Service service = getService(namespaceId, serviceName); checkServiceIsNull(service, namespaceId, serviceName); addInstance(namespaceId, serviceName, instance.isEphemeral(), instance); }Copy the code
CreateEmptyService creates an empty service
- Fetch service instance from cache based on namespaceId and serviceName
- If the service instance is empty, it is created and saved to the cache
public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
createServiceIfAbsent(namespaceId, serviceName, local, null);
}
Copy the code
/**
* Create service if not exist.
*
* @param namespaceId namespace
* @param serviceName service name
* @param local whether create service by local
* @param cluster cluster
* @throws NacosException nacos exception
*/
public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
throws NacosException {
Service service = getService(namespaceId, serviceName);
if (service == null) {
Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
service = new Service();
service.setName(serviceName);
service.setNamespaceId(namespaceId);
service.setGroupName(NamingUtils.getGroupName(serviceName));
// now validate the service. if failed, exception will be thrown
service.setLastModifiedMillis(System.currentTimeMillis());
service.recalculateChecksum();
if (cluster != null) {
cluster.setService(service);
service.getClusterMap().put(cluster.getName(), cluster);
}
service.validate();
putServiceAndInit(service);
if (!local) {
addOrReplaceService(service);
}
}
}
Copy the code
I don’t have too much logic, but focus on the putServiceAndInit method, which does the following:
- The service is cached into memory through the putService method.
- Service.init () establishes the heartbeat detection mechanism.
- ConsistencyService. Listent (realized) monitoring data consistency.
private void putServiceAndInit(Service service) throws NacosException {
putService(service);
service = getService(service.getNamespaceId(), service.getName());
service.init();
consistencyService
.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
consistencyService
.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
}
Copy the code
The code for the service.init() method is omitted, as shown in the figure, which continuously detects the last heartbeat packet sent by all instances of the current service through a scheduled task. If timeout occurs, set healthy to false to indicate that the service is unhealthy and send service change events. Here, please consider a question: who triggers the last heartbeat packet update time of the service instance? The Nacos client registers the service and establishes the heartbeat mechanism.
Heartbeat detection mechanism diagram
Let’s look at the putService method, which saves the Service to the serviceMap.
/** * Put service into manager. * * @param service service */ public void putService(Service service) { if (! serviceMap.containsKey(service.getNamespaceId())) { serviceMap.putIfAbsent(service.getNamespaceId(), new ConcurrentSkipListMap<>()); } serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service); }Copy the code
After the above steps are complete, continue to call the addInstance method to save the currently registered Service instance to the Service.
addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
At this point, the registration of the service is almost complete. Finally, a brief summary of the complete service registration process:
- The Nacos client sends service registration requests through the Open API
When the Nacos server receives the request, it does three things:
- Build a Service object and store it in the ConcurrentHashMap collection.
- This section describes how to set up a heartbeat detection mechanism for all instances of the current service using scheduled tasks.
- Service data is synchronized based on data consistency protocols.
Analysis Nacos service address dynamic sensing principle
The service consumer not only needs to get a list of service providers’ addresses, but also needs to listen for changes in the service address when an exception occurs to the service instance