gsr

package module
v0.0.0-...-b1cf986 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2018 License: Apache-2.0 Imports: 15 Imported by: 1

README

gsr - The Golang Service Registry library

gsr is a Golang library that can be used to provide service registration and discovery capabilities to Golang applications. gsr's library interfaces are simple to use, simple to reason about, and importantly, do not require a particular container runtime.

Overview

gsr uses etcd for storing its service registry. Within the etcd store, gsr sets up a series of keys representing services that have registered with gsr. The structure of the gsr registry in etcd looks like so:

$KEY_PREFIX <-- environ['GSR_KEY_PREFIX']
|
-> /services
   |
   -> /$SERVICE
       |
       -> /$ENDPOINT1
       -> /$ENDPOINT2

As an example, let's imagine you have a distributed system deployment composed of three different service applications:

  • web
  • data-access
  • background

Each of the above applications is a Golang application that is built to run within a container, a VM, on baremetal, whatever. Assume for now that you only deploy a single instance of each of the above service applications, and they end up running at the following addresses:

  • web: 172.16.28.23:80
  • data-access: 172.16.28.24:10000
  • background: 172.16.28.25:10000

Assuming that your GSR_KEY_PREFIX environment variable is "gsr", the gsr registry in etcd would look like this:

/gsr
|
-> /services
   |
   -> /web
       |
       -> 127.16.28.23:80

   -> /data-access
       |
       -> 127.16.28.24:10000

   -> /background
       |
       -> 127.16.28.25:10000

Usage

gsr can be used for both service discovery and service registration.

Service discovery

If you have a Golang application that needs to look up information about a required service that the application makes use of, you need service discovery capabilities.

The gsr.Registry struct can be used to discover information about services and their endpoints, as this example shows:

package main

import (
    "log"

    "github.com/jaypipes/gsr"

    "github.com/exampleorg/data"
)

func main() {
    // Creates a new gsr.Registry instance that is connected to the gsr etcd
    // registry
    sr, err := gsr.New()
    if err != nil {
         log.Fatalf("Failed to connect to gsr: %v.", err)
    }

    var dbConn *data.Connection

    // Our application depends on a service called "data-access", so let's find
    // the endpoints the service has so that we can query for data
    dataEps := sr.Endpoints("data-access")
    for _, ep := range(dataEps) {
    // Try connecting to the data access service. This code is an example.
    // Your service access code might look very different...
        if dbConn, err := data.connect(ep.Address); err != nil {
            log.Printf("Failed to connect to data access service: %v", err)
        }
    }
}

This strategy allows you to forego injecting service and endpoint configuration into environment variables of configuration files.

Service registration

If you have a service application written in Golang, upon startup, you want the service to register itself with some system in order to allow other services to discover it. What you need is service registration functionality.

The gsr.Registry struct can be used to register a service endpoint, as this example shows:

package main

import (
    "log"
    "net"
    "strings"

    "github.com/jaypipes/gsr"
)

const (
    myServiceName = "my-whizzbang-service"
)

var (
    myAddr = bindHost()
)

func main() {
    // Creates a new gsr.Registry instance that is connected to the gsr etcd
    // registry
    sr, err := gsr.New()
    if err != nil {
         log.Fatalf("Failed to connect to gsr: %v.", err)
    }

    ep := gsr.Endpoint{
        Service: &gsr.Service{myServiceName},
        Address: myAddr,
    }

    err := sr.Register(&ep)
    if err != nil {
        log.Fatalf("unable to register %v with gsr: %v", ep, err)
    }
}

func bindHost() string {
    c, err := net.Dial("udp", "8.8.8.8:80")
    if err != nil {
        log.Fatal(err)
    }
    defer c.Close()
    addr := c.LocalAddr().String()
    return addr[:strings.LastIndex(addr, ":")]
}
Service de-registration

Application services typically want to remove themselves from the gsr registry when the application container or process receives a SIGTERM signal (as is the case when, e.g. a docker stop $CONTAINER command is called). The gsr.Registry.Unregister() function can be called from a signal trap to fast-notify other gsr.Registry structs contained in other service endpoint code that a particular endpoint should be removed from their endpoints list.

Here is some example code you can use in your own application to trap SIGTERM and call the gsr.Registry.Unregister() method appropriately:

    sigs := make(chan os.Signal, 1)
    done := make(chan bool, 1)
    signal.Notify(sigs, syscall.SIGTERM)
    go func() {
        sig := <-sigs
        fmt.Printf("received %s. unregistering %s:%s endpoint in gsr\n", sig, myServiceName, myAddr)
        err := reg.Unregister(&ep)
        if err != nil {
            log.Fatalf("failed to unregister: %s\n", err)
        }
        done <- true
    }()

    // Wait for signal...
    <-done

Need more example code?

If you need more example code, please check out the README.md in the examples/ directory.

Configuring

In the spirit of 12-factor apps, gsr can be configured entirely by setting environment variables. Here is a list of environment variables that influence gsr's behaviour:

  • GSR_ETCD_ENDPOINTS: a comma-separated list of etcd endpoints gsr will look for an etcd K/V store. (default: http://127.0.0.1:2379)

  • GSR_KEY_PREFIX: a string indicating the prefix of keys in etcd where gsr will store service and endpoint information. (default: '')

  • GSR_ETCD_CONNECT_TIMEOUT_SECONDS: the number of seconds to attempt to connect to the etcd when calling gsr.New(). (default: 300)

  • GSR_ETCD_DIAL_TIMEOUT_SECONDS: the number of seconds to use as the dial timeout when attempting to connect to an etcd endpoint. (default: 1)

  • GSR_ETCD_REQUEST_TIMEOUT_SECONDS: the number of seconds to set each etcd request timeout to, once connected. (default: 1)

  • GSR_USE_TLS: 0 (default) if communication with etcd should be secured with TLS.

  • GSR_TLS_CERT_PATH: Path to the certificate file to use if TLS is used. (default: "/etc/gsr/server.pem")

  • GSR_TLS_KEY_PATH: Path to the private key file to use if TLS is used. (default: "/etc/gsr/server.key")

  • GSR_LOG_LEVEL: an integer representing the verbosity of logging. The higher the number, the more verbose. (default: 0 almost no output during normal operation)

  • GSR_LOG_MICROSECONDS: a boolean that sets up microsecond-precision log record timestamps (default: false)

  • GSR_LOG_FILE_TRACE: a boolean that includes source file and line number in log messages (default: false)

  • GSR_LEASE_SECONDS: an integer representing the number of seconds gsr should use when writing endpoint information into the registry. (default: 60)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	EtcdEndpoints             []string
	EtcdKeyPrefix             string
	EtcdConnectTimeoutSeconds time.Duration
	EtcdRequestTimeoutSeconds time.Duration
	EtcdDialTimeoutSeconds    time.Duration
	UseTLS                    bool
	TLSCertPath               string
	TLSKeyPath                string
	LogLevel                  int
	LogMicroseconds           bool
	LogFileTrace              bool
	LeaseSeconds              int64
}

func (*Config) EtcdConfig

func (c *Config) EtcdConfig() *etcd.Config

Returns an etcd configuration struct populated with all configured options.

func (*Config) TLSConfig

func (c *Config) TLSConfig() *tls.Config

Returns the TLS configuration struct to use with etcd client.

type Endpoint

type Endpoint struct {
	Service *Service
	Address string
	// contains filtered or unexported fields
}

type Heartbeat

type Heartbeat struct {
	// contains filtered or unexported fields
}

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

func New

func New() (*Registry, error)

Creates a new gsr.Registry object, registers a service and endpoint with the registry, and returns the registry object.

func (*Registry) Endpoints

func (r *Registry) Endpoints(service string) []*Endpoint

Returns a list of endpoints for a requested service type.

func (*Registry) L1

func (r *Registry) L1(message string, args ...interface{})

func (*Registry) L2

func (r *Registry) L2(message string, args ...interface{})

func (*Registry) LERR

func (r *Registry) LERR(message string, args ...interface{})

func (*Registry) Register

func (r *Registry) Register(ep *Endpoint) error

Registers an endpoint for a service type and sets up all necessary heartbeat and watch mechanisms.

func (*Registry) Unregister

func (r *Registry) Unregister(ep *Endpoint) error

Unregister removes an endpoint from the gsr registry. It is typically called from a SIGTERM signal handler to short-circuit the automatic heartbeat that keeps endpoints "alive" in gsr.

type Service

type Service struct {
	Name string
}

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL