Feat/http resovler (#953)
* add http resolver & balancer Co-authored-by: chenzhihui <zhihui_chen@foxmail.com>pull/956/head
parent
c1e5b1c17b
commit
28abad2268
@ -0,0 +1,18 @@ |
||||
package balancer |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/go-kratos/kratos/v2/registry" |
||||
) |
||||
|
||||
// DoneInfo is callback when rpc done
|
||||
type DoneInfo struct { |
||||
Err error |
||||
Trailer map[string]string |
||||
} |
||||
|
||||
// Balancer is node pick balancer
|
||||
type Balancer interface { |
||||
Pick(ctx context.Context, pathPattern string, nodes []*registry.ServiceInstance) (node *registry.ServiceInstance, done func(DoneInfo), err error) |
||||
} |
@ -0,0 +1,29 @@ |
||||
package random |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"math/rand" |
||||
|
||||
"github.com/go-kratos/kratos/v2/internal/balancer" |
||||
"github.com/go-kratos/kratos/v2/registry" |
||||
) |
||||
|
||||
var _ balancer.Balancer = &Balancer{} |
||||
|
||||
type Balancer struct { |
||||
} |
||||
|
||||
func New() *Balancer { |
||||
return &Balancer{} |
||||
} |
||||
|
||||
func (b *Balancer) Pick(ctx context.Context, pathPattern string, nodes []*registry.ServiceInstance) (node *registry.ServiceInstance, done func(balancer.DoneInfo), err error) { |
||||
if len(nodes) == 0 { |
||||
return nil, nil, fmt.Errorf("no instances avaiable") |
||||
} else if len(nodes) == 1 { |
||||
return nodes[0], func(di balancer.DoneInfo) {}, nil |
||||
} |
||||
idx := rand.Intn(len(nodes)) |
||||
return nodes[idx], func(di balancer.DoneInfo) {}, nil |
||||
} |
@ -0,0 +1,86 @@ |
||||
package http |
||||
|
||||
import ( |
||||
"context" |
||||
"net/url" |
||||
"sync" |
||||
|
||||
"github.com/go-kratos/kratos/v2/log" |
||||
"github.com/go-kratos/kratos/v2/registry" |
||||
) |
||||
|
||||
// Target is resolver target
|
||||
type Target struct { |
||||
Scheme string |
||||
Authority string |
||||
Endpoint string |
||||
} |
||||
|
||||
type resolver struct { |
||||
lock sync.RWMutex |
||||
nodes []*registry.ServiceInstance |
||||
|
||||
target Target |
||||
watcher registry.Watcher |
||||
logger *log.Helper |
||||
} |
||||
|
||||
func newResolver(ctx context.Context, scheme string, discovery registry.Discovery, target Target) (*resolver, error) { |
||||
watcher, err := discovery.Watch(ctx, target.Endpoint) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
r := &resolver{ |
||||
target: target, |
||||
watcher: watcher, |
||||
logger: log.NewHelper(log.DefaultLogger), |
||||
} |
||||
go func() { |
||||
for { |
||||
services, err := watcher.Next() |
||||
if err != nil { |
||||
r.logger.Errorf("http client watch services got unexpected error:=%v", err) |
||||
return |
||||
} |
||||
var nodes []*registry.ServiceInstance |
||||
for _, in := range services { |
||||
endpoint, err := parseEndpoint(scheme, in.Endpoints) |
||||
if err != nil { |
||||
r.logger.Errorf("Failed to parse discovery endpoint: %v error %v", in.Endpoints, err) |
||||
continue |
||||
} |
||||
if endpoint == "" { |
||||
continue |
||||
} |
||||
nodes = append(nodes, in) |
||||
} |
||||
if len(nodes) != 0 { |
||||
r.lock.Lock() |
||||
r.nodes = nodes |
||||
r.lock.Unlock() |
||||
} |
||||
} |
||||
}() |
||||
return r, nil |
||||
} |
||||
|
||||
func (r *resolver) fetch(ctx context.Context) []*registry.ServiceInstance { |
||||
r.lock.RLock() |
||||
nodes := r.nodes |
||||
r.lock.RUnlock() |
||||
|
||||
return nodes |
||||
} |
||||
|
||||
func parseEndpoint(schema string, endpoints []string) (string, error) { |
||||
for _, e := range endpoints { |
||||
u, err := url.Parse(e) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
if u.Scheme == schema { |
||||
return u.Host, nil |
||||
} |
||||
} |
||||
return "", nil |
||||
} |
Loading…
Reference in new issue