mirror of
https://github.com/geektutu/7days-golang.git
synced 2024-04-21 12:32:11 +00:00
110 lines
2.6 KiB
Go
110 lines
2.6 KiB
Go
package xclient
|
|
|
|
import (
|
|
"context"
|
|
. "geerpc"
|
|
"io"
|
|
"reflect"
|
|
"sync"
|
|
)
|
|
|
|
type XClient struct {
|
|
d Discovery
|
|
mode SelectMode
|
|
opt *Option
|
|
mu sync.Mutex // protect following
|
|
clients map[string]*Client
|
|
}
|
|
|
|
var _ io.Closer = (*XClient)(nil)
|
|
|
|
func NewXClient(d Discovery, mode SelectMode, opt *Option) *XClient {
|
|
return &XClient{d: d, mode: mode, opt: opt, clients: make(map[string]*Client)}
|
|
}
|
|
|
|
func (xc *XClient) Close() error {
|
|
xc.mu.Lock()
|
|
defer xc.mu.Unlock()
|
|
for key, client := range xc.clients {
|
|
// I have no idea how to deal with error, just ignore it.
|
|
_ = client.Close()
|
|
delete(xc.clients, key)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (xc *XClient) dial(rpcAddr string) (*Client, error) {
|
|
xc.mu.Lock()
|
|
defer xc.mu.Unlock()
|
|
client, ok := xc.clients[rpcAddr]
|
|
if ok && !client.IsAvailable() {
|
|
_ = client.Close()
|
|
delete(xc.clients, rpcAddr)
|
|
client = nil
|
|
}
|
|
if client == nil {
|
|
var err error
|
|
client, err = XDial(rpcAddr, xc.opt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
xc.clients[rpcAddr] = client
|
|
}
|
|
return client, nil
|
|
}
|
|
|
|
func (xc *XClient) call(rpcAddr string, ctx context.Context, serviceMethod string, args, reply interface{}) error {
|
|
client, err := xc.dial(rpcAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return client.Call(ctx, serviceMethod, args, reply)
|
|
}
|
|
|
|
// Call invokes the named function, waits for it to complete,
|
|
// and returns its error status.
|
|
// xc will choose a proper server.
|
|
func (xc *XClient) Call(ctx context.Context, serviceMethod string, args, reply interface{}) error {
|
|
rpcAddr, err := xc.d.Get(xc.mode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return xc.call(rpcAddr, ctx, serviceMethod, args, reply)
|
|
}
|
|
|
|
// Broadcast invokes the named function for every server registered in discovery
|
|
func (xc *XClient) Broadcast(ctx context.Context, serviceMethod string, args, reply interface{}) error {
|
|
servers, err := xc.d.GetAll()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var wg sync.WaitGroup
|
|
var mu sync.Mutex // protect e and replyDone
|
|
var e error
|
|
replyDone := reply == nil // if reply is nil, don't need to set value
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
for _, rpcAddr := range servers {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
var clonedReply interface{}
|
|
if reply != nil {
|
|
clonedReply = reflect.New(reflect.ValueOf(reply).Elem().Type()).Interface()
|
|
}
|
|
err := xc.call(rpcAddr, ctx, serviceMethod, args, clonedReply)
|
|
mu.Lock()
|
|
if err != nil && e == nil {
|
|
e = err
|
|
cancel() // if any call failed, cancel unfinished calls
|
|
}
|
|
if err == nil && !replyDone {
|
|
reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem())
|
|
replyDone = true
|
|
}
|
|
mu.Unlock()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
return e
|
|
}
|