Developing Dubbo Applications Using Native APIs

As an RPC framework, Dubbo defines a comprehensive set of API interfaces, allowing us to develop RPC services and microservice applications based on the native API.

You may have noticed that most of the functions and examples in the documentation are based on the Spring Boot model, but Spring Boot or Spring is merely one application or microservice development model adapted by Dubbo. As an RPC framework, Dubbo defines a comprehensive set of API interfaces, enabling us to develop Dubbo applications based on the native APIs. Business scenarios that can be achieved with pure APIs include:

  • Lightweight RPC Server & Client, typically used for simple remote call scenarios within applications, foundational components, middleware, etc.
  • Microservices Applications, without relying on Spring, directly using APIs to develop microservices.

API Overview

public class Application {
    public static void main(String[] args) {
        DubboBootstrap.getInstance()
            .protocol(new ProtocolConfig(CommonConstants.TRIPLE, 50051))
            .service(ServiceBuilder.newBuilder().interfaceClass(DemoService.class).ref(new DemoServiceImpl()).build())
            .start()
            .await();
    }
}

The above is a code example of starting a Dubbo RPC Server. The DubboBootstrap instance represents a Dubbo application and serves as the entry point for starting the entire Dubbo application. Based on DubboBootstrap, we can set protocol, service, registry, metrics, etc., to register services, connect to the registry, etc., similar to adjusting application.yml or application.properties files in Spring Boot.

The official recommendation is to use DubboBootstrap.start() as the centralized entry for application startup, but to facilitate the separate publication of some services after the process starts, the Dubbo framework also allows directly calling ServiceConfig.export() or ReferenceConfig.refer() methods to publish individual services. In this case, Service/Reference will register with the default DubboBootstrap instance, similar to calling DubboBootstrap.service(...).start().

Below are some components commonly used in development. For a complete component definition and detailed parameter description, please refer to the Reference Manual - Configuration Items Manual:

API ComponentGlobally UniqueCore Methods or PropertiesDescription
DubboBootstrapYes (except for multi-application scenarios)start(), stop()The DubboBootstrap instance represents a Dubbo application and serves as the entry point for starting the entire Dubbo application.
ApplicationConfigYesnameApplication name and some global configurations at the application level
MetricsConfigYesprotocol, prometheus, tracingConfigurations related to Metrics and tracing collection
ProtocolConfigNo. Services in multi-protocol scenarios are associated by idid, name, port, serialization, threadpoolRPC protocol port, serialization protocol, runtime behavior configuration
RegistrtyConfigNo. Services in multi-registry scenarios are associated by idid, address, protocol, groupRegistry implementation, address, subscription, etc. configurations
ConfigCenterConfigNo. Services in multi-configuration center scenarios are associated by idid, address, protocol, group, namespaceConfiguration center implementation, address, group isolation, etc. configurations
MetadataReportConfigNo. Services in multi-metadata center scenarios are associated by idid, address, protocol, group, namespaceMetadata center implementation, address, group isolation, etc. configurations
ProviderConfigNoReference ServiceConfigDefault values for multiple ServiceConfig
ConsumerConfigNoReference ReferenceConfigDefault values for multiple ReferenceConfig
ServiceConfigNo- Method: export()
- Properties: interfaceClass, ref, group, version, timeout, retry
A ServiceConfig instance represents an RPC service
ReferenceConfigNo- Method: refer()
- Properties: interfaceClass, group, version, timeout, retry, cluster, loadbalance
A ReferenceConfig instance represents an RPC service
MethodConfigNoname, oninvoke, onreturn, onthrowMethod-level configuration embedded in ServiceConfig/ReferenceConfig
ArgumentConfigNoindex, type, callbackParameter-level configuration embedded in MethodConfig

Lightweight RPC Example

This example demonstrates how to use the lightweight Dubbo SDK to develop RPC Server and Client. The example defines, publishes, and accesses RPC services using Java Interface, with Triple protocol communications. For complete code, please see dubbo-samples.

Based on the Triple protocol defined by Dubbo, you can easily write browser and gRPC compatible RPC services and run them simultaneously on HTTP/1 and HTTP/2. The Dubbo Java SDK supports defining services using IDL or language-specific methods and provides a lightweight API for publishing or calling these services.

Maven Dependency

Before coding with Dubbo RPC, you only need to add a lightweight dubbo dependency package to your project. For Maven, it looks like this:

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>3.3.0</version>
</dependency>

Define Service

Define a standard Java interface named DemoService as a Dubbo service (Dubbo also supports IDL-based service definition patterns).

public interface DemoService {
   String sayHello(String name);
}

Implement the DemoService interface and write the business logic code.

public class DemoServiceImpl implements DemoService {
    @Override
    public String sayHello(String name) {
        return "Hello " + name + ", response from provider.";
    }
}

Register Service and Start Server

Start the server and listen for RPC requests on the specified port. Before this, we registered the following information with the server:

  • Using Triple as the communication RPC protocol and listening on port 50051
  • Registering the Dubbo service to DemoService server
public class Application {
    public static void main(String[] args) {
        DubboBootstrap.getInstance()
            .protocol(new ProtocolConfig(CommonConstants.TRIPLE, 50051))
            .service(ServiceBuilder.newBuilder().interfaceClass(DemoService.class).ref(new DemoServiceImpl()).build())
            .start()
            .await();
    }
}

Access Service

The simplest way is to use an HTTP/1.1 POST request to access the service, with parameters passed as standard JSON format in the HTTP payload. Here is an example using the cURL command:

curl \
    --header "Content-Type: application/json" \
    --data '["Dubbo"]' \
    http://localhost:50051/org.apache.dubbo.demo.DemoService/sayHello

Parameters must be passed in array format. If there are multiple parameters, the format should be like ["param1", {"param2-field": "param2-value"}, ...], please refer to the triple protocol specification for specifics.

Next, you can also use a standard Dubbo client to request the service, specifying the server address to initiate an RPC call. The format is protocol://ip:host.

public class Application {
    public static void main(String[] args) {
        DemoService demoService =
            ReferenceBuilder.newBuilder()
            .interfaceClass(DemoService.class)
            .url("tri://localhost:50051")
            .build()
            .get();

        String message = demoService.sayHello("dubbo");
        System.out.println(message);
    }
}

Congratulations! The above is the basic usage of Dubbo Java RPC communication! 🎉

More Examples

Besides the simple usage scenarios above, developers can also publish multiple services, directly call ServiceConfig/ReferenceConfig to publish/subscribe individual services, etc.

Publish Multiple Services

The following example registers and publishes any number of services FooService, BarService. These services will all use the default timeout configured in providerConfig, avoiding the hassle of repeated configurations for multiple services.

public static void main(String[] args) {
    ProviderConfig providerConfig = new ProviderConfig();
    providerConfig.setTimeout(5000);

    ProtocolConfig protocolConfig = new ProtocolConfig(CommonConstants.TRIPLE, 50051);

    DubboBootstrap.getInstance()
        .protocol(protocolConfig)
        .provider(providerConfig)
        .service(ServiceBuilder.newBuilder().interfaceClass(FooService.class).ref(new FooServiceImpl()).build())
        .service(ServiceBuilder.newBuilder().interfaceClass(BarService.class).ref(new BarServiceImpl()).build())
        .start()
        .await();
}

Publish Individual Services

Directly call ServiceConfig.export() to publish services suitable for dynamic publication or subscription of a single service in runtime; it is similar for ReferenceConfig. For routine application startup processes, it is recommended to use DubboBootstrap instead of directly calling ServiceConfig.export() to publish individual services.

  1. Publishing a service through ServiceConfig
public static void main(String[] args) {
    ServiceConfig<DemoService> demoServiceConfig = new ServiceConfig<>();
    demoServiceConfig.setInterface(DemoService.class);
    demoServiceConfig.setRef(new DemoServiceImpl());
    demoServiceConfig.setVersion("1.0.0");

    demoServiceConfig.export(); // this service will be registered to the default instance of DubboBootstrap.getInstance()
}
  1. Subscribing to services through ReferenceConfig
private DemoService referService() {
    ReferenceConfig<DemoService> reference = new ReferenceConfig<>();
    reference.setInterfaceClass(DemoService.class);

    ReferenceCache cache = SimpleReferenceCache.getCache();
    try {
        return cache.get(reference);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
    }
}

Because the proxy object created by ReferenceConfig.get() holds a lot of resources such as connections and addresses, it is recommended to cache and reuse it. Dubbo officially provides a SimpleReferenceCache implementation for reference. For more content on SimpleReferenceCache, please refer to RPC Framework.

Getting Reference Proxy

Use DubboBootstrap as the startup entry, subscribe to services, and obtain proxy objects.

public static void main(String[] args) {
    ReferenceConfig<GreetingsService> reference =
            ReferenceBuilder.<GreetingsService>newBuilder()
            .interfaceClass(GreetingsService.class)
            .build();
    DubboBootstrap.getInstance().reference(reference).start();
    GreetingsService service = reference.get();
}

Microservices Example

Registry Center and Application Name

Compared to RPC server and RPC client, developing microservice applications based on APIs requires configurations for the application name and registry center.

public static void main(String[] args) {
    DubboBootstrap.getInstance()
        .application()
        .registry(new RegistryConfig("nacos://127.0.0.1:8848"))
        .protocol(new ProtocolConfig(CommonConstants.TRIPLE, 50051))
        .service(ServiceBuilder.newBuilder().interfaceClass(DemoService.class).ref(new DemoServiceImpl()).build())
        .service(ServiceBuilder.newBuilder().interfaceClass(FooService.class).ref(new FooServiceImpl()).build())
        .start()
        .await();
}

Multiple Registry Centers

Multiple registry centers can specify different ids, and services are associated with the registry center instances by id. In the following example, GreetingsService is published to bjRegistry, while DemoService is published to hzRegistry.

public static void main(String[] args) {
    RegistryConfig bjRegistry = new RegistryConfig();
    bjRegistry.setId("bj");
    bjRegistry.setAddress("nacos://127.0.0.1:8848");

    RegistryConfig hzRegistry = new RegistryConfig();
    hzRegistry.setId("hz");
    hzRegistry.setAddress("nacos://127.0.0.2:8848");

    DubboBootstrap.getInstance()
            .registry(bjRegistry)
            .registry(hzRegistry)
            .service(ServiceBuilder.newBuilder().registryIds("bj").interfaceClass(GreetingsService.class).ref(new GreetingsServiceImpl()).build())
            .service(ServiceBuilder.newBuilder().registryIds("hz").interfaceClass(DemoService.class).ref(new DemoServiceImpl()).build())
            .start()
            .await();
}

Publish Individual Services

Directly call ServiceConfig.export() to publish services, suitable for dynamically publishing or subscribing to a single service in runtime; it is similar for ReferenceConfig. For routine application startup processes, it is recommended to use DubboBootstrap instead of directly calling ServiceConfig.export() to publish individual services.

  1. Publishing a service through ServiceConfig
public static void main(String[] args) {
	RegistryConfig hzRegistry = new RegistryConfig();
    hzRegistry.setId("hz");
    hzRegistry.setAddress("nacos://127.0.0.2:8848");

    ServiceConfig<DemoService> demoServiceConfig = new ServiceConfig<>();
    demoServiceConfig.setInterface(DemoService.class);
    demoServiceConfig.setRef(new DemoServiceImpl());
    demoServiceConfig.setVersion("1.0.0");

    demoServiceConfig.setRegistry(hzRegistry);

    demoServiceConfig.export(); // this service will be registered to the default instance of DubboBootstrap.getInstance()
}
  1. Subscribing to services through ReferenceConfig
private DemoService referService() {
    RegistryConfig hzRegistry = new RegistryConfig();
	hzRegistry.setId("hz");
	hzRegistry.setAddress("nacos://127.0.0.2:8848");

    ReferenceConfig<DemoService> reference = new ReferenceConfig<>();
    reference.setInterfaceClass(DemoService.class);

    reference.setRegistry(hzRegistry)

    ReferenceCache cache = SimpleReferenceCache.getCache();
    try {
        return cache.get(reference);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage());
    }
}

More Content