Netty 实战篇:构建简易注册中心,实现服务发现与调用路由

发布于:2025-05-31 ⋅ 阅读:(31) ⋅ 点赞:(0)

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


网站公告

今日签到

点亮在社区的每一天
去签到