Самый эффективный способ создать путь в zookeeper, где корневые элементы пути могут существовать или не существовать? - PullRequest
10 голосов
/ 28 марта 2012

Представьте себе путь "/ root / child1 / child2 / child3"

Вообразите в zookeeper, что, возможно, часть этого существует, скажем "/ root / child1"

В zookeeper нет эквивалента "mkdir -p"; Кроме того, ZooKeeper.multi () завершится ошибкой, если произойдет сбой какой-либо одной операции, поэтому «путь создания» действительно не может быть включен в мульти-вызов. Кроме того, у вас может быть другой клиент, пытающийся создать тот же путь ...

Это то, что я придумал для создания пути. Интересно, стоит ли даже проверять, существует ли деталь, или нет, чтобы сохранить двустороннюю связь с существующим вызовом ().

//String[] pathParts new String[] { "root", "child1", "child2", "child3" };

public void savePath(String[] pathParts) {
    if (zooKeeper.exists(pathString, false) != null) return;
    StringBuilder path = new StringBuilder();
    for (String pathElement : pathParts) {
        path.append(UNIX_FILE_SEPARATOR).append(pathElement);
        String pathString = path.toString();
        try {
            //bother with the exists call or not?
            if (zooKeeper.exists(pathString, false) == null) {
                zooKeeper.create(pathString, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (KeeperException e) {
            if (e.code() != KeeperException.Code.NODEEXISTS)
                throw e;
        }
    }
}

Какой самый эффективный способ сделать это? Предполагая, что: а) вы не знаете заранее, какая часть пути уже существует, и б) какой-то другой клиент может пытаться написать этот же путь (и мы хотим избежать блокировки).

Ответы [ 2 ]

13 голосов
/ 28 марта 2012

Вы можете использовать библиотеку Netflix , которая значительно упрощает использование zookeeper

client.create().withMode(CreateMode.PERSISTENT).forPath("/root/child1/child2/child3", new byte[0]).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).creatingParentsIfNeeded();
7 голосов
/ 31 марта 2012

Существующий вызов может быть сделан с 1 поездкой туда и обратно от сервера к клиенту.

У вызова create такая же передача в оба конца, но create - это операция записи, которая влечет за собой еще пару циклов между серверами в кластере zk, так что создание немного дороже, чем существует.

Таким образом, общее время для вашего алгоритма,

Время для 1 операции чтения * Узел вероятности уже существует + (Время для 1 операции чтения) * (1 - Вероятность узла уже существует).

Так что любой из if(!exist()) create() против create() может быть быстрее. В конце концов, это, вероятно, не имеет значения.

Если вы хотите быть очень быстрым, вы можете использовать async api, чтобы вы могли создавать все компоненты вашего пути, не дожидаясь ответа сервера на запросы 1 на 1.

final AtomicBoolean success = new AtomicBoolean(false);
final CountdownLatch latch = new CountdownLatch(1);
StringCallback cb = new StringCallback() {
    processResult(int rc, String path, Object ctx, String name) {
        if(name.equals(pathString ) {
            //wait for the last path
            success.set(rc == KeeperException.Code.NODEEXISTS ||
                        rc == KeeperException.Code.OK);
            latch.countDown();
        }
    }
};

StringBuilder path = new StringBuilder();
for (String pathElement : pathParts) {
    path.append(UNIX_FILE_SEPARATOR).append(pathElement);
    String pathString = path.toString();
    //send requests to create all parts of the path without waiting for the
    //results of previous calls to return
    zooKeeper.create(pathString, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, cb);
}
latch.await();
if(!success.get()) { 
    throw ...
}
...