routing

package
v0.18.13 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2023 License: Apache-2.0, MIT Imports: 20 Imported by: 44

Documentation

Overview

Package routing implements matching of http requests to a continuously updatable set of skipper routes.

Request Evaluation

1. The path in the http request is used to find one or more matching route definitions in a lookup tree.

2. The rest of the request attributes is matched against the non-path conditions of the routes found in the lookup tree, from the most to the least strict one. The result is the first route where every condition is met.

(The regular expression conditions for the path, 'PathRegexp', are applied only in step 2.)

The matching conditions and the built-in filters that use regular expressions, use the go stdlib regexp, which uses re2:

https://github.com/google/re2/wiki/Syntax

Matching Conditions

The following types of conditions are supported in the route definitions.

- Path: the route definitions may contain a single path condition, optionally with wildcards, used for looking up routes in the lookup tree.

- PathSubtree: similar to Path, but used to match full subtrees including the path of the definition.

- PathRegexp: regular expressions to match the path.

- Host: regular expressions that the host header in the request must match.

- Method: the HTTP method that the request must match.

- Header: a header key and exact value that must be present in the request. Note that Header("Key", "Value") is equivalent to HeaderRegexp("Key", "^Value$").

- HeaderRegexp: a header key and a regular expression, where the key must be present in the request and one of the associated values must match the expression.

Wildcards

Path matching supports two kinds of wildcards:

- simple wildcard: e.g. /some/:wildcard/path. Simple wildcards are matching a single name in the request path.

- freeform wildcard: e.g. /some/path/*wildcard. Freeform wildcards are matching any number of names at the end of the request path.

In case of PathSubtree, simple wildcards behave similar to Path, while freeform wildcards only set the name of the path parameter containing the path in the subtree. If no free wildcard is used in the PathSubtree predicate, the name of this parameter will be "*". This makes the PathSubtree("/foo") predicate equivalent to having routes with Path("/foo"), Path("/foo/") and Path("/foo/**") predicates.

Custom Predicates

It is possible to define custom route matching rules in the form of custom predicates. Custom predicates need to implement the PredicateSpec interface, that serves as a 'factory' and is used in the routing package during constructing the routing tree. When a route containing a custom predicate is matched based on the path tree, the predicate receives the request object, and it returns true or false meaning that the request is a match or not.

Data Clients

Routing definitions are not directly passed to the routing instance, but they are loaded from clients that implement the DataClient interface. The router initially loads the complete set of the routes from each client, merges the different sets based on the route id, and converts them into their runtime representation, with initialized filters based on the filter specifications in the filter registry.

During operation, the router regularly polls the data clients for updates, and, if an update is received, generates a new lookup tree. In case of communication failure during polling, it reloads the whole set of routes from the failing client.

The active set of routes from the last successful update are used until the next successful update happens.

Currently, the routes with the same id coming from different sources are merged in an nondeterministic way, but this behavior may change in the future.

For a full description of the route definitions, see the documentation of the skipper/eskip package.

Example
package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/zalando/skipper/filters/builtin"
	"github.com/zalando/skipper/routing"
	"github.com/zalando/skipper/routing/testdataclient"
)

func main() {
	// create a data client with a predefined route:
	dataClient, err := testdataclient.NewDoc(
		`Path("/some/path/to/:id") -> requestHeader("X-From", "skipper") -> "https://www.example.org"`)
	if err != nil {
		log.Fatal(err)
	}
	defer dataClient.Close()

	// create a router:
	r := routing.New(routing.Options{
		FilterRegistry:  builtin.MakeRegistry(),
		MatchingOptions: routing.IgnoreTrailingSlash,
		DataClients:     []routing.DataClient{dataClient}})
	defer r.Close()

	// let the route data get propagated in the background:
	time.Sleep(36 * time.Millisecond)

	// create a request:
	req, err := http.NewRequest("GET", "https://www.example.com/some/path/to/Hello,+world!", nil)
	if err != nil {
		log.Fatal(err)
	}

	// match the request with the router:
	route, params := r.Route(req)
	if route == nil {
		log.Fatal("failed to route")
	}

	// verify the matched route and the path params:
	fmt.Println(route.Backend)
	fmt.Println(params["id"])

}
Output:

https://www.example.org
Hello, world!

Index

Examples

Constants

View Source
const (
	// Deprecated, use predicates.PathName instead
	PathName = predicates.PathName

	// Deprecated, use predicates.PathSubtreeName instead
	PathSubtreeName = predicates.PathSubtreeName

	// Deprecated, use predicates.WeightName instead
	WeightPredicateName = predicates.WeightName

	RoutesCountName = "X-Count"
)

Variables

This section is empty.

Functions

func FromContext added in v0.16.58

func FromContext[K comparable, V any](ctx context.Context, key K, defaultValue func() V) V

FromContext returns value from the routing context stored in ctx. It returns value associated with the key or stores result of the defaultValue call. defaultValue may be called multiple times but only one result will be used as a default value.

func NewContext added in v0.16.58

func NewContext(ctx context.Context) context.Context

NewContext returns a new context with associated routing context. It does nothing and returns ctx if it already has associated routing context.

Types

type DataClient

type DataClient interface {
	LoadAll() ([]*eskip.Route, error)
	LoadUpdate() ([]*eskip.Route, []string, error)
}

DataClient instances provide data sources for route definitions.

type EndpointRegistry added in v0.17.41

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

func NewEndpointRegistry added in v0.17.41

func NewEndpointRegistry(o RegistryOptions) *EndpointRegistry

func (*EndpointRegistry) DecInflightRequest added in v0.17.41

func (r *EndpointRegistry) DecInflightRequest(key string)

func (*EndpointRegistry) Do added in v0.17.41

func (r *EndpointRegistry) Do(routes []*Route) []*Route

func (*EndpointRegistry) GetMetrics added in v0.17.41

func (r *EndpointRegistry) GetMetrics(key string) Metrics

func (*EndpointRegistry) IncInflightRequest added in v0.17.41

func (r *EndpointRegistry) IncInflightRequest(key string)

func (*EndpointRegistry) SetDetectedTime added in v0.17.41

func (r *EndpointRegistry) SetDetectedTime(key string, detected time.Time)

type LBAlgorithm added in v0.10.168

type LBAlgorithm interface {
	Apply(*LBContext) LBEndpoint
}

LBAlgorithm implementations apply a load balancing algorithm over the possible endpoints of a load balanced route.

type LBContext added in v0.10.234

type LBContext struct {
	Request  *http.Request
	Route    *Route
	Params   map[string]interface{}
	Registry *EndpointRegistry
}

LBContext is used to pass data to the load balancer to decide based on that data which endpoint to call from the backends

func NewLBContext added in v0.10.234

func NewLBContext(r *http.Request, rt *Route) *LBContext

NewLBContext is used to create a new LBContext, to pass data to the load balancer algorithms. Deprecated: create LBContext instead

type LBEndpoint added in v0.10.168

type LBEndpoint struct {
	Scheme, Host string
	Metrics      *LBMetrics

	// Detected represents the time when skipper instances first detected a new LB endpoint. This detection
	// time is used for the fade-in feature of the round-robin and random LB algorithms.
	Detected time.Time
}

LBEndpoint represents the scheme and the host of load balanced backends.

type LBMetrics added in v0.11.177

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

LBMetrics contains metrics used by LB algorithms

func (*LBMetrics) DecInflightRequest added in v0.11.177

func (m *LBMetrics) DecInflightRequest()

DecInflightRequest decrements the number of outstanding requests from the proxy to a given backend.

func (*LBMetrics) GetInflightRequests added in v0.11.177

func (m *LBMetrics) GetInflightRequests() int

GetInflightRequests decrements the number of outstanding requests from the proxy to a given backend.

func (*LBMetrics) IncInflightRequest added in v0.11.177

func (m *LBMetrics) IncInflightRequest()

IncInflightRequest increments the number of outstanding requests from the proxy to a given backend.

type MatchingOptions

type MatchingOptions uint

MatchingOptions controls route matching.

const (
	// MatchingOptionsNone indicates that all options are default.
	MatchingOptionsNone MatchingOptions = 0

	// IgnoreTrailingSlash indicates that trailing slashes in paths are ignored.
	IgnoreTrailingSlash MatchingOptions = 1 << iota
)

type Metrics added in v0.17.41

type Metrics interface {
	DetectedTime() time.Time
	InflightRequests() int64
}

Metrics describe the data about endpoint that could be used to perform better load balancing, fadeIn, etc.

type Options

type Options struct {

	// Registry containing the available filter
	// specifications that are used during processing
	// the filter chains in the route definitions.
	FilterRegistry filters.Registry

	// Matching options are flags that control the
	// route matching.
	MatchingOptions MatchingOptions

	// The timeout between requests to the data
	// clients for route definition updates.
	PollTimeout time.Duration

	// The set of different data clients where the
	// route definitions are read from.
	DataClients []DataClient

	// Specifications of custom, user defined predicates.
	Predicates []PredicateSpec

	// Performance tuning option.
	//
	// When zero, the newly constructed routing
	// tree will take effect on the next routing
	// query after every update from the data
	// clients. In case of higher values, the
	// routing queries have priority over the
	// update channel, but the next routing tree
	// takes effect only a few requests later.
	//
	// (Currently disabled and used with hard wired
	// 0, until the performance benefit is verified
	// by benchmarks.)
	UpdateBuffer int

	// Set a custom logger if necessary.
	Log logging.Logger

	// SuppressLogs indicates whether to log only a summary of the route changes.
	SuppressLogs bool

	// Metrics is used to collect monitoring data about the routes health, including
	// total number of routes applied and UNIX time of the last routes update.
	Metrics metrics.Metrics

	// PreProcessors contains custom eskip.Route pre-processors.
	PreProcessors []PreProcessor

	// PostProcessors contains custom route post-processors.
	PostProcessors []PostProcessor

	// SignalFirstLoad enables signaling on the first load
	// of the routing configuration during the startup.
	SignalFirstLoad bool
}

Options for initialization for routing.

type PostProcessor added in v0.9.164

type PostProcessor interface {
	Do([]*Route) []*Route
}

PostProcessor is an interface for custom post-processors applying changes to the routes after they were created from their data representation and before they were passed to the proxy.

type PreProcessor added in v0.10.234

type PreProcessor interface {
	Do([]*eskip.Route) []*eskip.Route
}

PreProcessor is an interface for custom pre-processors applying changes to the routes before they were created from eskip.Route representation.

type Predicate

type Predicate interface {

	// Returns true if the request matches the predicate.
	Match(*http.Request) bool
}

Predicate instances are used as custom user defined route matching predicates.

type PredicateSpec

type PredicateSpec interface {

	// Name of the predicate as used in the route definitions.
	Name() string

	// Creates a predicate instance with concrete arguments.
	Create([]interface{}) (Predicate, error)
}

PredicateSpec instances are used to create custom predicates (of type Predicate) with concrete arguments during the construction of the routing tree.

type RegistryOptions added in v0.17.41

type RegistryOptions struct {
}

type Route

type Route struct {

	// Fields from the static route definition.
	eskip.Route

	// The backend scheme and host.
	Scheme, Host string

	// The preprocessed custom predicate instances.
	Predicates []Predicate

	// The preprocessed filter instances.
	Filters []*RouteFilter

	// LBEndpoints contain the possible endpoints of a load
	// balanced route.
	LBEndpoints []LBEndpoint

	// LBAlgorithm is the selected load balancing algorithm
	// of a load balanced route.
	LBAlgorithm LBAlgorithm

	// LBFadeInDuration defines the duration of the fade-in
	// function to be applied to new LB endpoints associated
	// with this route.
	LBFadeInDuration time.Duration

	// LBExponent defines a secondary exponent modifier of
	// the fade-in function configured mainly by the LBFadeInDuration
	// field, adjusting the shape of the fade-in. By default,
	// its value is usually 1, meaning linear fade-in, and it's
	// configured by the post-processor found in the filters/fadein
	// package.
	LBFadeInExponent float64
	// contains filtered or unexported fields
}

Route object with preprocessed filter instances.

type RouteFilter

type RouteFilter struct {
	filters.Filter
	Name string

	// Deprecated: currently not used, and post-processors may not maintain a correct value
	Index int
}

RouteFilter contains extensions to generic filter interface, serving mainly logging/monitoring purpose.

type RouteLookup added in v0.9.164

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

RouteLookup captures a single generation of the lookup tree, allowing multiple lookups to the same version of the lookup tree.

Experimental feature. Using this solution potentially can cause large memory consumption in extreme cases, typically when: the total number routes is large, the backend responses to a subset of these routes is slow, and there's a rapid burst of consecutive updates to the routing table. This situation is considered an edge case, but until a protection against is found, the feature is experimental and its exported interface may change.

func (*RouteLookup) Do added in v0.9.164

func (rl *RouteLookup) Do(req *http.Request) (*Route, map[string]string)

Do executes the lookup against the captured routing table. Equivalent to Routing.Route().

type Routing

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

Routing ('router') instance providing live updatable request matching.

func New

func New(o Options) *Routing

New initializes a routing instance, and starts listening for route definition updates.

func (*Routing) Close

func (r *Routing) Close()

Close closes routing, routeTable and stops statemachine for receiving routes.

func (*Routing) FirstLoad added in v0.10.113

func (r *Routing) FirstLoad() <-chan struct{}

FirstLoad, when enabled, blocks until the first routing configuration was received by the routing during the startup. When disabled, it doesn't block.

func (*Routing) Get added in v0.9.164

func (r *Routing) Get() *RouteLookup

Get returns a captured generation of the lookup table. This feature is experimental. See the description of the RouteLookup type.

func (*Routing) Route

func (r *Routing) Route(req *http.Request) (*Route, map[string]string)

Route matches a request in the current routing tree.

If the request matches a route, returns the route and a map of parameters constructed from the wildcard parameters in the path condition if any. If there is no match, it returns nil.

func (*Routing) ServeHTTP added in v0.9.70

func (r *Routing) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP renders the list of current routes.

type WeightedPredicateSpec added in v0.13.241

type WeightedPredicateSpec interface {
	PredicateSpec

	// Extra Weight of the predicate
	Weight() int
}

Directories

Path Synopsis
Package testdataclient provides a test implementation for the DataClient interface of the skipper/routing package.
Package testdataclient provides a test implementation for the DataClient interface of the skipper/routing package.

Jump to

Keyboard shortcuts

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