本文将为前面构建的轻量级 RPC 框架添加“服务注册与发现”功能,支持多服务节点动态上线、自动感知与调用路由,为构建真正可扩展的分布式系统打好基础。
一、背景:为什么需要注册中心?
如果每个客户端都硬编码连接某个 IP/端口的服务:
不利于服务水平扩展(多实例)
无法实现负载均衡
服务上线/下线无法感知
✅ 有了注册中心后:
服务启动时自动注册
客户端从注册中心获取最新服务列表
可实现轮询/哈希/权重等负载均衡
二、系统结构图
┌──────────────┐
│ 注册中心 │◄────────────┐
│(服务发现) │ │
└─────┬────────┘ │
▲ │注册服务
│ │
┌─────┴──────┐ ┌─────────┴─────────┐
│ 服务节点A │ │ 服务节点B │
└────┬───────┘ └─────────┬─────────┘
│注册 │注册
▼ ▼
客户端 ◄──── 查询服务地址列表 ─────┐
└── 负载均衡调用 ────────┘
三、注册中心(基于 Netty 实现)
public class RegisterCenterServer {
private static final Map<String, List<InetSocketAddress>> serviceMap = new ConcurrentHashMap<>();
public static void main(String[] args) throws Exception {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)))
.addLast(new ObjectEncoder())
.addLast(new RegisterHandler());
}
});
bootstrap.bind(9000).sync();
System.out.println("注册中心启动成功");
}
static class RegisterHandler extends SimpleChannelInboundHandler<Object> {
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof RegisterRequest) {
RegisterRequest req = (RegisterRequest) msg;
serviceMap.computeIfAbsent(req.getServiceName(), k -> new ArrayList<>())
.add(req.getAddress());
ctx.writeAndFlush("SUCCESS");
} else if (msg instanceof LookupRequest) {
LookupRequest req = (LookupRequest) msg;
List<InetSocketAddress> list = serviceMap.getOrDefault(req.getServiceName(), Collections.emptyList());
ctx.writeAndFlush(list);
}
}
}
}
四、服务节点注册流程
服务端在启动时将自己的信息注册到注册中心:
RegisterRequest req = new RegisterRequest();
req.setServiceName("helloService");
req.setAddress(new InetSocketAddress("127.0.0.1", 8080));
Socket socket = new Socket("127.0.0.1", 9000);
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(req);
五、客户端服务发现
public class ServiceDiscovery {
public List<InetSocketAddress> lookup(String serviceName) {
try (Socket socket = new Socket("127.0.0.1", 9000)) {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(new LookupRequest(serviceName));
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
return (List<InetSocketAddress>) in.readObject();
} catch (Exception e) {
throw new RuntimeException("服务发现失败", e);
}
}
}
六、负载均衡策略
public class LoadBalancer {
public static InetSocketAddress choose(List<InetSocketAddress> list) {
return list.get(new Random().nextInt(list.size())); // 简单轮询/随机
}
}
七、调用流程整合
ServiceDiscovery discovery = new ServiceDiscovery();
List<InetSocketAddress> providers = discovery.lookup("helloService");
InetSocketAddress address = LoadBalancer.choose(providers);
// 使用 address 建立 Netty 连接,发送 RPC 请求
八、总结
通过本篇内容,我们为 Netty RPC 框架实现了:
✅ 多服务节点注册与发现
✅ 基于 Netty 的轻量级注册中心
✅ 动态服务列表查询
✅ 简易负载均衡支持