Git Product home page Git Product logo

gsr's Introduction

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)

gsr's People

Contributors

jaypipes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

leobcn manson

gsr's Issues

Switch from rkt to a docker-compose system for examples/testing

The examples/ directory contains some Bash scripts that launch an etcd container and some example web and data access service containers using rkt. Let's switch those over to use Docker Compose, which is probably more familiar to most folks than rkt.

Allow configurable logging

Now that we no longer use the global log setup and instead have a registryLogs struct that we can hang loggers off, we should add some configurability around logging.

I'm thinking something like this:

GSR_LOG_MODE environment variable would be a string like "Ldate | Ltime | Lshortfile" that would allow the user to enable a particular log record format and align gsr's logging with their own application's needs/expectations.

Use gofmt

Even though I am not a fan of the canonical Go code style, gsr is a Go library and should follow go code formatting guidelines. Therefore, I need to update my vimrc for .go files and use gofmt for reformatting the existing files.

Add tests for TLS setup

Need to test the TLS mode and configuration options. Use a self-signed cert for basic tests.

Switch to dep

Now that dep is the vendor/dependency tool of choice, switch to it...

Move to a no-global-vars architecture

Remove all global vars from gsr. No global loggers or configuration options. Use a Config struct, passed in to the New() function and allow the overriding of log handlers.

Create `Registry.Unregister()` method

The gsr.Registry.Register() method is used to register some running code as a service endpoint in gsr. We need a corresponding method to de-register the same service endpoint. This Unregister() method can then be called in a SIGTERM handler.

test high-frequency register/unregister

Develop an end-to-end test that spins up a single backing etcd store container and multiple data and web example containers, each registering themselves with gsr. Have the test script call docker stop on many of the data and web containers and query the remaining ones that the expected endpoints are still what they should be, even with many unregisters that happen on docker stop (SIGTERM) of other endpoint containers.

Build and test with Docker

Build the gsr library and test it with Docker instead of the custom rkt-based scripts currently included.

Clean up logging

Determine whether to go with an open source structured logging package or standardize the tri-level + ERROR logging package that I've been using for most application projects.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.