Backend Development 23 min read

Understanding RPC and the Dubbo Framework: Concepts, Demo Code, Architecture and SPI Mechanism

This article explains the fundamentals of Remote Procedure Call (RPC), provides a complete Java RPC demo, introduces the Dubbo distributed RPC framework with its layered architecture, SPI extension mechanism, service exposure and reference processes, and discusses clustering, fault‑tolerance and load‑balancing strategies for building robust backend services.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding RPC and the Dubbo Framework: Concepts, Demo Code, Architecture and SPI Mechanism

In large‑scale internet systems, services are deployed on different machines and network communication code becomes complex; Remote Procedure Call (RPC) abstracts this by allowing a consumer to invoke a remote service as if it were local, hiding the networking details.

A minimal RPC demo is presented, starting with a common service interface:

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

The implementation simply returns a greeting:

public class SoWhatServiceImpl implements SoWhatService {
    @Override
    public String sayHello(String name) {
        return "你好啊 " + name;
    }
}

Service registration and exposure are handled by a tiny framework:

public class ServiceFramework {
    public static void export(Object service, int port) throws Exception {
        ServerSocket server = new ServerSocket(port);
        while (true) {
            Socket socket = server.accept();
            new Thread(() -> {
                try {
                    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                    String methodName = (String) input.readObject();
                    Class
[] parameterTypes = (Class
[]) input.readObject();
                    Object[] arguments = (Object[]) input.readObject();
                    Method method = service.getClass().getMethod(methodName, parameterTypes);
                    Object result = method.invoke(service, arguments);
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                    output.writeObject(result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

The server starts the service:

public class ServerMain {
    public static void main(String[] args) {
        SoWhatService service = new SoWhatServiceImpl();
        try {
            ServiceFramework.export(service, 1412);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Clients obtain a dynamic proxy that forwards calls over the network:

public class RpcFunction {
    public static
T refer(Class
interfaceClass, String host, int port) throws Exception {
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class
[]{interfaceClass},
            (proxy, method, arguments) -> {
                Socket socket = new Socket(host, port);
                ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                output.writeObject(method.getName());
                output.writeObject(method.getParameterTypes());
                output.writeObject(arguments);
                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                return input.readObject();
            });
    }
}

And a client uses the proxy:

public class RunMain {
    public static void main(String[] args) {
        try {
            SoWhatService service = RpcFunction.refer(SoWhatService.class, "127.0.0.1", 1412);
            System.out.println(service.sayHello(" sowhat1412"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Dubbo, an open‑source high‑performance RPC framework from Alibaba, builds on these ideas and adds features such as automatic service registration, discovery, clustering, and extensibility. Its six core capabilities are interface‑based high‑performance calls, intelligent fault‑tolerance and load balancing, automatic registration/discovery, high extensibility, runtime traffic scheduling, and visual service governance.

Dubbo’s architecture is divided into three main layers—Business, RPC, and Remoting—and further into ten detailed layers (Service, Config, Proxy, Registry, Cluster, Monitor, Protocol, Exchange, Transport, Serialize), each responsible for a specific aspect of remote invocation.

The framework uses a micro‑kernel design with a Service Provider Interface (SPI) mechanism. A simple SPI demo defines an interface and two implementations:

package com.example.demo.spi;
public interface SPIService {
    void execute();
}
public class SpiImpl1 implements SPIService {
    @Override
    public void execute() {
        System.out.println("SpiImpl1.execute()");
    }
}
public class SpiImpl2 implements SPIService {
    @Override
    public void execute() {
        System.out.println("SpiImpl2.execute()");
    }
}

Loading the implementations can be done via Java’s ServiceLoader or Dubbo’s ExtensionLoader, which supports on‑demand loading, AOP wrapping, and adaptive extensions. An adaptive example shows how a method annotated with @Adaptive generates a proxy that selects the real implementation based on URL parameters:

public interface WheelMaker {
    Wheel makeWheel(URL url);
    @Adaptive
    String watering(URL url);
}
public class AdaptiveWheelMaker implements WheelMaker {
    @Override
    public String watering(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        String wheelMakerName = url.getParameter("Wheel.maker");
        if (wheelMakerName == null) {
            throw new IllegalArgumentException("wheelMakerName == null");
        }
        WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);
        return wheelMaker.makeWheel(url);
    }
}

Dubbo’s service exposure process builds a URL from configuration, creates an Invoker, converts it to an Exporter via the selected Protocol, starts a Netty server, and registers the service URL with a registry. Service reference performs the reverse: it builds a URL, obtains an Invoker via the adaptive protocol, optionally registers the consumer, and finally creates a client‑side proxy.

During invocation, the client creates an RpcInvocation, the Cluster selects an appropriate Invoker using a LoadBalance strategy (Random, LeastActive, RoundRobin, ConsistentHash), the request is serialized (default Hessian2) and sent over a long‑lived NIO connection, the provider deserializes, invokes the target method, and returns the result.

Dubbo provides several clustering strategies (Failover, Failfast, Failsafe, Failback, Forking, Broadcast, Available, Mergeable) and load‑balancing algorithms, allowing fine‑grained control over fault tolerance and traffic distribution.

Finally, the article outlines how to design a custom RPC framework by reusing the concepts demonstrated: service registration/discovery (e.g., ZooKeeper), dynamic proxy‑based client calls, load‑balancing, a chosen transport (Netty), serialization, and optional monitoring.

distributed systemsJavabackend developmentRPCmicrokernelDubboSPI
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.