pylon

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

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

Go to latest
Published: Jun 13, 2020 License: MIT Imports: 16 Imported by: 0

README

Pylon Build Status

A Go Reverse Proxy and Load balancer

Usage

You just need a config, for example:

{
  "servers": [
    {
      "name": "server1",
      "port": 7777,
      "monitoring_route": "/health",
      "services": [
        {
          "name": "Billing Service",
          "route_prefix": "/microservice/",
          "instances": [
            {
              "host": "127.0.0.1:1111",
              "weight": 3
            },
            {
              "host": "127.0.0.1:2222"
            },
            {
              "host": "127.0.0.1:3333"
            }
          ],
          "balancing_strategy": "round_robin",
          "max_connections": 300,
          "health_check": {
            "enabled": true,
            "interval": 30,
            "dial_timeout": 2
          }
        }
      ]
    }
  ]
}

Notes:

  • Pylon supports regular expressions, you can replace "route_prefix": "/microservice/" with "route_pattern": "/microservice/*" or any valid regular expression

  • Strategies are specified with the "balancing_strategy" tag. Options are: "round_robin" / "least_connected" / "random"

  • All durations are in second.

  • If "max_connections" isn't specified or set to 0, the default max connections is 100 000

Simply use Pylon in your library or main like this:

package main

import (
	"github.com/leonardbesson/pylon"
	"log"
	"os"
)

func main() {
    	f, err := os.OpenFile("pylon.log", os.O_RDWR | os.O_CREATE | os.O_TRUNC, 0666)
	if err != nil {
		log.Fatalf("error opening file: %v", err)
	}
	defer f.Close()
	
	// Redirect the logging to a file
	pylon.SetLogWriter(f)
	// Set the logging levels
	pylon.SetLogLevels(pylon.LOG_ERROR | pylon.LOG_INFO)
	// Specify a custom logging function (default is log.Println prefixed with (ERROR|DEBUG|INFO|VERBO)
	pylon.SetInfoLogger(log.Println)
	pylon.SetErrorLogger(log.Println)
	pylon.SetVerboseLogger(log.Println)
	
	log.Fatal(pylon.ListenAndServe("./config.json"))
}

Executables

If you don't want to use Pylon in your own library or main, I have put pre-compiled executables (Win x64 and Linux x64) running a simple main in the "Release" section.

Launch it with the config file named "config.json" in the same directory and it will create a log file named "pylon.log". You can also specify the config and log files, for example:

 ./pylon_linux_x64 -c /path/to/config.json -log /your/log/file

Documentation

Overview

A simple reverse proxy and load balancer

supported balancing strategies:

  • Round Robin
  • Random
  • Least Connected

Example of a json config:

{
  "servers": [
    {
      "name": "server1",
      "port": 7777,
      "services": [
        {
          "route_prefix": "/microservice/",
          "instances": [
            {
              "host": "127.0.0.1:1111",
              "weight": 3
            },
            {
              "host": "127.0.0.1:2222"
            },
            {
              "host": "127.0.0.1:3333"
            }
          ],
          "balancing_strategy": "round_robin",
          "max_connections": 300,
          "health_check": {
            "enabled": true,
            "interval": 30
          }
        }
      ]
    }
  ]
}

Index

Constants

View Source
const (
	LOG_DEBUG          int8 = 1 << 0
	LOG_ERROR          int8 = 1 << 1
	LOG_INFO           int8 = 1 << 2
	LOG_VERBOSE        int8 = 1 << 3
	LOG_NONE           int8 = 0
	LOG_EXCEPT_VERBOSE int8 = LOG_DEBUG | LOG_ERROR | LOG_INFO
	LOG_ALL            int8 = LOG_DEBUG | LOG_ERROR | LOG_INFO | LOG_VERBOSE
)
View Source
const (
	ErrServiceNoRouteCode = iota + 30
	ErrInvalidRouteTypeCode
	ErrRouteNoRouteCode
	ErrServiceNoInstanceCode
	ErrAllInstancesDownCode
	ErrInvalidStrategyCode
	ErrFailedRoundRobinCode
	ErrInvalidRouteRegexCode
	ErrInvalidHostCode
)

Variables

View Source
var (
	ErrServiceNoRoute    = NewError(ErrServiceNoRouteCode, "Service has no route")
	ErrInvalidRouteType  = NewError(ErrInvalidRouteTypeCode, "Route has invalid type")
	ErrServiceNoInstance = NewError(ErrServiceNoInstanceCode, "Service has no instances")
	ErrAllInstancesDown  = NewError(ErrAllInstancesDownCode, "All instances are dead")
	ErrFailedRoundRobin  = NewError(ErrFailedRoundRobinCode, "No instance can be round robin picked")
)

Functions

func ListenAndServe

func ListenAndServe(p string) error

ListenAndServe tries to parse the config at the given path and serve it

func ListenAndServeConfig

func ListenAndServeConfig(c *Config) error

ListenAndServeConfig converts a given config to an exploitable structure (MicroService) and serves them

func NewProxy

func NewProxy() *httputil.ReverseProxy

func NewPylonHandler

func NewPylonHandler(p *Pylon) http.HandlerFunc

NewPylonHandler returns a func(w http.ResponseWriter, r *http.Request) that will handle incoming requests to the given Pylon

func NewPylonHealthHandler

func NewPylonHealthHandler(p *Pylon) http.HandlerFunc

NewPylonHealthHandler returns a func(w http.ResponseWriter, r *http.Request) that will collect and render some stats about the given Pylon: (Name / Strategy / Current request count) For every instance: (UP or DOWN / Host / Weight / Current request count)

func SetDebugLogger

func SetDebugLogger(l Logger)

func SetErrorLogger

func SetErrorLogger(l Logger)

func SetInfoLogger

func SetInfoLogger(l Logger)

func SetLogLevels

func SetLogLevels(mask int8)

func SetLogWriter

func SetLogWriter(w io.Writer)

func SetVerboseLogger

func SetVerboseLogger(l Logger)

Types

type Config

type Config struct {
	Servers []Server `json:"servers"`
}

type ConfigParser

type ConfigParser interface {
	Parse(r io.Reader) (*Config, error)
}

type Error

type Error struct {
	Code    int
	Message string
}

func NewError

func NewError(code int, message string) *Error

func (*Error) Error

func (e *Error) Error() string

type HealthCheck

type HealthCheck struct {
	Enabled  bool `json:"enabled"`
	Interval int  `json:"interval"`
	DialTO   int  `json:"dial_timeout"`
}

type Instance

type Instance struct {
	Host     string  `json:"host"`
	Weight   float32 `json:"weight"`
	ReqCount chan int
	RRPos    SharedInt
}

type InstanceRender

type InstanceRender struct {
	Up       bool
	Host     string
	Weight   float32
	CurrConn int
}

type JSONConfigParser

type JSONConfigParser struct {
}

func (*JSONConfigParser) Parse

func (p *JSONConfigParser) Parse(r io.Reader) (c *Config, err error)

func (*JSONConfigParser) ParseFromPath

func (p *JSONConfigParser) ParseFromPath(path string) (c *Config, err error)

type Logger

type Logger func(...interface{})

type MicroService

type MicroService struct {
	Name        string
	Route       Route
	Instances   []*Instance
	Strategy    Strategy
	LastUsedIdx SharedInt
	BlackList   map[int]bool
	ReqCount    chan int
	// Caching the weight sum for faster retrieval
	WeightSum   float32
	Mutex       *sync.RWMutex
	HealthCheck HealthCheck
}

func NewMicroService

func NewMicroService(s *Service) (*MicroService, error)

NewMicroService returns a new MicroService object given a Service

type PrefixRoute

type PrefixRoute struct {
	Prefix string
}

func (PrefixRoute) Data

func (p PrefixRoute) Data() interface{}

func (PrefixRoute) Type

func (p PrefixRoute) Type() RouteType

type ProxyPool

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

func NewProxyPool

func NewProxyPool(capacity int) *ProxyPool

func (*ProxyPool) Get

func (p *ProxyPool) Get() *httputil.ReverseProxy

func (*ProxyPool) Put

func (p *ProxyPool) Put(rp *httputil.ReverseProxy)

type Pylon

type Pylon struct {
	Services []*MicroService
}

func NewPylon

func NewPylon(s *Server) (*Pylon, error)

NewPylon returns a new Pylon object given a Server

type PylonTransport

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

func NewPylonTransport

func NewPylonTransport() *PylonTransport

func (*PylonTransport) RoundTrip

func (pt *PylonTransport) RoundTrip(req *http.Request) (*http.Response, error)

type RegexRoute

type RegexRoute struct {
	Regex *regexp.Regexp
}

func (RegexRoute) Data

func (r RegexRoute) Data() interface{}

func (RegexRoute) Type

func (r RegexRoute) Type() RouteType

type Route

type Route interface {
	Type() RouteType
	Data() interface{}
}

type RouteType

type RouteType int8
const (
	Regex  RouteType = iota
	Prefix RouteType = iota
)

type Server

type Server struct {
	Name        string    `json:"name"`
	Port        int       `json:"port"`
	HealthRoute string    `json:"monitoring_route"`
	Services    []Service `json:"services"`
}

type Service

type Service struct {
	Name        string      `json:"name"`
	Pattern     string      `json:"route_pattern"`
	Prefix      string      `json:"route_prefix"`
	Instances   []Instance  `json:"instances"`
	Strategy    Strategy    `json:"balancing_strategy"`
	MaxCon      int         `json:"max_connections"`
	HealthCheck HealthCheck `json:"health_check"`
}

type ServiceRender

type ServiceRender struct {
	Name      string
	CurrConn  int
	Strat     Strategy
	Instances []InstanceRender
}

type SharedInt

type SharedInt struct {
	*sync.RWMutex
	// contains filtered or unexported fields
}

func NewSharedInt

func NewSharedInt(v int) SharedInt

func (*SharedInt) Get

func (c *SharedInt) Get() int

func (*SharedInt) Set

func (c *SharedInt) Set(v int) int

type Strategy

type Strategy string
const (
	RoundRobin     Strategy = "round_robin"
	LeastConnected Strategy = "least_connected"
	Random         Strategy = "random"
)

Jump to

Keyboard shortcuts

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