Learning is not so utilitarian, two brothers with you from a higher dimension of easy to read source ~

This article takes you through the source level analysis of Nacos Client service discovery journey, the truth may not be as simple as you think.

Nacos service discovery

Intuitively, the Nacos client’s service discovery is to encapsulate parameters, invoke the server interface, and obtain a list of returned instances.

However, if you detail this process, you will find that it not only involves obtaining the list of services through NamingService, but also involves the communication protocol (Http or gRPC), subscription process, failover logic, and so on. Let’s take a look at the process in terms of service discovery.

NamingTest = NamingTest = NamingTest

NamingService namingService = NacosFactory.createNamingService(properties); namingService.registerInstance("nacos.test.1", instance); ThreadUtils.sleep(5000L); / / for Instance List < Instance > List = namingService. GetAllInstances (nacos. Test. "1");Copy the code

NamingService instantiation and basic functions have been discussed in the service registration, here directly look at the getAllInstances to get the list of instances. The parameter to this method is the name of the service.

After a series of overloaded method calls, the actual way to handle the core logic is as follows:

@Override public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException { ServiceInfo serviceInfo; String clusterString = StringUtils.join(clusters, ","); / / whether the subscription model the if (subscribe) {/ / service information was obtained from the client cache first serviceInfo. = serviceInfoHolder getServiceInfo (serviceName, groupName. clusterString); If (null == serviceInfo) {// If the local cache does not have service information, ServiceInfo = clientProxy. Subscribe (serviceName, groupName, clusterString); serviceInfo = clientProxy. }} else {// If the service information is not subscribed, Directly from the server to query serviceInfo. = clientProxy queryInstancesOfService (serviceName, groupName, clusterString, 0, false); } List<Instance> List; if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { return new ArrayList<Instance>(); } return list; }Copy the code

First, look at the overloaded getAllInstances method, which has a few more parameters than the entry method. It has not only the service name, but also the groupName, the clusters, and subscribe.

Default values are already set for other parameters in the overloaded method. For example, the group name defaults to “DEFAULT_GROUP,” the cluster list defaults to an empty array, and whether to subscribe defaults to “Subscribe.”

The above methods are arranged as a flow chart as follows:

The basic logic of the above process is:

In subscription mode, the service information (ServiceInfo) is fetched directly from the local cache and then the list of instances is fetched from it, because the subscription mechanism automatically synchronizes changes to the server instance locally. If it is not in the local cache, it is the first time to call, then subscribe, after the subscription is completed, the service information will be obtained.

If you’re not in a subscription mode, ask the server for the service information directly.

Subscription processing process

In the above process, the subscription logic is involved, and the entry code is to get the following method in the instance list:

serviceInfo = clientProxy.subscribe(serviceName, groupName, clusterString);
Copy the code

Let’s take a look at how the method is handled internally. First of all, the clientProxy here is an object of the NamingClientProxy class. The corresponding subscribe implementation is as follows:

@Override public ServiceInfo subscribe(String serviceName, String groupName, String clusters) throws NacosException { String serviceNameWithGroup = NamingUtils.getGroupedName(serviceName, groupName); String serviceKey = ServiceInfo.getKey(serviceNameWithGroup, clusters); / / get in the cache ServiceInfo ServiceInfo result = serviceInfoHolder. GetServiceInfoMap () get (the serviceKey); If (null == result) {if (null == result) { Based on the result = grpcClientProxy gRPC agreement. The subscribe (serviceName, groupName, clusters); } / / timing scheduling UpdateTask serviceInfoUpdateService. ScheduleUpdateIfAbsent (serviceName, groupName, clusters); . / / ServiceInfo local cache handling serviceInfoHolder processServiceInfo (result); return result; }Copy the code

In the above code, you can see that the subscription logic is also extended when the list of service instances is obtained (especially for the first time). The basic flowchart is as follows:

As can be seen from the process above, the subscription method first determines the local cache by proxy class. If ServiceInfo information exists in the local cache, it will be returned directly. If no, the gRPC protocol is used for subscription by default and ServiceInfo is returned.

The subscribe method of grpcClientProxy is to directly send a subscribe request to the server and return the result, so there is no too much processing.

After the subscription is complete, a scheduled task is started through ServiceInfoUpdateService. The scheduled task is used to periodically synchronize the instance list information on the server and update the local cache.

Last step, ServiceInfo local cache processing. Compare the latest ServiceInfo with ServiceInfo in local memory, update, publish change time, disk file storage, and so on. In fact, this step is also handled in the subscription scheduled task.

The subscription details and local cache handling are covered in more detail, which we will expand on separately later. Just know the whole process here.

summary

This paper mainly summarizes the core process of Nacos client service discovery, including:

First, if the subscription mode is not enabled, the service instance list is directly obtained through the /instance/list interface (gRPC protocol by default).

Second, if the subscription mode is enabled (by default), the instance information is obtained from the local cache. If the instance information does not exist, the instance information is subscribed and obtained.

Third, when subscriptions are started, scheduled tasks are enabled to execute UpdateTask (obtain server instance information, update the local cache, and publish events) on a scheduled basis.

Fourth, after the latest instance information is obtained in step 2, the processServiceInfo method is also executed to update the memory and local instance cache and publish the change time.

Fifth, so far, form a loop with the second step, each time get the local cache, do not exist update……

We’ll continue with the UpdateTask method used to handle subscriptions and the ServiceInfoHolder#processServiceInfo method used to handle the local cache.

Author of SpringBoot Tech Insider, loves to delve into technology and write about it.

Official account: “Program New Horizon”, the official account of the blogger, welcome to follow ~

Technical exchange: please contact the blogger wechat id: Zhuan2quan