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.
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.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.