Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
package selector
import (
"sync"
"time"
"github.com/pkg/errors"
"go-micro.dev/v4/registry"
"go-micro.dev/v4/registry/cache"
)
type registrySelector struct {
so Options
rc cache.Cache
mu sync.RWMutex
}
func (c *registrySelector) newCache() cache.Cache {
opts := make([]cache.Option, 0, 1)
if c.so.Context != nil {
if t, ok := c.so.Context.Value("selector_ttl").(time.Duration); ok {
opts = append(opts, cache.WithTTL(t))
}
}
return cache.New(c.so.Registry, opts...)
}
func (c *registrySelector) Init(opts ...Option) error {
c.mu.Lock()
defer c.mu.Unlock()
for _, o := range opts {
o(&c.so)
}
c.rc.Stop()
c.rc = c.newCache()
return nil
}
func (c *registrySelector) Options() Options {
return c.so
}
func (c *registrySelector) Select(service string, opts ...SelectOption) (Next, error) {
c.mu.RLock()
defer c.mu.RUnlock()
sopts := SelectOptions{
Strategy: c.so.Strategy,
}
for _, opt := range opts {
opt(&sopts)
}
// get the service
// try the cache first
// if that fails go directly to the registry
services, err := c.rc.GetService(service)
if err != nil {
if errors.Is(err, registry.ErrNotFound) {
return nil, ErrNotFound
}
return nil, err
}
// apply the filters
for _, filter := range sopts.Filters {
services = filter(services)
}
// if there's nothing left, return
if len(services) == 0 {
return nil, ErrNoneAvailable
}
return sopts.Strategy(services), nil
}
func (c *registrySelector) Mark(service string, node *registry.Node, err error) {
}
func (c *registrySelector) Reset(service string) {
}
// Close stops the watcher and destroys the cache.
func (c *registrySelector) Close() error {
c.rc.Stop()
return nil
}
func (c *registrySelector) String() string {
return "registry"
}
// NewSelector creates a new default selector.
func NewSelector(opts ...Option) Selector {
sopts := Options{
Strategy: Random,
}
for _, opt := range opts {
opt(&sopts)
}
if sopts.Registry == nil {
sopts.Registry = registry.DefaultRegistry
}
s := &registrySelector{
so: sopts,
}
s.rc = s.newCache()
return s
}
+73
View File
@@ -0,0 +1,73 @@
package selector
import (
"go-micro.dev/v4/registry"
)
// FilterEndpoint is an endpoint based Select Filter which will
// only return services with the endpoint specified.
func FilterEndpoint(name string) Filter {
return func(old []*registry.Service) []*registry.Service {
var services []*registry.Service
for _, service := range old {
for _, ep := range service.Endpoints {
if ep.Name == name {
services = append(services, service)
break
}
}
}
return services
}
}
// FilterLabel is a label based Select Filter which will
// only return services with the label specified.
func FilterLabel(key, val string) Filter {
return func(old []*registry.Service) []*registry.Service {
var services []*registry.Service
for _, service := range old {
serv := new(registry.Service)
var nodes []*registry.Node
for _, node := range service.Nodes {
if node.Metadata == nil {
continue
}
if node.Metadata[key] == val {
nodes = append(nodes, node)
}
}
// only add service if there's some nodes
if len(nodes) > 0 {
// copy
*serv = *service
serv.Nodes = nodes
services = append(services, serv)
}
}
return services
}
}
// FilterVersion is a version based Select Filter which will
// only return services with the version specified.
func FilterVersion(version string) Filter {
return func(old []*registry.Service) []*registry.Service {
var services []*registry.Service
for _, service := range old {
if service.Version == version {
services = append(services, service)
}
}
return services
}
}
+70
View File
@@ -0,0 +1,70 @@
package selector
import (
"context"
"go-micro.dev/v4/logger"
"go-micro.dev/v4/registry"
)
type Options struct {
Registry registry.Registry
Strategy Strategy
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
// Logger is the underline logger
Logger logger.Logger
}
type SelectOptions struct {
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
Strategy Strategy
Filters []Filter
}
type Option func(*Options)
// SelectOption used when making a select call.
type SelectOption func(*SelectOptions)
// Registry sets the registry used by the selector.
func Registry(r registry.Registry) Option {
return func(o *Options) {
o.Registry = r
}
}
// SetStrategy sets the default strategy for the selector.
func SetStrategy(fn Strategy) Option {
return func(o *Options) {
o.Strategy = fn
}
}
// WithFilter adds a filter function to the list of filters
// used during the Select call.
func WithFilter(fn ...Filter) SelectOption {
return func(o *SelectOptions) {
o.Filters = append(o.Filters, fn...)
}
}
// Strategy sets the selector strategy.
func WithStrategy(fn Strategy) SelectOption {
return func(o *SelectOptions) {
o.Strategy = fn
}
}
// WithLogger sets the underline logger.
func WithLogger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
+43
View File
@@ -0,0 +1,43 @@
// Package selector is a way to pick a list of service nodes
package selector
import (
"errors"
"go-micro.dev/v4/registry"
)
// Selector builds on the registry as a mechanism to pick nodes
// and mark their status. This allows host pools and other things
// to be built using various algorithms.
type Selector interface {
Init(opts ...Option) error
Options() Options
// Select returns a function which should return the next node
Select(service string, opts ...SelectOption) (Next, error)
// Mark sets the success/error against a node
Mark(service string, node *registry.Node, err error)
// Reset returns state back to zero for a service
Reset(service string)
// Close renders the selector unusable
Close() error
// Name of the selector
String() string
}
// Next is a function that returns the next node
// based on the selector's strategy.
type Next func() (*registry.Node, error)
// Filter is used to filter a service during the selection process.
type Filter func([]*registry.Service) []*registry.Service
// Strategy is a selection strategy e.g random, round robin.
type Strategy func([]*registry.Service) Next
var (
DefaultSelector = NewSelector()
ErrNotFound = errors.New("not found")
ErrNoneAvailable = errors.New("none available")
)
+56
View File
@@ -0,0 +1,56 @@
package selector
import (
"math/rand"
"sync"
"time"
"go-micro.dev/v4/registry"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
// Random is a random strategy algorithm for node selection.
func Random(services []*registry.Service) Next {
nodes := make([]*registry.Node, 0, len(services))
for _, service := range services {
nodes = append(nodes, service.Nodes...)
}
return func() (*registry.Node, error) {
if len(nodes) == 0 {
return nil, ErrNoneAvailable
}
i := rand.Int() % len(nodes)
return nodes[i], nil
}
}
// RoundRobin is a roundrobin strategy algorithm for node selection.
func RoundRobin(services []*registry.Service) Next {
nodes := make([]*registry.Node, 0, len(services))
for _, service := range services {
nodes = append(nodes, service.Nodes...)
}
var i = rand.Int()
var mtx sync.Mutex
return func() (*registry.Node, error) {
if len(nodes) == 0 {
return nil, ErrNoneAvailable
}
mtx.Lock()
node := nodes[i%len(nodes)]
i++
mtx.Unlock()
return node, nil
}
}