fuddle

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 17, 2023 License: GPL-3.0 Imports: 8 Imported by: 3

README

Fuddle Go

Go Reference CI Workflow

A Go SDK for the Fuddle service registry, which lets nodes register themselves and discover other nodes in the cluster.

Usage

Installation

go get github.com/fuddle-io/fuddle-go

Usage

See the package documentation for SDK usage, and Fuddle for documentation on the Fuddle registry.

⚠ Limitations

Fuddle is still in early stages of development so has a number of limitations.

See Fuddle for details.

Documentation

Overview

Package fuddle implements an SDK client for the Fuddle service registry.

Clients use the SDK to register themselves with Fuddle and discover nodes and services in the cluster.

Register

Nodes register by passing their registered node state and a list of Fuddle node seed addresses to Register:

registry, err := fuddle.Register(
	// List of Fuddle seed addresses.
	[]string{"192.168.1.1:8220", "192.168.1.2:8220", "192.168.1.3:8220"},
	fuddle.Node{
		// Attributes describing the local node.
		ID:       "orders-32eaba4e",
		Service:  "orders",
		Locality: "aws.us-east-1-b",
		Created:  time.Now().UnixMilli(),
		Revision: "v5.1.0-812ebbc",

		// Application defined metadata to share as part of service discovery.
		Metadata: map[string]string{
			"status":           "booting",
			"addr.rpc.ip":      "192.168.2.1",
			"addr.rpc.port":    "5562",
			"protocol.version": "3",
		},
	},
)

This will register the local node so it can be discovered by other nodes in the cluster.

The node state includes a set of attributes that can be used for service discovery, such as looking up the nodes in the order service in us-east-1, and observability, such as checking the revision and start time of nodes that are unhealthy. The attributes are immutable so cannot be changed during the lifetime of the node.

Nodes also include a map of application defined metadata which is shared with other nodes in the cluster. Such as routing information and protocol version. This metadata may be updated using registry.UpdateLocalMetadata, and the update will be propagated to the other nodes.

Remember to unregister the node with registry.Unregister() when it is shutdown to gracefully leave the cluster. Otherwise Fuddle will consider the node as failed rather than unregistered.

Cluster Discovery

Once a node has registered, it will receive the state of the other nodes in the cluster and stream updates about changes to the nodes in the registry. Therefore it maintains its own eventually-consistent view of the cluster.

This cluster state can be queried to receive the set of nodes matching some filter. Users can also subscribe to updates when the registry is updated, due to nodes joining, leaving or updating their metadata.

Lookup a set of nodes:

registry.Nodes(opts)

Subscribe to changes in a set of nodes:

registry.Subscribe(callback, opts)

Note when subscribing the callback will fire immediately with the matching cluster state, so theres no need to call Nodes first.

Filters

Queries and subscriptions can filter the set of nodes they are interested in based on service name, locality and metadata.

The service, locality and metadata field formats are all user defined, however it is recommended to structure as a hierarchy with some delimiter like a dot to make it easy to filter using wildcards.

Such as using a format '<provider>.<availability zone>' for the locality lets you filter either availability zones ('aws.us-east-1-a'), regions ('aws.us-east-1-*') or location ('aws.eu-*').

Wildcards can be used for the service name, locality and metadata values (though not metadata keys). The locality and metadata filters also support multiple possible values.

For example to filter only order service nodes in us-east-1 whose status is 'active' and protocol version is either 2 or 3:

filter := fuddle.Filter{
	"order": {
		Locality: []string{"aws.us-east-1-*"},
		Metadata: fuddle.MetadataFilter{
			"status":           []string{"active"},
			"protocol.version": []string{"2", "3"},
		},
	},
}

// Lookup the set of nodes matching the filter.
registry.Nodes(fuddle.WithFilter(filter))

// Subscribe to updates in the set of nodes matching the filter.
registry.Subscribe(callback, fuddle.WithFilter(filter))
Example (LookupOrdersServiceNodes)

Registers a 'frontend' service and queries the set of active order service nodes in us-east-1.

package main

import (
	"fmt"

	fuddle "github.com/fuddle-io/fuddle-go"
)

func main() {
	registry, err := fuddle.Register(
		// Seed addresses of Fuddle servers.
		[]string{"192.168.1.1:8220", "192.168.1.2:8220", "192.168.1.3:8220"},
		fuddle.Node{
			// ...
		},
	)
	if err != nil {
		// handle err ...
	}
	defer registry.Unregister()

	// Filter to only include order service nodes in us-east-1 whose status
	// is active and protocol version is either 2 or 3.
	orderNodes := registry.Nodes(fuddle.WithFilter(fuddle.Filter{
		"order": {
			Locality: []string{"aws.us-east-1-*"},
			Metadata: fuddle.MetadataFilter{
				"status":           []string{"active"},
				"protocol.version": []string{"2", "3"},
			},
		},
	}))
	addrs := []string{}
	for _, node := range orderNodes {
		addr := node.Metadata["addr.rpc.ip"] + ":" + node.Metadata["addr.rpc.port"]
		addrs = append(addrs, addr)
	}

	// ...

	fmt.Println("order service:", addrs)
}
Output:

Example (RegisterOrdersServiceNode)

Registers an 'orders' service node in 'us-east-1-b'.

package main

import (
	"time"

	fuddle "github.com/fuddle-io/fuddle-go"
)

func main() {
	registry, err := fuddle.Register(
		// Seed addresses of Fuddle servers.
		[]string{"192.168.1.1:8220", "192.168.1.2:8220", "192.168.1.3:8220"},
		fuddle.Node{
			ID:       "orders-32eaba4e",
			Service:  "orders",
			Locality: "aws.us-east-1-b",
			Created:  time.Now().UnixMilli(),
			Revision: "v5.1.0-812ebbc",
			Metadata: map[string]string{
				"status":           "booting",
				"addr.rpc.ip":      "192.168.2.1",
				"addr.rpc.port":    "5562",
				"addr.admin.ip":    "192.168.2.1",
				"addr.admin.port":  "7723",
				"protocol.version": "3",
				"instance":         "i-0bc636e38d6c537a7",
			},
		},
	)
	if err != nil {
		// handle err ...
	}
	defer registry.Unregister()

	// ...

	// Once ready update the nodes status to 'active'. This update will be
	// propagated to the other nodes in the cluster.
	err = registry.UpdateLocalMetadata(map[string]string{
		"status": "active",
	})
	if err != nil {
		// handle err ...
	}
}
Output:

Example (SubscribeToOrdersServiceNodes)

Registers a 'frontend' service and subscribes to the set of active order service nodes in us-east-1.

package main

import (
	"fmt"

	fuddle "github.com/fuddle-io/fuddle-go"
)

func main() {
	registry, err := fuddle.Register(
		// Seed addresses of Fuddle servers.
		[]string{"192.168.1.1:8220", "192.168.1.2:8220", "192.168.1.3:8220"},
		fuddle.Node{
			// ...
		},
	)
	if err != nil {
		// handle err ...
	}
	defer registry.Unregister()

	filter := fuddle.Filter{
		"order": {
			Locality: []string{"aws.us-east-1-*"},
			Metadata: fuddle.MetadataFilter{
				"status":           []string{"active"},
				"protocol.version": []string{"2", "3"},
			},
		},
	}

	// Filter to only include order service nodes in us-east-1 whose status
	// is active and protocol version is either 2 or 3.
	var addrs []string
	registry.Subscribe(func(orderNodes []fuddle.Node) {
		addrs = nil
		for _, node := range orderNodes {
			addr := node.Metadata["addr.rpc.ip"] + ":" + node.Metadata["addr.rpc.port"]
			addrs = append(addrs, addr)
		}

		fmt.Println("order service:", addrs)
	}, fuddle.WithFilter(filter))
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Filter

type Filter map[string]ServiceFilter

Filter specifies a node filter.

This maps a service name (which may include wildcards with '*') to a service filter.

Any nodes whose service don't match any of those listed are discarded.

func (*Filter) Match

func (f *Filter) Match(node Node) bool

type MetadataFilter

type MetadataFilter map[string][]string

MetadataFilter specifies a node filter that discards nodes whose metadata doesn't match the state listed.

To match, for each filter key, the node must include a value for that key and match at least on of the filters for that key.

The filter values may include wildcards, though the keys cannot.

func (*MetadataFilter) Match

func (f *MetadataFilter) Match(node Node) bool

type Node

type Node struct {
	// ID is a unique identifier for the node in the cluster.
	ID string

	// Service is the name of the service running on the node.
	Service string

	// Locality is the location of the node in the cluster.
	Locality string

	// Created is the time the node was created in UNIX milliseconds.
	Created int64

	// Revision identifies the version of the service running on the node.
	Revision string

	// Metadata contains application defined key-value pairs.
	Metadata map[string]string
}

Node represents the state of a node in the cluster.

It includes both fixed attributes of the node, and mutable application defined state.

func (*Node) Copy

func (s *Node) Copy() Node

func (*Node) Equal

func (s *Node) Equal(o Node) bool

type NodesOption

type NodesOption interface {
	// contains filtered or unexported methods
}

func WithFilter

func WithFilter(f Filter) NodesOption

WithFilter filters the returned set of nodes.

type Registry

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

Registry manages the nodes entry into the cluster registry.

func Register

func Register(addrs []string, node Node, opts ...RegistryOption) (*Registry, error)

Register registers the given node with the cluster registry.

Once registered the nodes state will be propagated to the other nodes in the cluster. It will also stream the existing cluster state and any future updates to maintain a local eventually consistent view of the cluster.

The given addresses are a set of seed addresses for Fuddle nodes.

func (*Registry) Nodes

func (r *Registry) Nodes(opts ...NodesOption) []Node

Nodes returns the set of nodes in the cluster.

func (*Registry) Subscribe

func (r *Registry) Subscribe(cb func(nodes []Node), opts ...NodesOption) func()

Subscribe registers the given callback to fire when the registry state changes.

The callback will be called immediately after registering with the current node state.

Note the callback is called synchronously with the registry mutex held, therefore it must NOT block or callback to the registry (or it will deadlock).

func (*Registry) Unregister

func (r *Registry) Unregister() error

Unregister unregisters the node from the cluster registry.

Note nodes must unregister themselves before shutting down. Otherwise Fuddle will think the node failed rather than left.

func (*Registry) UpdateLocalMetadata

func (r *Registry) UpdateLocalMetadata(update map[string]string) error

UpdateLocalMetadata will update the state of this node, which will be propagated to the other nodes in the cluster.

type RegistryOption

type RegistryOption interface {
	// contains filtered or unexported methods
}

func WithConnectTimeout

func WithConnectTimeout(timeout time.Duration) RegistryOption

WithConnectTimeout defines the time to wait for each connection attempt before timing out. Default to 1 second.

type ServiceFilter

type ServiceFilter struct {
	// Locality is a list of localities (which may include wildcards with '*'),
	// where the nodes locality must match at least on of the listed localities.
	Locality []string

	// Metadata contains the state filter.
	Metadata MetadataFilter
}

ServiceFilter specifies a node filter that applies to all nodes in a service.

func (*ServiceFilter) Match

func (f *ServiceFilter) Match(node Node) bool

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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