registry

package module
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 2023 License: MIT Imports: 23 Imported by: 0

README

build status Go Reference contributions welcome Mit License

English | 简体中文

Registry

Registry is a simple service registry that uses the consistent hashing algorithm for service discovery.

What is consistent hashing

Consistent hashing is a hashing technique that performs really well when operated in a dynamic environment where the distributed system scales up and scales down frequently.

The problem of naive hashing function

A naive hashing function is key % n where n is the number of servers. It has two major drawbacks:

  1. NOT horizontally scalable, or in other words, NOT partition tolerant. When you add new servers, all existing mapping are broken. It could introduce painful maintenance work and downtime to the system.
  2. May NOT be load balanced. If the data is not uniformly distributed, this might cause some servers to be hot and saturated while others idle and almost empty.

Problem 2 can be resolved by hashing the key first, hash(key) % n, so that the hashed keys will be likely to be distributed more evenly. But this can't solve the problem 1. We need to find a solution that can distribute the keys and is not dependent on n.

Consistent Hashing

Consistent Hashing allows distributing data in such a way that minimize reorganization when nodes are added or removed, hence making the system easier to scale up or down.

The key idea is that it's a distribution scheme that DOES NOT depend directly on the number of servers.

In Consistent Hashing, when the hash table is resized, in general only k / n keys need to be remapped, where k is the total number of keys and n is the total number of servers.

When a new node is added, it takes shares from a few hosts without touching other's shares When a node is removed, its shares are shared by other hosts.

Getting started

Build registry
cd cmd 
go build -o registry
Usage
   -id string
        Service ID, cannot be empty
  -bind string
        The address used to register the service (default ":7370").
  -bind-advertise string
        The address will advertise to other services (default ":7370").
  -addr string
        The address used for service discovery (default ":9800").
  -advertise string
        The address will advertise to client for service discover (default ":9800").
  -registries string
        Registry server addresses, it can be empty, and multiples are separated by commas.
  

Starting registry server

To start a registry server, follow these steps:

  1. Determine the number of nodes required based on your actual situation.
  2. Execute the following commands to start the nodes:
# Starting the first node
./registry -bind=":7370" \
     -bind-advertise="172.16.3.3:7370" \
     -id=service-1 \
     -addr=":9800" \
     -advertise="172.16.3.3:9800"

# Starting the second node
# The second one has an additional parameter -registries="172.16.3.3:7370",
# because the second node needs to register with the first one
./registry -bind=":7371" \
     -bind-advertise="172.16.3.3:7371" \
     -id=service-2 \
     -registries="172.16.3.3:7370" \
     -addr=":9801" \
     -advertise="172.16.3.3:9801"

Note: If there is a firewall, make sure to open both TCP and UDP ports.

Register services

Use the following code snippet to register services:

// Create a new registration object
r := register.New(id, bind, advertise, registries, group, addr)

// Start the registration
err = r.Start()
if err != nil {
	panic(err)
}

Parameters:

  • id: Service ID.
  • bind: Address used to register the service to the registry server.
  • advertise: Address that the service will advertise to the registry server. Can be used for basic NAT traversal where both the internal IP:port and external IP:port are known.
  • registries: Addresses of the registry server(s). If there are more than one, separate them with commas, such as "192.168.1.101:7370,192.168.1.102:7370".
  • group: Group name the current service belongs to.
  • addr: Address currently provided by this service to the client. For example, if the current service is an HTTP server, the address is 172.16.3.3:80, which is the address that HTTP listens to.

Service Discovery

Usage
// You can choose any one of the registered servers.
registryAddr := "172.16.3.3:9801"
group := "test-group"

// Create a new RpcClient
client, err := client.NewRpcClient(registryAddr)
if err != nil {
	panic(err)
}

// Use consistent hash to assign services based on user ID
service, err := client.Match(groupName, "user-id-1")
if err != nil {
	panic(err)
}

log.Printf("[INFO] Matched key: %s, Service ID: %s, Service Address: %s\n", key, service.Id, service.Addr)

// Get all services of the group
allService, err := client.Members(group)
if err != nil {
      log.Printf("[ERROR] Failed to get all services: %s\n", err)
}
log.Printf("[INFO] All services: %+v\n", allService)

Examples

Register two web services.
# Register the first web service.
cd examples/service
go build -o webservice webservice.go 
./webservice \
	-group=webservice-group \
	-id=webserver1 \
	-registries=172.16.3.3:7370 \
	-bind=":8370"
	-addr="172.16.3.3:8080"

# Register the second web service.
cd examples/service
./webservice \
	-group=webservice-group \
	-id=webserver2 \
	-registries=172.16.3.3:7370 \
	-bind=":8371" \
	-addr="172.16.3.3:8081"
Client discovery service
cd examples/client
go build -o client main.go
./client

Contributions

Contributions and feedback are both welcomed and encouraged! Open an issue to report a bug, ask a question, or make a feature request.

Documentation

Index

Constants

View Source
const (
	// TagGroup is the tag key of the group name.
	TagGroup = "group"

	// TagAddr is the tag key of the service address.
	TagAddr = "addr"

	// TagReplicas is the tag key of replicas.
	TagReplicas = "replicas"
)
View Source
const (
	DefaultReplicas = "10000" // Default number of replicas to virtualize a service
)

Variables

View Source
var (
	ErrMemberIdEmpty       = Err{Code: 10000, Msg: "id can't be empty"}
	ErrReplicasParam       = Err{Code: 10000, Msg: "member replicas param error"}
	ErrGroupNameEmpty      = Err{Code: 10001, Msg: "member group name empty"}
	ErrParseAddrToHostPort = Err{Code: 10002, Msg: "parse addr to host and port error"}
	ErrParsePort           = Err{Code: 10003, Msg: "parse port error"}
)

Pre-defined error instances with specific error codes and messages.

View Source
var File_rpcserver_proto protoreflect.FileDescriptor

Functions

func RegisterRServer

func RegisterRServer(s *grpc.Server, srv RServer)

Types

type Api

type Api interface {

	// Start the discovery server
	// addr: the addr that discovery server listens to
	Start(addr string) error

	// Stop the discovery server
	Stop()
}

Api is an api interface for service discovery

type Discovery

type Discovery interface {

	// SetHandler sets the event processing handler when new services are discovered.
	SetHandler(Handler)

	// Members returns the members of all services.
	Members() []*Member

	// LocalMember returns the current service.
	LocalMember() *Member

	// Start starts the discovery service.
	Start() error

	// Stop stops the discovery service.
	Stop()
}

Auto-discover interface.

type Err added in v0.0.6

type Err struct {
	Msg  string // the error message
	Code int    // the error code
}

Err represents a custom error type with an error message and error code.

func (Err) Error added in v0.0.6

func (e Err) Error() string

Error returns the error message as a string (implements the error interface).

func (Err) String added in v0.0.6

func (e Err) String() string

String returns the error message as a string.

type Handler

type Handler interface {

	// OnMemberJoin is triggered when a new service is registered.
	OnMemberJoin(*Member) error

	// OnMemberLeave is triggered when a service leaves.
	OnMemberLeave(*Member) error

	// OnMemberUpdate is triggered when a service is updated.
	OnMemberUpdate(*Member) error
}

Auto-discover event notification interface

type Http

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

Http represents the http server object

func NewHttp

func NewHttp() *Http

NewHttp returns a new Http object

func (*Http) Start

func (h *Http) Start(addr string) error

Start starts the http server

func (*Http) Stop

func (h *Http) Stop()

Stop stops the http server

type IOption

type IOption func(o *Option)

IOption represents a function that modifies the Option.

func OptAddr

func OptAddr(addr string) IOption

OptAddr sets the service discovery address option.

func OptAdvertise

func OptAdvertise(addr string) IOption

OptAdvertise sets the advertised address for service discovery option.

func OptBind

func OptBind(addr string) IOption

OptBind sets the address used for service registration option.

func OptBindAdvertise

func OptBindAdvertise(addr string) IOption

OptBindAdvertise sets the advertised address for service registration option.

func OptId

func OptId(id string) IOption

OptId sets the service ID option.

func OptRegistries

func OptRegistries(registries string) IOption

OptRegistries sets the addresses of other registry servers option.

type MatchRequest

type MatchRequest struct {
	Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"`
	Key   string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
	// contains filtered or unexported fields
}

func (*MatchRequest) Descriptor deprecated

func (*MatchRequest) Descriptor() ([]byte, []int)

Deprecated: Use MatchRequest.ProtoReflect.Descriptor instead.

func (*MatchRequest) GetGroup

func (x *MatchRequest) GetGroup() string

func (*MatchRequest) GetKey

func (x *MatchRequest) GetKey() string

func (*MatchRequest) ProtoMessage

func (*MatchRequest) ProtoMessage()

func (*MatchRequest) ProtoReflect

func (x *MatchRequest) ProtoReflect() protoreflect.Message

func (*MatchRequest) Reset

func (x *MatchRequest) Reset()

func (*MatchRequest) String

func (x *MatchRequest) String() string

type MatchResponse

type MatchResponse struct {
	Id    string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
	Group string `protobuf:"bytes,2,opt,name=group,proto3" json:"group,omitempty"`
	Addr  string `protobuf:"bytes,3,opt,name=addr,proto3" json:"addr,omitempty"`
	// contains filtered or unexported fields
}

func (*MatchResponse) Descriptor deprecated

func (*MatchResponse) Descriptor() ([]byte, []int)

Deprecated: Use MatchResponse.ProtoReflect.Descriptor instead.

func (*MatchResponse) GetAddr

func (x *MatchResponse) GetAddr() string

func (*MatchResponse) GetGroup

func (x *MatchResponse) GetGroup() string

func (*MatchResponse) GetId

func (x *MatchResponse) GetId() string

func (*MatchResponse) ProtoMessage

func (*MatchResponse) ProtoMessage()

func (*MatchResponse) ProtoReflect

func (x *MatchResponse) ProtoReflect() protoreflect.Message

func (*MatchResponse) Reset

func (x *MatchResponse) Reset()

func (*MatchResponse) String

func (x *MatchResponse) String() string

type Member

type Member struct {
	sync.Mutex

	// The ID of the service.
	Id string `json:"id"`

	// The address used to register the service to the registry server.
	Bind string `json:"bind"`

	// The address that the service will advertise to the registry server.
	Advertise string `json:"advertise"`

	// The addresses of the registry servers. If there are more than one, separate them with commas, such as "192.168.1.101:7370,192.168.1.102:7370".
	Registries string `json:"-"`

	// The number of replicated elements of a service that need to be virtualized.
	Replicas string `json:"replicas"`

	// Service information.
	Service Service `json:"service"`
	// contains filtered or unexported fields
}

Member is used for auto-discovery. When a service is discovered, a Member object is created.

func NewMember

func NewMember(id string, bind string, advertise string, registries string, group string, addr string) *Member

NewMember creates a new Member object with the given attributes.

func NewSimpleMember

func NewSimpleMember(id string, bind string, advertise string) *Member

NewSimpleMember creates a simple Member object. It does not contain the address of the service.

func (*Member) GetTag

func (m *Member) GetTag(key string) (string, bool)

GetTag retrieves the value associated with the given tag for this Member object.

func (*Member) GetTags

func (m *Member) GetTags() map[string]string

GetTags retrieves all tags and their values for this Member object.

func (*Member) IsSelf

func (m *Member) IsSelf(b *Member) bool

IsSelf returns true if the given Member object has the same ID as this Member object.

func (*Member) Marshal

func (m *Member) Marshal() ([]byte, error)

Marshal returns the JSON encoding of this Member object.

func (*Member) SetTag

func (m *Member) SetTag(key string, val string)

SetTag sets the extra information associated with the given tag for this Member object.

func (*Member) SetTags

func (m *Member) SetTags(tags map[string]string)

SetTags set tags for this service

func (*Member) Unmarshal

func (m *Member) Unmarshal(paylaod []byte) error

Unmarshal parses the given JSON-encoded data and stores

type MembersRequest

type MembersRequest struct {
	Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"`
	// contains filtered or unexported fields
}

func (*MembersRequest) Descriptor deprecated

func (*MembersRequest) Descriptor() ([]byte, []int)

Deprecated: Use MembersRequest.ProtoReflect.Descriptor instead.

func (*MembersRequest) GetGroup

func (x *MembersRequest) GetGroup() string

func (*MembersRequest) ProtoMessage

func (*MembersRequest) ProtoMessage()

func (*MembersRequest) ProtoReflect

func (x *MembersRequest) ProtoReflect() protoreflect.Message

func (*MembersRequest) Reset

func (x *MembersRequest) Reset()

func (*MembersRequest) String

func (x *MembersRequest) String() string

type MembersResponse

type MembersResponse struct {
	Services []*MatchResponse `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
	// contains filtered or unexported fields
}

func (*MembersResponse) Descriptor deprecated

func (*MembersResponse) Descriptor() ([]byte, []int)

Deprecated: Use MembersResponse.ProtoReflect.Descriptor instead.

func (*MembersResponse) GetServices

func (x *MembersResponse) GetServices() []*MatchResponse

func (*MembersResponse) ProtoMessage

func (*MembersResponse) ProtoMessage()

func (*MembersResponse) ProtoReflect

func (x *MembersResponse) ProtoReflect() protoreflect.Message

func (*MembersResponse) Reset

func (x *MembersResponse) Reset()

func (*MembersResponse) String

func (x *MembersResponse) String() string

type Option

type Option struct {

	// Id is the service ID.
	Id string

	// Bind is the address used to register the service.
	// If there is a firewall, ensure that the port is open for both TCP and UDP.
	Bind string

	// BindAdvertise is the address that the service will advertise to other services for registering.
	// Can be used for basic NAT traversal where both the internal IP:port and external IP:port are known.
	BindAdvertise string

	// Registries are the addresses of other registry servers.
	// If there are more than one, separate them with commas, such as "192.168.1.101:7370,192.168.1.102:7370".
	Registries string

	// Addr is the address used for service discovery.
	Addr string

	// Advertise is the address that will be advertised to clients for service discovery.
	Advertise string
}

Option represents the options for registry server.

func DefaultOption

func DefaultOption() *Option

DefaultOption returns the default options for registering a server.

type RClient

type RClient interface {
	Match(ctx context.Context, in *MatchRequest, opts ...grpc.CallOption) (*MatchResponse, error)
	Members(ctx context.Context, in *MembersRequest, opts ...grpc.CallOption) (*MembersResponse, error)
}

RClient is the client API for R service.

For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.

func NewRClient

func NewRClient(cc grpc.ClientConnInterface) RClient

type RServer

type RServer interface {
	Match(context.Context, *MatchRequest) (*MatchResponse, error)
	Members(context.Context, *MembersRequest) (*MembersResponse, error)
}

RServer is the server API for R service.

type Registry

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

Registry is the registry server object

func New

func New(opts []IOption) *Registry

New creates a new registry object that can start a registry server when calling Serve().

func (*Registry) Close

func (s *Registry) Close()

Close closes the registry server

func (*Registry) Match

func (s *Registry) Match(groupName string, key string) (*Service, error)

Match uses a consistent hashing algorithm to assign a service to a key.

func (*Registry) Members

func (s *Registry) Members(groupName string) []*Service

Members returns a list of services for a given group name.

func (*Registry) OnMemberJoin

func (s *Registry) OnMemberJoin(m *Member) error

OnMemberJoin is triggered when a new service is registered

func (*Registry) OnMemberLeave

func (s *Registry) OnMemberLeave(m *Member) error

OnMemberLeave is triggered when a service leaves

func (*Registry) OnMemberUpdate

func (s *Registry) OnMemberUpdate(m *Member) error

OnMemberUpdate is triggered when a service is updated

func (*Registry) Serve

func (s *Registry) Serve()

Serve runs the registry server

type RpcServer

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

RpcServer is a gRPC server for service discovery

func NewRpcServer

func NewRpcServer() *RpcServer

NewRpcServer creates a new RpcServer object

func (*RpcServer) Match

func (s *RpcServer) Match(ctx context.Context, req *MatchRequest) (*MatchResponse, error)

Match assigns a service to a key using the consistent hashing algorithm

func (*RpcServer) Members

func (s *RpcServer) Members(ctx context.Context, req *MembersRequest) (*MembersResponse, error)

Members returns a list of services in a group

func (*RpcServer) Start

func (s *RpcServer) Start(addr string) error

Start starts the gRPC server

func (*RpcServer) Stop

func (s *RpcServer) Stop()

Stop stops the gRPC server

type Serf

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

Serf represents a discovery instance of hashicorp/serf.

func NewSerf

func NewSerf(local *Member) *Serf

NewSerf creates a new instance of Serf.

func (*Serf) Join

func (s *Serf) Join(members []string) error

Join joins the Serf agent to an existing Serf cluster with the specified members.

func (*Serf) LocalMember

func (s *Serf) LocalMember() *Member

LocalMember returns the current registry service.

func (*Serf) Members

func (s *Serf) Members() []*Member

Members returns the members of all services.

func (*Serf) SetHandler

func (s *Serf) SetHandler(h Handler)

SetHandler sets the event processing handler when new services are discovered.

func (*Serf) Start

func (s *Serf) Start() error

Start starts the HashiCorp Serf agent with the configuration provided in s.

func (*Serf) Stop

func (s *Serf) Stop()

Stop stops the Serf server.

type Service

type Service struct {
	// The ID of the service.
	Id string `json:"id"`

	// The group name of this service.
	Group string `json:"group"`

	// The service address provided to the client.
	Addr string `json:"addr"`
}

Service represents a service object.

func NewService

func NewService(id string, group string, addr string) *Service

NewService creates a new service object.

type UnimplementedRServer

type UnimplementedRServer struct {
}

UnimplementedRServer can be embedded to have forward compatible implementations.

func (*UnimplementedRServer) Match

func (*UnimplementedRServer) Members

Directories

Path Synopsis
SPDX-License-Identifier: MIT SPDX-FileCopyrightText: 2023 werbenhu SPDX-FileContributor: werbenhu
SPDX-License-Identifier: MIT SPDX-FileCopyrightText: 2023 werbenhu SPDX-FileContributor: werbenhu
SPDX-License-Identifier: MIT SPDX-FileCopyrightText: 2023 werbenhu SPDX-FileContributor: werbenhu
SPDX-License-Identifier: MIT SPDX-FileCopyrightText: 2023 werbenhu SPDX-FileContributor: werbenhu
examples
service
SPDX-License-Identifier: MIT SPDX-FileCopyrightText: 2023 werbenhu SPDX-FileContributor: werbenhu
SPDX-License-Identifier: MIT SPDX-FileCopyrightText: 2023 werbenhu SPDX-FileContributor: werbenhu

Jump to

Keyboard shortcuts

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