使用 ZooKeeper 进行服务发现是一种常见的方法,它可以帮助分布式系统中的服务实例动态地注册和发现彼此。以下是使用 ZooKeeper 进行服务发现的基本步骤:
首先,你需要安装并运行 ZooKeeper 集群。确保所有节点都正常运行并且可以相互通信。
当一个服务实例启动时,它需要向 ZooKeeper 注册自己的信息。通常,这包括服务的名称、地址、端口等。
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
public class ServiceRegistry {
    private static final String ZK_ADDRESS = "localhost:2181";
    private static final int SESSION_TIMEOUT = 3000;
    private static final String REGISTRY_PATH = "/services/myService";
    public void registerService(String serviceName, String host, int port) throws Exception {
        ZooKeeper zk = new ZooKeeper(ZK_ADDRESS, SESSION_TIMEOUT, event -> {
            // 处理连接事件
        });
        String servicePath = REGISTRY_PATH + "/" + serviceName;
        if (zk.exists(servicePath, false) == null) {
            zk.create(servicePath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        String instancePath = servicePath + "/" + host + ":" + port;
        zk.create(instancePath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        zk.close();
    }
}
当一个服务实例需要发现其他服务实例时,它可以从 ZooKeeper 中读取相应的节点信息。
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import java.util.List;
public class ServiceDiscovery {
    private static final String ZK_ADDRESS = "localhost:2181";
    private static final int SESSION_TIMEOUT = 3000;
    private static final String REGISTRY_PATH = "/services/myService";
    public List<String> discoverServices(String serviceName) throws Exception {
        ZooKeeper zk = new ZooKeeper(ZK_ADDRESS, SESSION_TIMEOUT, event -> {
            // 处理连接事件
        });
        String servicePath = REGISTRY_PATH + "/" + serviceName;
        if (zk.exists(servicePath, false) == null) {
            zk.close();
            return null;
        }
        List<String> instances = zk.getChildren(servicePath, false);
        zk.close();
        return instances;
    }
}
为了实时更新服务实例列表,可以使用 ZooKeeper 的监听机制。当有新的服务实例注册或现有的服务实例下线时,ZooKeeper 会通知客户端。
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.concurrent.CountDownLatch;
public class ServiceWatcher implements Watcher {
    private static final String ZK_ADDRESS = "localhost:2181";
    private static final int SESSION_TIMEOUT = 3000;
    private static final String REGISTRY_PATH = "/services/myService";
    private ZooKeeper zk;
    private CountDownLatch connectedSignal = new CountDownLatch(1);
    public void connectToZooKeeper() throws Exception {
        zk = new ZooKeeper(ZK_ADDRESS, SESSION_TIMEOUT, this);
        connectedSignal.await();
    }
    @Override
    public void process(WatchedEvent event) {
        if (event.getState() == Event.KeeperState.SyncConnected) {
            connectedSignal.countDown();
        }
    }
    public void watchServices(String serviceName) throws Exception {
        connectToZooKeeper();
        String servicePath = REGISTRY_PATH + "/" + serviceName;
        if (zk.exists(servicePath, this) == null) {
            throw new Exception("Service not found");
        }
        zk.getChildren(servicePath, this);
    }
    public static void main(String[] args) throws Exception {
        ServiceWatcher watcher = new ServiceWatcher();
        watcher.watchServices("myService");
        // 保持程序运行以接收监听事件
        Thread.sleep(Long.MAX_VALUE);
    }
}
通过以上步骤,你可以使用 ZooKeeper 实现服务发现。服务实例在启动时注册自己,在需要时可以发现其他服务实例,并且可以通过监听机制实时更新服务列表。这种方法在分布式系统中非常有用,可以提高系统的可扩展性和可靠性。