Categorygithub.com/segmentio/consul-go
modulepackage
1.3.0
Repository: https://github.com/segmentio/consul-go.git
Documentation: pkg.go.dev

# README

consul-go CircleCI Go Report Card GoDoc

Motivations

Consul being built in Go it already has pretty good client library support, however the package was written a while ago and still lack modern Go features.
This package exposes an API for interacting with a consul agent. It differs from the standard Go package in a couple of ways:

  • The abstractions are not one-to-one translations of the Consul API, instead the package offers building blocks that can be used to interract with Consul.

  • Arguments are passed by value which makes the code easier to manipulate, safer (no risk of dereferencing nil pointers), and greatly reduces the number of dynamic memory allocations.

  • The Client type borrows its design from the net/http package and makes the its use more idiomatic to Go developers.

  • The client methods all support passing a context.Context, allowing finer grain control over requests timeout and cancellations.

Resolver

One of the main features of Consul is service discovery, which means translating a logical service name into a set of network addresses at which clients can access it.
The package offers a high-level abstraction to address this specific use-case with the Resolver type.

package main

import (
    "context"
    "fmt"

    "github.com/segmentio/consul-go"
)

func main() {
    // Queries Consul for a list of addresses where "my-service" is available,
    // the result will be sorted to get the addresses closest to the agent first.
    rslv := &Resolver{
        Near: "_agent",
    }

    addrs, err := rslv.LookupService(context.Background(), "my-service")
    if err != nil {
        fmt.Println(err)
        return
    }

    for _, addr := range addrs {
        fmt.Println(addr)
    }
}

Dialer

Resolving service names to addresses is often times done because the program intends to connect to those services.
The package provides an abstractions of this mechanism with the Dialer type, which mirror the standard net.Dialer to make it an easy drop-in replacement and bring service discovery to existing software.

Here's an example of how the Dialer type can be paired with the standard HTTP client:

package main

import (
    "context"
    "fmt"
    "net/http"
    "os"

    "github.com/segmentio/consul-go"
)

func main() {
    // Replace the DialContext method on the default transport to use consul for
    // all host name resolutions.
    http.DefaultTransport.DialContext = (&consul.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
        DualStack: true,
    }).DialContext

    res, err := http.Get("http://my-service/")
    if err != nil {
        fmt.Println(err)
        return
    }

    io.Copy(os.Stdout, res.Body)
    res.Body.Close()
}

Listener

On the other side, services also need to register to consul. While there are ways to automate this using tools like registrator some systems may need to have finer grain control over the metadata attached to the service registration. This is where the Listener type comes into play. It allows the creation of net.Listener values that are automatically registered to Consul.

Here's an example of how the Listener type can be paired with the standard HTTP server:

package main

import (
    "context"
    "fmt"
    "net/http"

    "github.com/segmentio/consul-go"
)

func main() {
    // This listener automatically registers the IP and port that it accepts
    // connections on.
    httpLstn, err := consul.Listen("tcp", ":0")
    if err != nil {
        fmt.Println(err)
        return
    }

    (&http.Server{
        Handler: http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
            // ...
        }),
    }).Serve(httpLstn)
}

Transport (HTTP)

The approach of overwritting the dialer in the HTTP transport may not always be ideal because of connection pooling, there will be a sticky effect where all requests going out for a single hostname would hit the same services. This is due to the fact that once the connection is established no more consul lookups are made to resolve service names.
An alternative option is to make a service resolution call for every request, which may resolve to different network addresses and better distribute the load among the pool of available services. The httpconsul package has a decorator that is intended to transparently provide this feature on http.RoundTripper instances.

package main

import (
    "net/http"

    "github.com/segmentio/consul-go/httpconsul"
)

func main() {
    // Wraps the default transport so all service names are looked up in consul.
    // The consul client uses its own transport so there's no risk of recursive
    // loop here.
    http.DefaultTransport = httpconsul.NewTransport(http.DefaultTransport)

    // ...
}

Sessions and Locks

Sessions and Locks have lifetimes, which translates nicely into the Go Context concept. The APIs abstract sessions and locks as contexts, which makes it possible to inject dependencies on Consul Sessions and Locks into any context-aware code.

The synchronization mechanisms come in various locking algorithms (see Lock, and other similar functions). Lock takes a list of keys and blocks until it was able to acquire all of them, the algorithm is designed to prevent deadlocks (by sorting the list of keys and acquiring the locks sequentially).

Here are a couple of examples:

Creating Sessions

// Creates a session in Consul and returns a context associated to it.
// The session is automatically renewed, and destroyed when cancel is called.
//
// If the session gets expired or removed for some reason the context is
// asynchronously canceled.
ctx, cancel := consul.WithSession(context.Background(), consul.Session{
  Name: "my session",
})

Acquiring Locks

// A session is automatically created and attached to the keys, if the session
// expires it also releases the locks which means the returned context would
// get asynchronously canceled. This is great to build algorithms that depend
// on the lock being held and need to abort their execution if they detect that
// they lost ownership of the keys.
ctx, cancel := consul.Lock(context.Background(), "key-1", "key-A")

Chaining dependencies

// This context is canceled after 10 seconds, it's the parent context of the
// session which means it expires the session after 10 seconds.
deadline, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// ...
session, destroy := consul.WithSession(deadline, consul.Session{
  Name: "my session",
})
// By passing the session as parent context we can attach the session to
// multiple locks, if it expires, all those locks are released and their
// contexts are canceled.
lock1, release1 := consul.Lock(session, "key-1")
lock2, release2 := consul.Lock(session, "key-2")

# Packages

Package httpconsul provides extensions to integrate the standard net/http package with consul.

# Functions

Dial is a wrapper for calling (*Dialer).Dial on a default dialer.
DialContext is a wrapper for calling (*Dialer).DialContext on a default dialer.
Distance computes the approximate RTT between two Consul coordinates.
Listen creates a listener that accept connections on the given network and address, and registers to consul using the default client.
ListenContext creates a listener that accept connections on the given network and address, and registers to consul use the default client.
ListServices is a helper function that delegates to the default catalog.
Lock calls DefaultLocker.Lock.
LookupHost is a wrapper around the default resolver's LookupHost method.
LookupService is a wrapper around the default resolver's LookupService method.
MultiBalancer composes a new Balancer from a list of multiple balancers, each of them being called for each Balance call in the order that they were given to the function.
PreferEC2AvailabilityZone is a constructor for a balancer which prefers routing traffic to services registered in the same EC2 availability zone than the caller.
Shuffle is a sorting function that randomly rearranges the list of endpoints.
TryLockOne calls DefaultLocker.TryLockOne.
Watch is the package-level Watch definition which is called on DefaultWatcher.
WatchPrefix is the package-level WatchPrefix definition which is called on DefaultWatcher.
WeightedShuffle is a sorting function that randomly rearranges the list of endpoints, using the weightOf function to obtain the weight of each endpoint of the list.
WeightedShuffleOnRTT is a sorting function that randomly rearranges the list of endpoints, using the RTT as a weight to increase the chance of endpoints with low RTT to be placed at the front of the list.
WeightRTT returns the weight of the given endpoint based on it's RTT value.
WithSession constructs a copy of the context which is attached to a newly created session.

# Constants

No description provided by the author
DefaultAddress is the default consul agent address used when creating a consul client.
Delete describes the delete behavior, keys are deleted when the session that hold a lock on them expires.
Release describes the release behavior, locks are released on keys associated with an expired session.

# Variables

DefaultAgent is an agent configured to expose the agent information of the consul agent that the default client connects to.
DefaultCatalog is a catalog configured to use the default client.
DefaultClient is the default client used when none is specified.
DefaultLocker is the default Locker used by the package-level lock management functions.
DefaultResolver is the Resolver used by a Dialer when non has been specified.
DefaultTomography is used as the default Tomography instance when.
DefaultTransport is the default HTTP transport used by consul clients.
DefaultUserAgent is the default user agent used by consul clients when none has been set.
No description provided by the author
LocksKey is used to lookup the keys held by a lock from its associated context.
SessionKey is the key at which the Session value is stored in a context.
Unlocked is the error returned by contexts when the lock they were associated with has been lost.
WatchTransport is the same as DefaultTransport with a longer ResponseHeaderTimeout.

# Structs

Agent exposes methods to get information about the agent that a client is configured to connect to.
Catalog exposes methods to interract with the consul catalog.
A Client exposes an API for communicating with a consul agent.
The Coordinates type represents network coordinates of nodes in a consul system.
The Dialer type mirrors the net.Dialer API but uses consul to resolve service names to network addresses instead of DNS.
An Endpoint represents the address at which a service is available, coupled with the metadata associated with the service registration.
KeyData is a representation of a key in Consul, which follows the structure documented at https://www.consul.io/api/kv.html#read-key.
The Listener type contains options to create listeners that automatically register to consul.
A LoadBalancer is an implementation of Balancer which maintains a set of balancers that are local to each service name that Balance has been called for.
A Locker exposes methods for acquiring locks on keys of the consul key/value store.
NullBalancer is a balancer which doesn't modify the list of endpoints.
Param represents a single item in a query string.
A Resolver is a high-level abstraction on top of the consul service discovery API.
ResolverBlacklist implements a negative caching for Resolver instances.
The ResolverCache type provides the implementation of a caching layer for service name resolutions.
Rotator is the implementation of a load balancing algorithms similar to RoundRobin but which returns the full list of endpoints instead of a single one.
RoundRobin is the implementation of a simple load balancing algorithms which chooses and returns a single endpoint of the input list in a round robin fashion.
Session carries a session configuration, it is used in the WithSession function to customize the session properties.
Shuffler is a Balancer implementation which returns a randomly shuffled list of endpoints.
A Store exposes an API to interract with the consul key/value store.
Tomography exposes methods to fetch network coordinates information from consul.
Watcher is the struct upon which Watch and WatchPrefix are built.
WeightedShuffler is a Balancer implementation which shuffles the list of endpoints using a different weight for each endpoint.

# Interfaces

Balancer is the interface implemented by types that provides load balancing algorithms for a Resolver.

# Type aliases

BalancerFunc allows regular functions to be used as balancers.
LookupServiceFunc is the signature of functions that can be used to lookup service names.
NodeCoordinates is a mapping of node names to their consul coordinates.
PreferTags is a balancer which groups endpoints that match certain tags.
Query is a representation of a URL query string as a list of parameters.
SessionBehavior is an enumeration repesenting the behaviors of session expirations.
SessionID is a type representing unique session identifiers.
WatcherFunc is the function signature for the callback from watch.