go_study/go-for-study/vendor/github.com/wvanbergen/kazoo-go/kazoo.go

225 lines
5.7 KiB
Go

package kazoo
import (
"encoding/json"
"errors"
"fmt"
"path"
"strconv"
"strings"
"time"
"github.com/samuel/go-zookeeper/zk"
)
var (
FailedToClaimPartition = errors.New("Failed to claim partition for this consumer instance. Do you have a rogue consumer running?")
)
// ParseConnectionString parses a zookeeper connection string in the form of
// host1:2181,host2:2181/chroot and returns the list of servers, and the chroot.
func ParseConnectionString(zookeeper string) (nodes []string, chroot string) {
nodesAndChroot := strings.SplitN(zookeeper, "/", 2)
if len(nodesAndChroot) == 2 {
chroot = fmt.Sprintf("/%s", nodesAndChroot[1])
}
nodes = strings.Split(nodesAndChroot[0], ",")
return
}
// BuildConnectionString builds a Zookeeper connection string for a list of nodes.
// Returns a string like "zk1:2181,zk2:2181,zk3:2181"
func BuildConnectionString(nodes []string) string {
return strings.Join(nodes, ",")
}
// ConnectionStringWithChroot builds a Zookeeper connection string for a list
// of nodes and a chroot. The chroot should start with "/".
// Returns a string like "zk1:2181,zk2:2181,zk3:2181/chroot"
func BuildConnectionStringWithChroot(nodes []string, chroot string) string {
return fmt.Sprintf("%s%s", strings.Join(nodes, ","), chroot)
}
// Kazoo interacts with the Kafka metadata in Zookeeper
type Kazoo struct {
conn *zk.Conn
conf *Config
}
// Config holds configuration values f.
type Config struct {
// The chroot the Kafka installation is registerde under. Defaults to "".
Chroot string
// The amount of time the Zookeeper client can be disconnected from the Zookeeper cluster
// before the cluster will get rid of watches and ephemeral nodes. Defaults to 1 second.
Timeout time.Duration
}
// NewConfig instantiates a new Config struct with sane defaults.
func NewConfig() *Config {
return &Config{Timeout: 1 * time.Second}
}
// NewKazoo creates a new connection instance
func NewKazoo(servers []string, conf *Config) (*Kazoo, error) {
if conf == nil {
conf = NewConfig()
}
conn, _, err := zk.Connect(servers, conf.Timeout)
if err != nil {
return nil, err
}
return &Kazoo{conn, conf}, nil
}
// NewKazooFromConnectionString creates a new connection instance
// based on a zookeeer connection string that can include a chroot.
func NewKazooFromConnectionString(connectionString string, conf *Config) (*Kazoo, error) {
if conf == nil {
conf = NewConfig()
}
nodes, chroot := ParseConnectionString(connectionString)
conf.Chroot = chroot
return NewKazoo(nodes, conf)
}
// Brokers returns a map of all the brokers that make part of the
// Kafka cluster that is registered in Zookeeper.
func (kz *Kazoo) Brokers() (map[int32]string, error) {
root := fmt.Sprintf("%s/brokers/ids", kz.conf.Chroot)
children, _, err := kz.conn.Children(root)
if err != nil {
return nil, err
}
type brokerEntry struct {
Host string `json:"host"`
Port int `json:"port"`
}
result := make(map[int32]string)
for _, child := range children {
brokerID, err := strconv.ParseInt(child, 10, 32)
if err != nil {
return nil, err
}
value, _, err := kz.conn.Get(path.Join(root, child))
if err != nil {
return nil, err
}
var brokerNode brokerEntry
if err := json.Unmarshal(value, &brokerNode); err != nil {
return nil, err
}
result[int32(brokerID)] = fmt.Sprintf("%s:%d", brokerNode.Host, brokerNode.Port)
}
return result, nil
}
// BrokerList returns a slice of broker addresses that can be used to connect to
// the Kafka cluster, e.g. using `sarama.NewAsyncProducer()`.
func (kz *Kazoo) BrokerList() ([]string, error) {
brokers, err := kz.Brokers()
if err != nil {
return nil, err
}
result := make([]string, 0, len(brokers))
for _, broker := range brokers {
result = append(result, broker)
}
return result, nil
}
// Controller returns what broker is currently acting as controller of the Kafka cluster
func (kz *Kazoo) Controller() (int32, error) {
type controllerEntry struct {
BrokerID int32 `json:"brokerid"`
}
node := fmt.Sprintf("%s/controller", kz.conf.Chroot)
data, _, err := kz.conn.Get(node)
if err != nil {
return -1, err
}
var controllerNode controllerEntry
if err := json.Unmarshal(data, &controllerNode); err != nil {
return -1, err
}
return controllerNode.BrokerID, nil
}
// Close closes the connection with the Zookeeper cluster
func (kz *Kazoo) Close() error {
kz.conn.Close()
return nil
}
////////////////////////////////////////////////////////////////////////
// Util methods
////////////////////////////////////////////////////////////////////////
// Exists checks existence of a node
func (kz *Kazoo) exists(node string) (ok bool, err error) {
ok, _, err = kz.conn.Exists(node)
return
}
// DeleteAll deletes a node recursively
func (kz *Kazoo) deleteRecursive(node string) (err error) {
children, stat, err := kz.conn.Children(node)
if err == zk.ErrNoNode {
return nil
} else if err != nil {
return
}
for _, child := range children {
if err = kz.deleteRecursive(path.Join(node, child)); err != nil {
return
}
}
return kz.conn.Delete(node, stat.Version)
}
// MkdirAll creates a directory recursively
func (kz *Kazoo) mkdirRecursive(node string) (err error) {
parent := path.Dir(node)
if parent != "/" {
if err = kz.mkdirRecursive(parent); err != nil {
return
}
}
_, err = kz.conn.Create(node, nil, 0, zk.WorldACL(zk.PermAll))
if err == zk.ErrNodeExists {
err = nil
}
return
}
// Create stores a new value at node. Fails if already set.
func (kz *Kazoo) create(node string, value []byte, ephemeral bool) (err error) {
if err = kz.mkdirRecursive(path.Dir(node)); err != nil {
return
}
flags := int32(0)
if ephemeral {
flags = zk.FlagEphemeral
}
_, err = kz.conn.Create(node, value, flags, zk.WorldACL(zk.PermAll))
return
}