Understanding Apache Dubbo: A Comprehensive RPC Framework Guide

Apache Dubbo RPC Framework Overview

Apache Dubbo is a high-performance RPC (Remote Procedure Call) service development framework designed to address service governance and communication challenges in microservice architectures. The framework provides multi-language SDK implementations including Java and Golang. Microservices built with Dubbo inherently possess remote address discovery and communication capabilities between eachother. By leveraging Dubbo's extensive service governance features, users can implement various service governance requirements such as service discovery, load balancing, and traffic scheduling. Dubbo is designed for high extensibility, allowing users to easily implement customized logic for traffic interception and service routing.

Dubbo has been widely adopted across industries, with its stability and comprehensive features thoroughly tested in production environments. The framework has evolved to Dubbo 3.0, which maintains a strong position in scenarios involving usability, large-scale microservice practices, cloud-native infrastructure adaptation, and security. It remains a top choice for RPC service frameworks.

  1. Distributed System Fundamentals

1.1 Why Do We Need Distribution?

Why do modern large-scale internet applications adopt distributed architectures?

The key consideration is: only when the processing capacity of a single machine cannot meet growing computing and storage demands, and hardware improvements (such as adding memory, using better CPUs) fail to provide better optimization, should we consider a distributed architecture. The fundamental problems that distributed systems aim to solve are the same as those in standalone systems. However, distributed architectures introduce multiple nodes and network communication topologies that create many issues not present in standalone systems. Resolving these issues introduces additional mechanisms and protocols, potentially leading to more unexpected problems.

Simply put, a distributed architecture distributes application pressure by deploying different modules on different servers, with modules communicating through lightweight mechanisms to reduce the processing burden on the application system.

With the growth of internet applications, the scale of website applications continues to expand, and traditional vertical application architectures can no longer meet requirements. Distributed service architectures and flow computing architectures have become necessary. The following illustrates the evolution of website architectures:

1. Single Application Architecture (1-10 users)

When website traffic is minimal, only one application is needed with all functionalities deployed together to reduce deployment nodes and costs. At this stage, data access frameworks (ORM) that simplify CRUD operations are crucial. This architecture suits small websites and management systems due to its simplicity.

Drawbacks:

  • Difficult to scale performance
  • Collaboration challenges (not conducive to team development)
  • Maintenance difficulties (e.g., functional adjustments require repackaging and restarting)

2. Vertical Application Architecture (10-1000 users)

As traffic increases, optimizing a single application becomes less effective. The application is split into independent applications to improve processing efficiency. At this stage, web frameworks (MVC) for accelerating frontend development become essential.

Advantages: Independent module deployment through business division reduces maintenance and deployment difficulties. Teams can focus on specific areas, making performance scaling more targeted.

Drawbacks: Public modules cannot be reused, high module coupling, and increased maintenance costs.

3. Distributed Service Architecture (1000-10000 users)

Improvement over vertical application architecture: as vertical applications grow, interaction between applications becomes inevitable. Core business is extracted as independent services, gradually forming a stable service center that enables frontend applications to respond more quickly to changing market demands. At this stage, distributed service frameworks (RPC) that improve business reuse and manage service collaboration become essential.

4. Flow Computing Architecture (10000+ users)

As services proliferate, reasonable utilization of service resources becomes increasingly important (for example, some services may have very high call frequency while others have very low). In such cases, a scheduling center is needed to dynamically manage cluster capacity based on访问压力 and improve cluster utilization.

SOA (Service Oriented Architecture) serves as the key technology for resource scheduling and governance centers that provide machine utilization.

1.2 Understanding RPC

What is RPC?

RPC (Remote Procedure Call) is a method of inter-process communication. It is a technical approach rather than a specification or protocol. RPC allows a program to call procedures or functions in another address space (typically on another machine connected via a network) without requiring developers to implement the details of the remote call. Whether calling local or remote functions, developers essentially write the same calling code.

In essence, when server A hosts an application that needs to call a function provided by an application on server B, since they don't share memory space, the call must be expressed through the network, conveying both the semantics and data of the call.

Why Use RPC?

RPC is needed for requirements that cannot be met through local calls within a single process or even a single computer, such as communication between different systems or even different organizations. When horizontal scaling of computing capability is required, applications need to be deployed across multiple machines in a cluster. RPC enables calling remote functions as if they were local functions.

Basic RPC working principle involves client stub and server stub components that act as "assistants" for coordinating remote function calls. Their main responsibilities include:

Client Stub:

  1. Client invocation
  2. Serialization
  3. Message sending
  4. Deserialization of response
  5. Return call results

Server Stub:

  1. Deserialization of request
  2. Local service invocation
  3. Return processing results
  4. Serialization of results
  5. Response message

Two main factors affect RPC call efficiency:

  1. Communication (lightweight communication is particularly important)
  2. Serialization and deserialization (whether using XML, JSON, or binary streams for data transmission)

Based on the RPC concept, various RPC frameworks have emerged in the market, including Dubbo, gRPC, Thrift, and HSF (High Speed Service Framework).

  1. Dubbo Framework

2.1 Dubbo Role Architecture

The following describes the node roles in Dubbo architecture:

Node Role Description
Provider Service provider that exposes services
Consumer Service consumer that invokes remote services
Registry Service registry and discovery center
Monitor Monitoring center for call statistics and timing
Container Service running container

Understanding these roles, let's examine Dubbo's workflow:

  1. Start: The service container is responsible for starting, loading, and running service providers.
  2. Register: Service providers register their services with the registry upon startup.
  3. Subscribe: Service consumers subscribe to required services from the registry upon startup.
  4. Notify: The registry returns the provider address list to consumers. If changes occur, the registry pushes updated data to consumers through persistent connections.
  5. Invoke: Service consumers select a provider from the address list using soft load balancing algorithms. If the call fails, another provider is selected.
  6. Count: Service consumers and providers accumulate call counts and times in memory, sending statistical data to the monitoring center every minute.

2.2 Setting Up the Dubbo Environment

1. Registry Center

Dubbo supports mainstream registry centers including Zookeeper, Nacos, Multicast, and Redis. This guide uses Zookeeper as the registry center, which is a tree-structured directory service supporting change notifications, making it suitable as Dubbo's service registry. This is also the officially recommended registry.

Docker commands to set up Zookeeper:

# Pull latest Zookeeper image
docker pull zookeeper

# Start Zookeeper
docker run -d -p 2181:2181 --name zookeeper --restart always [imageId]

# Check Zookeeper status
./zkServer.sh status

2. Project Integration

Dubbo is essentially a JAR package that helps Java applications connect to Zookeeper for service consumption or provision. It is not a standalone service application.

3. Project Setup

Create two projects: a provider (service producer) and a consumer (service consumer). These projects will be used to test various Dubbo features.

2.3 Hello World Implementation

Consider a scenario with a user service (provider) and an order service (consumer) deployed on different servers. When creating an order, the order service needs to retrieve user information including phone numbers and shipping addresses. This requires the order service to remotely invoke interfaces or methods provided by the user service.

1. Registering the Provider with Zookeeper

Adding Dependencies

Add Dubbo dependencies to the provider project (using SpringBoot for the provider, and native Spring for the consumer):

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.8</version>
</dependency>

Dubbo uses Curator as the programming client for interacting with Zookeeper Server. Add the Zookeeper dependency:

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-dependencies-zookeeper</artifactId>
    <version>2.7.8</version>
    <type>pom</type>
</dependency>

Note: Official documentation indicates version compatibility issues between Dubbo and dubbo-dependencies-zookeeper. If compatibility issues arise, you can add Curator and Zookeeper Client dependencies directly:

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
</dependency>

Configuration Methods

Method 1: Annotation-based Configuration

Using @DubboService to expose services and @DubboReference to reference services:

dubbo.application.name=provider-server
dubbo.registry.address=zookeeper://180.76.238.29:2181
dubbo.registry.timeout=20000
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.scan.base-packages=com.example.provider.service

Method 2: XML Configuration

spring-dubbo.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://code.alibabatech.com/schema/dubbo 
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="user-service"/>
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:service interface="com.example.provider.service.UserService" ref="userService"/>
    <bean id="userService" class="com.example.provider.service.impl.UserServiceImpl"/>
</beans>

Import in the main application:

@ImportResource(locations = {"classpath:spring/spring-dubbo.xml"})
@SpringBootApplication
public class ApplicationMain {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationMain.class, args);
    }
}

Method 3: Java Configuration Class

@Configuration
public class DubboConfiguration {
    @Autowired
    private UserService userService;

    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig config = new ApplicationConfig();
        config.setName("user-service");
        return config;
    }

    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig config = new RegistryConfig();
        config.setProtocol("zookeeper");
        config.setAddress("127.0.0.1:2181");
        return config;
    }

    @Bean
    public ProtocolConfig protocolConfig() {
        ProtocolConfig config = new ProtocolConfig();
        config.setName("dubbo");
        config.setPort(20882);
        return config;
    }

    @Bean
    public ServiceConfig<UserService> serviceConfig() {
        ServiceConfig<UserService> serviceConfig = new ServiceConfig<>();
        serviceConfig.setInterface(UserService.class);
        serviceConfig.setRef(userService);
        serviceConfig.setVersion("1.0-SNAPSHOT");
        return serviceConfig;
    }
}

Verify service registration in Zookeeper:

[zk: localhost:2181(CONNECTED) 12] ls /
[dubbo, zookeeper]
[zk: localhost:2181(CONNECTED) 13] ls /dubbo
[metadata, config, com.example.provider.service.UserService]

The UserService node indicates successful registration.

2. Consumer Subscribing to Services

spring-dubbo.xml for consumer:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://code.alibabatech.com/schema/dubbo 
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="consumer-server"/>
    <dubbo:registry address="zookeeper://180.76.238.29:2181" timeout="20000"/>
    <dubbo:reference interface="com.example.provider.service.UserService" id="userService"/>
</beans>

Important notes: If Java objects are transferred during remote calls, they must be serializable. Additionally, consumers need to maintain the same package structure as providers, or extract shared interfaces into a common module.

3. Testing Remote Procedure Calls

Provider side - create the service interface and implementation:

public interface UserService {
    String login(String username, String password);
}

@DubboService
public class UserServiceImpl implements UserService {
    @Override
    public String login(String username, String password) {
        JSONObject response = new JSONObject();
        response.put("token", UUID.randomUUID().toString());
        response.put("username", username);
        return response.toString();
    }
}

Consumer side - invoke the remote service:

@SpringBootTest
class ConsumerApplicationTests {
    @Autowired
    private UserService userService;

    @Test
    void testLogin() {
        String result = userService.login("admin", "123");
        System.out.println(result);
    }
}

2.4 Dubbo-Admin Setup

After Alibaba donated Dubbo to Apache, Apache重构了 dubbo-admin with Vue for the frontend and SpringBoot for the backend, offering improved UI and enhanced functionality.

1. Download and Setup

Download the source code from GitHub: https://github.com/apache/dubbo-admin. Find Dubbo Admin 0.3.0 from releases (compatible with Dubbo 2.7+).

2. Backend Configuration

Modify application.properties to configure Zookeeper connection:

admin.registry.address=zookeeper://180.76.238.29:2181?timeout=20000
admin.config-center=zookeeper://180.76.238.29:2181
admin.metadata-report.address=zookeeper://180.76.238.29:2181

Start the backend and access http://127.0.0.1:8080/swagger-ui.html

3. Frontend Configuration

Install dependencies: npm install (requires Node.js version >10.x). Modify vue.config.js to set the proxy target:

module.exports = {
  outputDir: "target/dist",
  lintOnSave: false,
  devServer: {
    port: 8082,
    proxy: {
      '/': {
        target: 'http://127.0.0.1:8080/',
        changeOrigin: true
      }
    }
  }
}

Start the frontend with npm run serve.

2.5 Dubbo Configuration Options

Dubbo supports three configuration approaches: XML, properties, and API configuration.

1. Startup Check

Dubbo checks service availability on startup. Disable with check="false":

<!-- Disable specific service check -->
<dubbo:reference interface="com.example.service.BarService" check="false"/>

<!-- Disable all service checks -->
<dubbo:consumer check="false"/>

<!-- Disable registry check -->
<dubbo:registry check="false" />

2. Timeout Configuration

<!-- Interface-level timeout -->
<dubbo:reference interface="com.example.service.UserService" timeout="3000"/>

<!-- Global timeout -->
<dubbo:consumer application="consumer-server" timeout="5000"/>

<!-- Method-level timeout -->
<dubbo:reference interface="com.example.service.UserService">
    <dubbo:method name="login" timeout="4000"/>
</dubbo:reference>

Priority: method-level > interface-level > global configuration

3. Retry Configuration

<dubbo:reference interface="com.example.service.UserService">
    <dubbo:method name="login" timeout="4000" retries="3"/>
</dubbo:reference>

Note: Retry is suitable for idempotent methods.

4. Multiple Versions

Support multiple versions for the same service:

@DubboService(version = "v1.0")
public class UserServiceImplV1 implements UserService { }

@DubboService(version = "v2.0")
public class UserServiceImplV2 implements UserService { }

Consumer configuration:

<!-- Call v1.0 -->
<dubbo:reference interface="com.example.service.UserService" version="v1.0"/>

<!-- Call v2.0 -->
<dubbo:reference interface="com.example.service.UserService" version="v2.0"/>

<!-- Random version -->
<dubbo:reference interface="com.example.service.UserService" version="*"/>

5. Local Stub

Execute client-side logic before remote calls:

<dubbo:reference interface="com.example.service.UserService" stub="true"/>

Stub implementation:

public class UserServiceStub implements UserService {
    private final UserService userService;

    public UserServiceStub(UserService userService) {
        this.userService = userService;
    }

    @Override
    public String login(String username, String password) {
        if ("admin".equals(username)) {
            return "{\"message\":\"Admin user doesn't need login\",\"success\":true}";
        }
        try {
            return userService.login(username, password);
        } catch (Exception e) {
            return "{\"message\":\"Login failed: \" + e.getMessage(),\"success\":false}";
        }
    }
}

  1. Dubbo High Availability

3.1 Service Downtime and Direct Connection

When the registry (Zookeeper) experiences downtime, Dubbo can still consume exposed services using local cache information that records provider details.

Additional high-availability features:

  • Monitoring center downtime doesn't affect usage (only loses some sampled data)
  • Database failure allows the registry to serve cached service lists but prevents new registrations
  • Registry peer clusters automatically switch when one fails
  • Stateless providers allow any node to go down without affecting usage
  • When all providers fail, consumers will retry indefinitely

Direct Connection Configuration

Bypass the registry and connect directly:

<dubbo:reference interface="com.example.service.UserService" url="127.0.0.1:20880"/>

Or using annotations:

@DubboReference(url = "127.0.0.1:20880")
private UserService userService;

Summary: Dubbo can call services via local cache when the registry fails, and can also communicate directly without a registry.

3.2 Load Balancing in Cluster Mode

Setting Up the Cluster Environment

Provider implementation:

@DubboService(
    interfaceClass = ServerPortService.class,
    version = "v0.1", 
    group = "server"
)
public class ServerPortServiceImpl implements ServerPortService {
    @Value("${server.port}")
    private String port;

    @Override
    public String getServerPort() {
        return port;
    }
}

Start multiple provider instances with different ports:

java -Dserver.port=8081 -Ddubbo.protocol.port=20881 -jar provider.jar
java -Dserver.port=8082 -Ddubbo.protocol.port=20882 -jar provider.jar
java -Dserver.port=8083 -Ddubbo.protocol.port=20883 -jar provider.jar

Load Balancing Strategies

Dubbo provides four load balancing strategies:

1. Random LoadBalance: Weighted random selection. Default strategy where each service has equal weight.

2. RoundRobin LoadBalance: Weighted round-robin. Configure with loadbalance attribute:

@DubboReference(loadbalance = "roundrobin")
private ServerPortService serverPortService;

3. LeastActive LoadBalance: Least active calls - tracks request completion time and selects the fastest service.

4. ConsistentHash LoadBalance: Consistent hashing - selects services based on hash values for stable request distribution.

3.3 Service Degradation

What is Service Degradation?

In complex distributed or microservice systems with numerous dependencies (where service A depends on B, which depends on C), if service C times out, the entire call chain fails, leading to cascading failures and potentially a service avalanche effect.

Service degradation provides a mechanism to prevent cascading failures when issues occur in specific nodes or services.

1. Mock/Force Return

Temporarily mock a service to return null, ensuring core business continues正常运行. Configure in dubbo-admin dynamic configuration:

configVersion: v0.1
scope: service
enabled: true
key: server:com.example.service.ServerPortService:v0.1
configs:
  - addresses: [0.0.0.0]
    side: consumer
    parameters:
      force: return null

2. Cluster Fault Tolerance

When cluster calls fail, Dubbo provides multiple fault tolerance strategies, with failover as the default. Other options include failfast, failback, and fork cluster modes.

Example configuring failfast:

<dubbo:reference cluster="failfast" />

  1. Dubbo Architecture Principles

Dubbo's architecture is built on several key principles that enable its high performance and reliability in distributed systems. The framework handles service discovery, communication, and governance through a well-designed architecture that separates concerns and provides flexibility for various deployment scenarios.

The core components work together to provide seamless remote service invocation while maintaining service governance capabilities essential for production microservices environments.

Tags: Dubbo rpc microservices java distributed-systems

Posted on Tue, 04 Aug 2026 16:12:32 +0000 by daveoliveruk