本文将为前面构建的轻量级 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 的轻量级注册中心
✅ 动态服务列表查询
✅ 简易负载均衡支持