// Close the release channel when you want to clean up nicely. func CreatePidNode(zconn zookeeper.Conn, zkPath string, contents string, done chan struct{}) error { // On the first try, assume the cluster is up and running, that will // help hunt down any config issues present at startup if _, err := zconn.Create(zkPath, []byte(contents), zookeeper.FlagEphemeral, zookeeper.WorldACL(PERM_FILE)); err != nil { if ZkErrorEqual(err, zookeeper.ErrNodeExists) { err = zconn.Delete(zkPath, -1) } if err != nil { return fmt.Errorf("zkutil: failed deleting pid node: %v: %v", zkPath, err) } _, err = zconn.Create(zkPath, []byte(contents), zookeeper.FlagEphemeral, zookeeper.WorldACL(PERM_FILE)) if err != nil { return fmt.Errorf("zkutil: failed creating pid node: %v: %v", zkPath, err) } } go func() { for { _, _, watch, err := zconn.GetW(zkPath) if err != nil { if ZkErrorEqual(err, zookeeper.ErrNoNode) { _, err = zconn.Create(zkPath, []byte(contents), zookeeper.FlagEphemeral, zookeeper.WorldACL(zookeeper.PermAll)) if err != nil { log.Warningf("failed recreating pid node: %v: %v", zkPath, err) } else { log.Infof("recreated pid node: %v", zkPath) continue } } else { log.Warningf("failed reading pid node: %v", err) } } else { select { case event := <-watch: if ZkEventOk(event) && event.Type == zookeeper.EventNodeDeleted { // Most likely another process has started up. However, // there is a chance that an ephemeral node is deleted by // the session expiring, yet that same session gets a watch // notification. This seems like buggy behavior, but rather // than race too hard on the node, just wait a bit and see // if the situation resolves itself. log.Warningf("pid deleted: %v", zkPath) } else { log.Infof("pid node event: %v", event) } // break here and wait for a bit before attempting case <-done: log.Infof("pid watcher stopped on done: %v", zkPath) return } } select { // No one likes a thundering herd, least of all zookeeper. case <-time.After(5*time.Second + time.Duration(rand.Int63n(55e9))): case <-done: log.Infof("pid watcher stopped on done: %v", zkPath) return } } }() return nil }
/** * Watches a Zookeeper node continuously in a loop. When a watch fires, the new config is rendered. * When first registering the watch, the initial payload is also rendered */ func (z ZookeeperClient) watchLocalProxyConfig(conn *zk.Conn, path string) { go func() { for { payload, _, watch, err := conn.GetW(path) must(err) RenderLocalProxyConfig(payload, ConfigObj) // block till event fires event := <-watch log.Info("Received Zookeeper event: " + event.Type.String()) RenderLocalProxyConfig(payload, ConfigObj) } }() }
func sinkSelfEvents(conn *zk.Conn, path string, sink chan<- zk.Event) chan<- bool { control := make(chan bool) go func() { _, _, selfCh, err := conn.GetW(path) if err != nil { logger.Panicf("failed to set listener on path: %s", err.Error()) } for { select { case _ = <-control: break case ev := <-selfCh: sink <- ev _, _, selfCh, err = conn.GetW(path) if err != nil { logger.Printf("failed to set listener on path: %s\n", err.Error()) } } } }() return control }