In Depth with Alibaba’s Nacos: Exploring Registry Implementation in Dubbo

A technical guide to implementing Nacos’ registry function in Dubbo

Image for post
Image for post

This article is part of the Alibaba Open Source series.

Nacos, Alibaba’s open source naming configuration service, offers users an easy approach to service discovery, management, and configuration in cloud-native applications. To use Nacos, the dubbo-registry-nacos package must first be implemented as an integrated registry in the Dubbo ecosystem.

In this article, technical audiences can explore step-by-step instructions for implementing Nacos’s registry function in Dubbo.

Preparatory Work

Before integrating dubbo-registry-nacos into a Dubbo project, users should make sure that the Nacos service has started in the background. If you are not familiar with the basic use of Nacos, you can refer to the Nacos quick start; using Nacos 0.6.1 or later versions is recommended.

Quick Start

Integrating Nacos into a Dubbo registry involves very simple steps, which can be divided into adding Maven dependencies and configuring the registry.

First, the Maven dependencies of dubbo-registry-nacos must be added to the project’s pom.xml file, for which using Dubbo 2.6.5 is strongly recommended, as shown in the following XML:

...

<!-- Dubbo Nacos registry dependency -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>0.0.2</version>
</dependency>
<!-- Keep latest Nacos client version -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>[0.6.1,)</version>
</dependency>
<!-- Dubbo dependency -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.5</version>
</dependency>
<!-- Alibaba Spring Context extension -->
<dependency>
<groupId>com.alibaba.spring</groupId>
<artifactId>spring-context-support</artifactId>
<version>1.0.2</version>
</dependency>
...

After adding dubbo-registry-nacos to the project, there is no need to explicitly program to implement service discovery and registration logic, as the actual implementation is provided by a tripartite package. Following this, users can configure the Nacos registry.

Assuming that your Dubbo app uses the Spring framework for assembly, there are two configuration options available: Dubbo Spring externalization configuration, and Spring XML configuration file, of which the former is highly recommended.

The Dubbo Spring externalization configuration is a new feature introduced in Dubbo 2.5.8 that automatically generates and binds Dubbo configuration beans through Spring Environment properties, simplifying configuration and lowering the threshold for microservice development.

Assume at this point that your Dubbo app uses Zookeeper as the registry and its server IP address is 10.20.153.10. Meanwhile, the registered address is stored in the dubbo-config.properties file as Dubbo externalization configuration properties, shown as follows (in plain text):

application

dubbo.application.name = your-dubbo-application

Zookeeper registry address

Dubbo.registry.address = zookeeper://10.20.153.10:2181

Assuming that your Nacos Server is also running on server 10.20.153.10 and using the default Nacos service port 8848, you only need to adjust the dubbo.registry.address property as follows (in plain text):

Other properties remain the same

Nacos registry address

dubbo.registry.address = nacos://10.20.153.10:8848

Next, users should restart their Dubbo apps; Dubbo’s service offerings and consumption information can be displayed in the Nacos console:

Image for post
Image for post

As shown in the figure, the information of which the service name is preceded by “providers:” is the meta information for the service provider, while those preceded by “consumer:” are meta information for the service consumer. Clicking on “Details” shows the service status details:

Image for post
Image for post

Alternately, if you are using the Spring XML configuration file to assemble the Dubbo registry, you can instead refer to the steps in the following section.

For the following, again assume that your Dubbo app uses Zookeeper as the registry and its server IP address is 10.20.153.10, and the Spring Bean is assembled in the XML file as follows:

<!-- Provider application information for calculating dependencies -->
<dubbo:application name="dubbo-provider-xml-demo" />
<!-- Use the Zookeeper Registry -->
<dubbo:registry address="zookeeper://10.20.153.10:2181" />
...

Similarly to the Dubbo Spring externalization configuration, you only need to adjust the address attribute configuration as shown in the following XML file:

<!-- Provider application information for calculating dependencies -->
<dubbo:application name="dubbo-provider-xml-demo" />
<!-- Use the Nacos Registry -->
<dubbo:registry address="nacos://10.20.153.10:8848" />
...

After restarting the Dubbo app, you will also find that the registration meta-information of the service provider and the consumer is presented in the Nacos console:

Image for post
Image for post

As shown above, configuring or switching the Nacos registry is extremely easy. For a more in-depth understanding, you can refer to the complete example as follows.

Complete Implementation Explained

The metadata in the above figure is derived from the Dubbo Spring annotation driver example and the Dubbo Spring XML configuration driver example, which are described in detail in the following sections. For this example, users can choose their preferred programming model. Before formal discussion, some mention is needed of the applicable preparatory work, as both rely on Java service interfaces and implementations. Meanwhile, users should make sure that the Nacos service has been started in the local (127.0.0.1) environment.

First, the sample interface should be defined as in the following Java:

package com.alibaba.dubbo.demo.service;

/**

DemoService

@since 2.6.5
*/
public interface DemoService {

String sayName(String name);

}

Next, provide the implementation class for the above interface as in the following Java:

package com.alibaba.dubbo.demo.service;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.rpc.RpcContext;

import org.springframework.beans.factory.annotation.Value;

/**

Default {@link DemoService}

@since 2.6.5
*/
@Service(version = “${demo.service.version}”)
public class DefaultService implements DemoService {

@Value(“${demo.service.name}”)
private String serviceName;

public String sayName(String name) {
RpcContextrpcContext = RpcContext.getContext();
return String.format(“Service [name :%s , port : %d] %s(“%s”) : Hello,%s”,
serviceName,
rpcContext.getLocalPort(),
rpcContext.getMethodName(),
name,
name);
}
}

Once the interface and implementation are ready, they will be implemented using the annotation driver and the XML configuration driver, respectively.

Dubbo 2.5.7 has refactored the Spring annotation-driven programming model.

The service provider’s annotation driver implementation is as follows:

First, define the source of Dubbo provider externalization configuration properties (provider-config.properties) as shown in the following plain text:

application

dubbo.application.name = dubbo-provider-demo

Nacos registry address

dubbo.registry.address = nacos://127.0.0.1:8848

Dubbo Protocol

dubbo.protocol.name = dubbo
dubbo.protocol.port = -1

Provider @Service version

demo.service.version=1.0.0
demo.service.name = demoService

Next, implement the service provider bootstrap class (DemoServiceProviderBootstrap) as shown in the following Java:

package com.alibaba.dubbo.demo.provider;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import com.alibaba.dubbo.demo.service.DemoService;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
importorg.springframework.context.annotation.PropertySource;

import java.io.IOException;

/**

{@link DemoService} provider demo
*/
@EnableDubbo(scanBasePackages = “com.alibaba.dubbo.demo.service”)
@PropertySource(value = “classpath:/provider-config.properties”)
public class DemoServiceProviderBootstrap {

Public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(DemoServiceProviderBootstrap.class);
context.refresh();
System.out.println(“DemoService provider is starting…”);
System.in.read();
}
}

The annotation @EnableDubbo activates the Dubbo annotation driver and the externalization configuration. The scanBasePackages property scans the specified Java package, exposes all service interface implementation classes marked @Service as Spring Bean, and then exports the Dubbo service.

@PropertySource is a standard import property configuration resource annotation introduced by Spring Framework 3.1 that will provide an externalization configuration for Dubbo.

For this implementation, define the source of the Dubbo consumer externalization configuration property (consumer-config.properties) as shown in the following plain text:

Dubbo Application info

dubbo.application.name = dubbo-consumer-demo

Nacos registry address

dubbo.registry.address = nacos://127.0.0.1:8848

@Reference version

demo.service.version= 1.0.0

Similarly, the dubbo.registry.address attribute points to the Nacos registry, and other meta-information related to the Dubbo service is obtained through the Nacos registry.

Next, implement the service consumer boot class (DemoServiceConsumerBootstrap) as shown in the following Java:

package com.alibaba.dubbo.demo.consumer;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import com.alibaba.dubbo.demo.service.DemoService;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
importorg.springframework.context.annotation.PropertySource;

import javax.annotation.PostConstruct;
import java.io.IOException;

/**

{@link DemoService} consumer demo
*/
@EnableDubbo
@PropertySource(value = “classpath:/consumer-config.properties”)
public class DemoServiceConsumerBootstrap {

@Reference(version = “${demo.service.version}”)
private DemoServicedemoService;

@PostConstruct
public void init() {
for (int i = 0; i< 10; i++) {
System.out.println(demoService.sayName(“Bro Ma(mercyblitz)”));
}
}

public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(DemoServiceConsumerBootstrap.class);
context.refresh();
context.close();
}
}

Similarly, the @EnableDubbo annotation activates the Dubbo annotation driver and externalization configuration, but is currently a service consumer and so does not need to specify the Java package name to scan the service implementation of the @Service.

@Reference is a dependency injection annotation for Dubbo Remote Services that requires the service provider and consumer to agree on the interface, version, and group information. In the current service consumption example, the service version of DemoService is derived from the property configuration file consumer-config.properties.

Part of the @PostConstruct code shows that the Dubbo remote method call is executed ten times when the DemoServiceConsumerBootstrap Bean is initialized.

When you start DemoServiceProviderBootstrap twice locally, two health services will appear in the registry:

Image for post
Image for post

Running DemoServiceConsumerBootstrap again will generate the following result (shown in plain text):

Service [name :demoService , port : 20880] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20881] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20880] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20880] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20881] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20881] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20880] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20880] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20881] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :demoService , port : 20881] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)

The execution is correct, and the service consumer uses a load balancing strategy to evenly distribute ten RPC calls to two Dubbo service provider instances.

The Spring XML configuration driver is the programming model for traditional Spring assembly components.

Users should define the service provider XML context configuration file — /META-INF/spring/dubbo-provider-context.xml, as shown in the following XML:

<!-- Provider application information for calculating dependencies -->
<dubbo:application name="dubbo-provider-xml-demo"/>
<!-- Use the Nacos registry -->
<dubbo:registry address="nacos://127.0.0.1:8848"/>
<!-- Exposing services on random ports using the dubbo protocol -->
<dubbo:protocol name="dubbo" port="-1"/>
<!-- Declare the service interface that needs to be exposed -->
<dubbo:service interface="com.alibaba.dubbo.demo.service.DemoService" ref="demoService" version="2.0.0"/>
<!-- Implement the same service as a local bean -->
<bean id="demoService" class="com.alibaba.dubbo.demo.service.DefaultService"/>

Next, Implement the service provider bootstrap class (DemoServiceProviderXmlBootstrap) as shown in the following XML:

package com.alibaba.dubbo.demo.provider;

import com.alibaba.dubbo.demo.service.DemoService;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

/**

{@link DemoService} provider demo XML bootstrap
*/
public class DemoServiceProviderXmlBootstrap {

public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
context.setConfigLocation(“/META-INF/spring/dubbo-provider-context.xml”);
context.refresh();
System.out.println(“DemoService provider (XML) is starting…”);
System.in.read();
}
}

Define the service consumer XML context configuration file (/META-INF/spring/dubbo-consumer-context.xml) as shown in the following XML:

<!-- Provider application information for calculating dependencies -->
<dubbo:application name="dubbo-consumer-xml-demo"/>
<!-- Use the Nacos registry -->
<dubbo:registry address="nacos://127.0.0.1:8848"/>
<!-- Refer service interfaces -->
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.service.DemoService" version="2.0.0"/>

Next, implement the service consumer bootstrap class (DemoServiceConsumerXmlBootstrap) as shown in the following Java:

package com.alibaba.dubbo.demo.consumer;

import com.alibaba.dubbo.demo.service.DemoService;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

/**

{@link DemoService} consumer demo XML bootstrap
*/
public class DemoServiceConsumerXmlBootstrap {

public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
context.setConfigLocation(“/META-INF/spring/dubbo-consumer-context.xml”);
context.refresh();
System.out.println(“DemoService consumer (XML) is starting…”);
DemoServicedemoService = context.getBean(“demoService”, DemoService.class);
for (int i = 0; i< 10; i++) {
System.out.println(demoService.sayName(“Bro Ma(mercyblitz)”));
}
context.close();
}
}

In a similar manner to the above, start the two DemoServiceProviderXmlBootstrap bootstrap classes and observe changes to the Nacos registry service provider:

Image for post
Image for post

The service version of the XML configuration driver is 2.0.0, so the registration service is correct.

Next, run the service consumer bootstrap class DemoServiceConsumerXmlBootstrap and observe the console output, as shown in the following plain text:

Service [name :null , port : 20882] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20882] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20883] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20882] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20882] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20883] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20882] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20883] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20883] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)
Service [name :null , port : 20883] sayName(“Bro Ma(mercyblitz)”) : Hello,Bro Ma(mercyblitz)

Likewise, it turns out that the running and load balancing is normal; however, since the current example has not added the attribute demo.service.name, the “name” information is output as null. For more information, you can refer to resources available on Github.

Users interested in Dubbo, Nacos, and other open source projects are welcome to learn more and add a star at the following Github addresses:

Apache Dubbo:https://github.com/apache/incubator-dubbo
Dubbo Nacos Registry:https://github.com/dubbo/dubbo-registry-nacos
Alibaba Nacos:https://github.com/alibaba/nacos

Alibaba Tech

First hand and in-depth information about Alibaba’s latest technology → Facebook: “Alibaba Tech”. Twitter: “AlibabaTech”.

Written by

First-hand & in-depth information about Alibaba's tech innovation in Artificial Intelligence, Big Data & Computer Engineering. Follow us on Facebook!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store