How RPC Works: Build a Simple RPC Framework with Spring, Netty, and Zookeeper
This article explains the fundamentals of Remote Procedure Call (RPC), detailing the end‑to‑end workflow—from interface definition and dynamic proxy generation on the client, through service registration in ZooKeeper, to server‑side handling with Spring and Netty—accompanied by complete Java code snippets and configuration examples.
RPC Call Overview
Remote Procedure Call (RPC) enables a program on one host to invoke a method on a remote host as if it were local. The typical lifecycle includes defining a shared service interface, generating a client‑side dynamic proxy, registering the service implementation in a registry (ZooKeeper), discovering service addresses, and transmitting requests/responses over the network (Netty).
Service Interface and Implementation
public interface HelloService {
String sayHello(String somebody);
}
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String somebody) {
return "hello " + somebody + "!";
}
}Spring XML Service Registration
Custom storm:service tag registers the bean in ZooKeeper. Example XSD fragment and configuration are shown below.
<xsd:element name="service">
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="interface" type="xsd:string" use="required"/>
<xsd:attribute name="timeout" type="xsd:int" use="required"/>
<xsd:attribute name="serverPort" type="xsd:int" use="required"/>
<xsd:attribute name="ref" type="xsd:string" use="required"/>
<xsd:attribute name="weight" type="xsd:int" use="optional"/>
<xsd:attribute name="workerThreads" type="xsd:int" use="optional"/>
<xsd:attribute name="appKey" type="xsd:string" use="required"/>
<xsd:attribute name="groupName" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:element> <bean id="helloService" class="com.hsunfkqm.storm.framework.test.HelloServiceImpl"/>
<storm:service id="helloServiceRegister"
interface="com.hsunfkqm.storm.framework.test.HelloService"
ref="helloService"
groupName="default"
weight="2"
appKey="ares"
workerThreads="100"
serverPort="8081"
timeout="600"/>Netty Server Listener
Server bootstrap creates boss and worker groups, configures channel options, and adds decoder, encoder and business handler.
public void start(final int port) {
synchronized (NettyServer.class) {
if (bossGroup != null || workerGroup != null) return;
bossGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();
ServerBootstrap sb = new ServerBootstrap();
sb.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyDecoderHandler(StormRequest.class, serializeType));
ch.pipeline().addLast(new NettyEncoderHandler(serializeType));
ch.pipeline().addLast(new NettyServerInvokeHandler());
}
});
channel = sb.bind(port).sync().channel();
}
} NettyServerInvokeHandlerextracts the target method via reflection, applies a Semaphore for concurrency limiting, invokes the method on the service implementation, and writes back a StormResponse.
Client Proxy Generation and Invocation
Dynamic proxy forwards method calls to a StormRequest, selects a provider using a load‑balancing strategy, and sends the request through Netty.
public Object getProxy() {
return Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{targetInterface},
this);
}Service discovery and load balancing:
String serviceKey = targetInterface.getName();
List<ProviderService> providerServices = RegisterCenter.singleton()
.getServiceMetaDataMap4Consume().get(serviceKey);
ClusterStrategy strategy = ClusterEngine.queryClusterStrategy(clusterStrategy);
ProviderService provider = strategy.select(providerServices);Sending the request and awaiting response:
Future responseFuture = fixedThreadPool.submit(
RevokerServiceCallable.of(
new InetSocketAddress(provider.getServerIp(), provider.getServerPort()),
request));
StormResponse response = (StormResponse) responseFuture.get(
request.getInvokeTimeout(), TimeUnit.MILLISECONDS);
return response != null ? response.getResult() : null;Request / Response DTOs
public class StormRequest implements Serializable {
private String uniqueKey; // UUID
private ProviderService providerService; // target provider
private String invokedMethodName;
private Object[] args;
private String appName;
private long invokeTimeout;
// getters / setters omitted
}
public class StormResponse implements Serializable {
private String uniqueKey;
private long invokeTimeout;
private Object result;
// getters / setters omitted
}Provider Registration Logic
After ProviderFactoryBean properties are set, NettyServer.singleton().start(serverPort) is invoked and the service metadata is written to ZooKeeper under a path /ROOT_PATH/{appKey}/{groupName}/{serviceName}. Ephemeral nodes store each instance’s IP, port, weight and thread count.
Testing the RPC System
Server entry point loads the Spring context:
public class MainServer {
public static void main(String[] args) throws Exception {
new ClassPathXmlApplicationContext("storm-server.xml");
System.out.println("Service published");
}
}Client entry point obtains the proxy from Spring and invokes the method:
public class Client {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("storm-client.xml");
HelloService hello = (HelloService) ctx.getBean("helloService");
System.out.println(hello.sayHello("World"));
}
}Complete Source
The full implementation, including custom namespace handlers, provider factories, and Netty codec classes, can be found at the following repository:
https://github.com/fankongqiumu/storm.git
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.
