Compare commits
94 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ac2a5a04a2 | ||
f1d08f251f | |||
|
718ae42808 | ||
|
2413cbcd80 | ||
|
9c820445a4 | ||
|
c44fd63301 | ||
|
d9a699ae6f | ||
|
4495ca3839 | ||
|
0b0eee41d0 | ||
|
e18f8defde | ||
|
7abdc68049 | ||
|
c90e1ccb99 | ||
|
991142cd57 | ||
|
5a5b1b8f6e | ||
|
58bc4c103f | ||
|
88817dc53f | ||
|
ef04331b86 | ||
|
67215ae5da | ||
|
f120452d28 | ||
|
740cfab8d0 | ||
|
f6b8045dd5 | ||
|
b776fbb766 | ||
|
a42de29f67 | ||
|
0f6d09af33 | ||
|
2dd5109eee | ||
|
e609095ba4 | ||
|
4843f09afa | ||
|
f9eddf1e6f | ||
|
f19308f1e6 | ||
|
8f0c2e0412 | ||
|
bf0e46dc0d | ||
|
15975d2903 | ||
|
9f2f0e3cea | ||
|
151bcf0ea1 | ||
|
dc0fbfc3c0 | ||
|
e607485c6b | ||
70d0029658 | |||
a606813fdf | |||
|
750267b308 | ||
|
7ce0305db4 | ||
|
c39591af0e | ||
|
fedc6be3e6 | ||
|
cb1679fd8d | ||
|
c0a676bfa9 | ||
|
cb4e376c64 | ||
|
c2c8182a5b | ||
|
01cb146e0d | ||
|
d0d729a789 | ||
|
113d87d855 | ||
|
56df10f68b | ||
|
3a5428fb36 | ||
|
178a3b3d8e | ||
|
de34f259ba | ||
|
81b68a1d7f | ||
|
1a600810a7 | ||
|
94127ae1aa | ||
|
cd2ac648ff | ||
|
e613b0c205 | ||
|
57dacf1831 | ||
|
8986b3135f | ||
|
6dd3ea1853 | ||
|
2c66e94045 | ||
|
c1ff3ceee4 | ||
|
b13604fb4b | ||
|
057adb2b2e | ||
|
7bd6d1b549 | ||
|
37988b596d | ||
|
9eb45dac82 | ||
|
59b13aef22 | ||
|
1e496938b7 | ||
|
fbc1d523d7 | ||
|
11795071fb | ||
|
c7e8a2aeb9 | ||
|
cb1c1afc84 | ||
|
3fc7d9ea50 | ||
|
abc2ace409 | ||
|
243d43df92 | ||
|
9c2b882008 | ||
|
4370f03e04 | ||
|
a3b962f37b | ||
|
a894b4f354 | ||
|
fc379f2d2c | ||
|
dcf4fed6a3 | ||
|
117376a922 | ||
|
380d9790e6 | ||
|
0baea58938 | ||
|
edb0fe4b16 | ||
|
eae32176c4 | ||
|
bc751c55fb | ||
|
91f2af91de | ||
|
3adce58eb2 | ||
|
bb01b3ed78 | ||
|
c3ea25225c | ||
|
beffa625f8 |
@@ -1,6 +1,5 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
@@ -13,18 +13,19 @@ import (
|
||||
)
|
||||
|
||||
type natsBroker struct {
|
||||
sync.Once
|
||||
sync.RWMutex
|
||||
addrs []string
|
||||
conn *nats.Conn
|
||||
opts broker.Options
|
||||
nopts nats.Options
|
||||
drain bool
|
||||
addrs []string
|
||||
conn *nats.Conn
|
||||
opts broker.Options
|
||||
nopts nats.Options
|
||||
drain bool
|
||||
closeCh chan (error)
|
||||
}
|
||||
|
||||
type subscriber struct {
|
||||
s *nats.Subscription
|
||||
opts broker.SubscribeOptions
|
||||
drain bool
|
||||
s *nats.Subscription
|
||||
opts broker.SubscribeOptions
|
||||
}
|
||||
|
||||
type publication struct {
|
||||
@@ -54,9 +55,6 @@ func (s *subscriber) Topic() string {
|
||||
}
|
||||
|
||||
func (s *subscriber) Unsubscribe() error {
|
||||
if s.drain {
|
||||
return s.s.Drain()
|
||||
}
|
||||
return s.s.Unsubscribe()
|
||||
}
|
||||
|
||||
@@ -122,20 +120,17 @@ func (n *natsBroker) Connect() error {
|
||||
|
||||
func (n *natsBroker) Disconnect() error {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
if n.drain {
|
||||
n.conn.Drain()
|
||||
} else {
|
||||
n.conn.Close()
|
||||
return <-n.closeCh
|
||||
}
|
||||
n.RUnlock()
|
||||
n.conn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) Init(opts ...broker.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&n.opts)
|
||||
}
|
||||
n.addrs = setAddrs(n.opts.Addrs)
|
||||
n.setOption(opts...)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -167,11 +162,6 @@ func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...bro
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
var drain bool
|
||||
if _, ok := opt.Context.Value(drainSubscriptionKey{}).(bool); ok {
|
||||
drain = true
|
||||
}
|
||||
|
||||
fn := func(msg *nats.Msg) {
|
||||
var m broker.Message
|
||||
if err := n.opts.Codec.Unmarshal(msg.Data, &m); err != nil {
|
||||
@@ -193,7 +183,7 @@ func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...bro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &subscriber{s: sub, opts: opt, drain: drain}, nil
|
||||
return &subscriber{s: sub, opts: opt}, nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) String() string {
|
||||
@@ -207,39 +197,59 @@ func NewBroker(opts ...broker.Option) broker.Broker {
|
||||
Context: context.Background(),
|
||||
}
|
||||
|
||||
n := &natsBroker{
|
||||
opts: options,
|
||||
}
|
||||
n.setOption(opts...)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *natsBroker) setOption(opts ...broker.Option) {
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
o(&n.opts)
|
||||
}
|
||||
|
||||
natsOpts := nats.GetDefaultOptions()
|
||||
if n, ok := options.Context.Value(optionsKey{}).(nats.Options); ok {
|
||||
natsOpts = n
|
||||
}
|
||||
n.Once.Do(func() {
|
||||
n.nopts = nats.GetDefaultOptions()
|
||||
})
|
||||
|
||||
var drain bool
|
||||
if _, ok := options.Context.Value(drainSubscriptionKey{}).(bool); ok {
|
||||
drain = true
|
||||
if nopts, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok {
|
||||
n.nopts = nopts
|
||||
}
|
||||
|
||||
// broker.Options have higher priority than nats.Options
|
||||
// only if Addrs, Secure or TLSConfig were not set through a broker.Option
|
||||
// we read them from nats.Option
|
||||
if len(options.Addrs) == 0 {
|
||||
options.Addrs = natsOpts.Servers
|
||||
if len(n.opts.Addrs) == 0 {
|
||||
n.opts.Addrs = n.nopts.Servers
|
||||
}
|
||||
|
||||
if !options.Secure {
|
||||
options.Secure = natsOpts.Secure
|
||||
if !n.opts.Secure {
|
||||
n.opts.Secure = n.nopts.Secure
|
||||
}
|
||||
|
||||
if options.TLSConfig == nil {
|
||||
options.TLSConfig = natsOpts.TLSConfig
|
||||
if n.opts.TLSConfig == nil {
|
||||
n.opts.TLSConfig = n.nopts.TLSConfig
|
||||
}
|
||||
n.addrs = setAddrs(n.opts.Addrs)
|
||||
|
||||
return &natsBroker{
|
||||
opts: options,
|
||||
nopts: natsOpts,
|
||||
addrs: setAddrs(options.Addrs),
|
||||
drain: drain,
|
||||
if n.opts.Context.Value(drainConnectionKey{}) != nil {
|
||||
n.drain = true
|
||||
n.closeCh = make(chan error)
|
||||
n.nopts.ClosedCB = n.onClose
|
||||
n.nopts.AsyncErrorCB = n.onAsyncError
|
||||
}
|
||||
}
|
||||
|
||||
func (n *natsBroker) onClose(conn *nats.Conn) {
|
||||
n.closeCh <- nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) {
|
||||
// There are kinds of different async error nats might callback, but we are interested
|
||||
// in ErrDrainTimeout only here.
|
||||
if err == nats.ErrDrainTimeout {
|
||||
n.closeCh <- err
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
type optionsKey struct{}
|
||||
type drainConnectionKey struct{}
|
||||
type drainSubscriptionKey struct{}
|
||||
|
||||
// Options accepts nats.Options
|
||||
func Options(opts nats.Options) broker.Option {
|
||||
@@ -16,10 +15,5 @@ func Options(opts nats.Options) broker.Option {
|
||||
|
||||
// DrainConnection will drain subscription on close
|
||||
func DrainConnection() broker.Option {
|
||||
return setBrokerOption(drainConnectionKey{}, true)
|
||||
}
|
||||
|
||||
// DrainSubscription will drain pending messages when unsubscribe
|
||||
func DrainSubscription() broker.SubscribeOption {
|
||||
return setSubscribeOption(drainSubscriptionKey{}, true)
|
||||
return setBrokerOption(drainConnectionKey{}, struct{}{})
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/transport"
|
||||
|
||||
"github.com/micro/go-micro/util/buf"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding"
|
||||
@@ -73,10 +72,11 @@ func (g *grpcClient) next(request client.Request, opts client.CallOptions) (sele
|
||||
|
||||
// get next nodes from the selector
|
||||
next, err := g.opts.Selector.Select(service, opts.SelectOptions...)
|
||||
if err != nil && err == selector.ErrNotFound {
|
||||
return nil, errors.NotFound("go.micro.client", err.Error())
|
||||
} else if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
return next, nil
|
||||
@@ -350,15 +350,17 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
|
||||
|
||||
// select next node
|
||||
node, err := next()
|
||||
if err != nil && err == selector.ErrNotFound {
|
||||
return errors.NotFound("go.micro.client", err.Error())
|
||||
} else if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
service := req.Service()
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
// make the call
|
||||
err = gcall(ctx, node, req, rsp, callOpts)
|
||||
g.opts.Selector.Mark(req.Service(), node, err)
|
||||
g.opts.Selector.Mark(service, node, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -429,14 +431,16 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
|
||||
}
|
||||
|
||||
node, err := next()
|
||||
if err != nil && err == selector.ErrNotFound {
|
||||
return nil, errors.NotFound("go.micro.client", err.Error())
|
||||
} else if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
service := req.Service()
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
stream, err := g.stream(ctx, node, req, callOpts)
|
||||
g.opts.Selector.Mark(req.Service(), node, err)
|
||||
g.opts.Selector.Mark(service, node, err)
|
||||
return stream, err
|
||||
}
|
||||
|
||||
@@ -486,14 +490,13 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
|
||||
}
|
||||
md["Content-Type"] = p.ContentType()
|
||||
|
||||
cf, err := g.newCodec(p.ContentType())
|
||||
cf, err := g.newGRPCCodec(p.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
b := buf.New(nil)
|
||||
|
||||
if err := cf(b).Write(&codec.Message{Type: codec.Event}, p.Payload()); err != nil {
|
||||
b, err := cf.Marshal(p.Payload())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
@@ -503,7 +506,7 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
|
||||
|
||||
return g.opts.Broker.Publish(p.Topic(), &broker.Message{
|
||||
Header: md,
|
||||
Body: b.Bytes(),
|
||||
Body: b,
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -96,19 +96,14 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
}
|
||||
}
|
||||
|
||||
var grr error
|
||||
c, err := r.pool.Get(address, transport.WithTimeout(opts.DialTimeout))
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
// defer execution of release
|
||||
r.pool.Release(c, grr)
|
||||
}()
|
||||
|
||||
seq := atomic.LoadUint64(&r.seq)
|
||||
atomic.AddUint64(&r.seq, 1)
|
||||
codec := newRpcCodec(msg, c, cf)
|
||||
codec := newRpcCodec(msg, c, cf, "")
|
||||
|
||||
rsp := &rpcResponse{
|
||||
socket: c,
|
||||
@@ -116,15 +111,19 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
}
|
||||
|
||||
stream := &rpcStream{
|
||||
id: fmt.Sprintf("%v", seq),
|
||||
context: ctx,
|
||||
request: req,
|
||||
response: rsp,
|
||||
codec: codec,
|
||||
closed: make(chan bool),
|
||||
id: fmt.Sprintf("%v", seq),
|
||||
release: func(err error) { r.pool.Release(c, err) },
|
||||
sendEOS: false,
|
||||
}
|
||||
// close the stream on exiting this function
|
||||
defer stream.Close()
|
||||
|
||||
// wait for error response
|
||||
ch := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
@@ -150,14 +149,26 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
|
||||
ch <- nil
|
||||
}()
|
||||
|
||||
var grr error
|
||||
|
||||
select {
|
||||
case err := <-ch:
|
||||
grr = err
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
grr = ctx.Err()
|
||||
return errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
|
||||
}
|
||||
|
||||
// set the stream error
|
||||
if grr != nil {
|
||||
stream.Lock()
|
||||
stream.err = grr
|
||||
stream.Unlock()
|
||||
|
||||
return grr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) {
|
||||
@@ -201,12 +212,18 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
|
||||
}
|
||||
|
||||
c, err := r.opts.Transport.Dial(address, dOpts...)
|
||||
c, err := r.pool.Get(address, dOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
}
|
||||
|
||||
codec := newRpcCodec(msg, c, cf)
|
||||
// increment the sequence number
|
||||
seq := atomic.LoadUint64(&r.seq)
|
||||
atomic.AddUint64(&r.seq, 1)
|
||||
id := fmt.Sprintf("%v", seq)
|
||||
|
||||
// create codec with stream id
|
||||
codec := newRpcCodec(msg, c, cf, id)
|
||||
|
||||
rsp := &rpcResponse{
|
||||
socket: c,
|
||||
@@ -219,16 +236,24 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
}
|
||||
|
||||
stream := &rpcStream{
|
||||
id: id,
|
||||
context: ctx,
|
||||
request: req,
|
||||
response: rsp,
|
||||
closed: make(chan bool),
|
||||
codec: codec,
|
||||
// used to close the stream
|
||||
closed: make(chan bool),
|
||||
// signal the end of stream,
|
||||
sendEOS: true,
|
||||
// release func
|
||||
release: func(err error) { r.pool.Release(c, err) },
|
||||
}
|
||||
|
||||
// wait for error response
|
||||
ch := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
// send the first message
|
||||
ch <- stream.Send(req.Body())
|
||||
}()
|
||||
|
||||
@@ -242,6 +267,12 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
|
||||
}
|
||||
|
||||
if grr != nil {
|
||||
// set the error
|
||||
stream.Lock()
|
||||
stream.err = grr
|
||||
stream.Unlock()
|
||||
|
||||
// close the stream
|
||||
stream.Close()
|
||||
return nil, grr
|
||||
}
|
||||
@@ -312,10 +343,11 @@ func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, erro
|
||||
|
||||
// get next nodes from the selector
|
||||
next, err := r.opts.Selector.Select(service, opts.SelectOptions...)
|
||||
if err != nil && err == selector.ErrNotFound {
|
||||
return nil, errors.NotFound("go.micro.client", "service %s: %v", service, err.Error())
|
||||
} else if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %v", service, err.Error())
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
return next, nil
|
||||
@@ -375,15 +407,17 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
|
||||
// select next node
|
||||
node, err := next()
|
||||
if err != nil && err == selector.ErrNotFound {
|
||||
return errors.NotFound("go.micro.client", "service %s: %v", request.Service(), err.Error())
|
||||
} else if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "error getting next %s node: %v", request.Service(), err.Error())
|
||||
service := request.Service()
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
// make the call
|
||||
err = rcall(ctx, node, request, response, callOpts)
|
||||
r.opts.Selector.Mark(request.Service(), node, err)
|
||||
r.opts.Selector.Mark(service, node, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -452,14 +486,16 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
||||
}
|
||||
|
||||
node, err := next()
|
||||
if err != nil && err == selector.ErrNotFound {
|
||||
return nil, errors.NotFound("go.micro.client", "service %s: %v", request.Service(), err.Error())
|
||||
} else if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %v", request.Service(), err.Error())
|
||||
service := request.Service()
|
||||
if err != nil {
|
||||
if err == selector.ErrNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
|
||||
}
|
||||
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
|
||||
}
|
||||
|
||||
stream, err := r.stream(ctx, node, request, callOpts)
|
||||
r.opts.Selector.Mark(request.Service(), node, err)
|
||||
r.opts.Selector.Mark(service, node, err)
|
||||
return stream, err
|
||||
}
|
||||
|
||||
|
@@ -39,6 +39,9 @@ type rpcCodec struct {
|
||||
|
||||
req *transport.Message
|
||||
buf *readWriteCloser
|
||||
|
||||
// signify if its a stream
|
||||
stream string
|
||||
}
|
||||
|
||||
type readWriteCloser struct {
|
||||
@@ -113,7 +116,7 @@ func getHeaders(m *codec.Message) {
|
||||
}
|
||||
}
|
||||
|
||||
func setHeaders(m *codec.Message) {
|
||||
func setHeaders(m *codec.Message, stream string) {
|
||||
set := func(hdr, v string) {
|
||||
if len(v) == 0 {
|
||||
return
|
||||
@@ -126,6 +129,11 @@ func setHeaders(m *codec.Message) {
|
||||
set("Micro-Service", m.Target)
|
||||
set("Micro-Method", m.Method)
|
||||
set("Micro-Endpoint", m.Endpoint)
|
||||
set("Micro-Error", m.Error)
|
||||
|
||||
if len(stream) > 0 {
|
||||
set("Micro-Stream", stream)
|
||||
}
|
||||
}
|
||||
|
||||
// setupProtocol sets up the old protocol
|
||||
@@ -149,7 +157,7 @@ func setupProtocol(msg *transport.Message, node *registry.Node) codec.NewCodec {
|
||||
return defaultCodecs[msg.Header["Content-Type"]]
|
||||
}
|
||||
|
||||
func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec) codec.Codec {
|
||||
func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCodec, stream string) codec.Codec {
|
||||
rwc := &readWriteCloser{
|
||||
wbuf: bytes.NewBuffer(nil),
|
||||
rbuf: bytes.NewBuffer(nil),
|
||||
@@ -159,6 +167,7 @@ func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCod
|
||||
client: client,
|
||||
codec: c(rwc),
|
||||
req: req,
|
||||
stream: stream,
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -177,7 +186,7 @@ func (c *rpcCodec) Write(m *codec.Message, body interface{}) error {
|
||||
}
|
||||
|
||||
// set the mucp headers
|
||||
setHeaders(m)
|
||||
setHeaders(m, c.stream)
|
||||
|
||||
// if body is bytes Frame don't encode
|
||||
if body != nil {
|
||||
@@ -240,6 +249,12 @@ func (c *rpcCodec) ReadHeader(m *codec.Message, r codec.MessageType) error {
|
||||
|
||||
func (c *rpcCodec) ReadBody(b interface{}) error {
|
||||
// read body
|
||||
// read raw data
|
||||
if v, ok := b.(*raw.Frame); ok {
|
||||
v.Data = c.buf.rbuf.Bytes()
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.codec.ReadBody(b); err != nil {
|
||||
return errors.InternalServerError("go.micro.client.codec", err.Error())
|
||||
}
|
||||
|
@@ -18,6 +18,12 @@ type rpcStream struct {
|
||||
response Response
|
||||
codec codec.Codec
|
||||
context context.Context
|
||||
|
||||
// signal whether we should send EOS
|
||||
sendEOS bool
|
||||
|
||||
// release releases the connection back to the pool
|
||||
release func(err error)
|
||||
}
|
||||
|
||||
func (r *rpcStream) isClosed() bool {
|
||||
@@ -120,6 +126,26 @@ func (r *rpcStream) Close() error {
|
||||
return nil
|
||||
default:
|
||||
close(r.closed)
|
||||
return r.codec.Close()
|
||||
|
||||
// send the end of stream message
|
||||
if r.sendEOS {
|
||||
// no need to check for error
|
||||
r.codec.Write(&codec.Message{
|
||||
Id: r.id,
|
||||
Target: r.request.Service(),
|
||||
Method: r.request.Method(),
|
||||
Endpoint: r.request.Endpoint(),
|
||||
Type: codec.Error,
|
||||
Error: lastStreamResponseError,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
err := r.codec.Close()
|
||||
|
||||
// release the connection
|
||||
r.release(r.Error())
|
||||
|
||||
// return the codec error
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@@ -51,6 +51,9 @@ func (c *registrySelector) Select(service string, opts ...SelectOption) (Next, e
|
||||
// if that fails go directly to the registry
|
||||
services, err := c.rc.GetService(service)
|
||||
if err != nil {
|
||||
if err == registry.ErrNotFound {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@@ -42,6 +42,7 @@ import (
|
||||
tgrpc "github.com/micro/go-micro/transport/grpc"
|
||||
thttp "github.com/micro/go-micro/transport/http"
|
||||
tmem "github.com/micro/go-micro/transport/memory"
|
||||
"github.com/micro/go-micro/transport/quic"
|
||||
)
|
||||
|
||||
type Cmd interface {
|
||||
@@ -211,6 +212,7 @@ var (
|
||||
"memory": tmem.NewTransport,
|
||||
"http": thttp.NewTransport,
|
||||
"grpc": tgrpc.NewTransport,
|
||||
"quic": quic.NewTransport,
|
||||
}
|
||||
|
||||
// used for default selection as the fall back
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -8,10 +9,15 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/config/source/env"
|
||||
"github.com/micro/go-micro/config/source/file"
|
||||
)
|
||||
|
||||
var (
|
||||
sep = string(os.PathSeparator)
|
||||
)
|
||||
|
||||
func createFileForIssue18(t *testing.T, content string) *os.File {
|
||||
data := []byte(content)
|
||||
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))
|
||||
@@ -116,3 +122,57 @@ func TestConfigMerge(t *testing.T) {
|
||||
actualHost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileChange(t *testing.T) {
|
||||
// create a temp file
|
||||
fileName := uuid.New().String() + "testWatcher.json"
|
||||
f, err := os.OpenFile("."+sep+fileName, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer os.Remove("." + sep + fileName)
|
||||
|
||||
// load the file
|
||||
if err := Load(file.NewSource(
|
||||
file.WithPath("." + sep + fileName),
|
||||
)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// watch changes
|
||||
watcher, err := Watch()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
changeTimes := 0
|
||||
go func() {
|
||||
for {
|
||||
v, err := watcher.Next()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
changeTimes++
|
||||
t.Logf("file change,%s", string(v.Bytes()))
|
||||
}
|
||||
}()
|
||||
|
||||
content := map[int]string{}
|
||||
// change the file
|
||||
for i := 0; i < 5; i++ {
|
||||
content[i] = time.Now().String()
|
||||
bytes, _ := json.Marshal(content)
|
||||
f.Truncate(0)
|
||||
f.Seek(0, 0)
|
||||
if _, err := f.Write(bytes); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
if changeTimes != 4 {
|
||||
t.Error(fmt.Errorf("watcher error: change times %d is not enough", changeTimes))
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
//+build !linux
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
|
72
config/source/file/watcher_linux.go
Normal file
72
config/source/file/watcher_linux.go
Normal file
@@ -0,0 +1,72 @@
|
||||
//+build linux
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/micro/go-micro/config/source"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
f *file
|
||||
|
||||
fw *fsnotify.Watcher
|
||||
exit chan bool
|
||||
}
|
||||
|
||||
func newWatcher(f *file) (source.Watcher, error) {
|
||||
fw, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fw.Add(f.path)
|
||||
|
||||
return &watcher{
|
||||
f: f,
|
||||
fw: fw,
|
||||
exit: make(chan bool),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
// is it closed?
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil, errors.New("watcher stopped")
|
||||
default:
|
||||
}
|
||||
|
||||
// try get the event
|
||||
select {
|
||||
case event, _ := <-w.fw.Events:
|
||||
if event.Op == fsnotify.Rename {
|
||||
// check existence of file, and add watch again
|
||||
_, err := os.Stat(event.Name)
|
||||
if err == nil || os.IsExist(err) {
|
||||
w.fw.Add(event.Name)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := w.f.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add path again for the event bug of fsnotify
|
||||
w.fw.Add(w.f.path)
|
||||
|
||||
return c, nil
|
||||
case err := <-w.fw.Errors:
|
||||
return nil, err
|
||||
case <-w.exit:
|
||||
return nil, errors.New("watcher stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() error {
|
||||
return w.fw.Close()
|
||||
}
|
@@ -27,7 +27,7 @@ Specify source with data
|
||||
|
||||
```go
|
||||
memorySource := memory.NewSource(
|
||||
memory.WithData(data),
|
||||
memory.WithJSON(data),
|
||||
)
|
||||
```
|
||||
|
||||
|
@@ -18,6 +18,7 @@ type memory struct {
|
||||
func (s *memory) Read() (*source.ChangeSet, error) {
|
||||
s.RLock()
|
||||
cs := &source.ChangeSet{
|
||||
Format: s.ChangeSet.Format,
|
||||
Timestamp: s.ChangeSet.Timestamp,
|
||||
Data: s.ChangeSet.Data,
|
||||
Checksum: s.ChangeSet.Checksum,
|
||||
|
41
debug/handler/debug.go
Normal file
41
debug/handler/debug.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
proto "github.com/micro/go-micro/debug/proto"
|
||||
)
|
||||
|
||||
type Debug struct {
|
||||
proto.DebugHandler
|
||||
started int64
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultHandler = newDebug()
|
||||
)
|
||||
|
||||
func newDebug() *Debug {
|
||||
return &Debug{
|
||||
started: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error {
|
||||
rsp.Status = "ok"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Debug) Stats(ctx context.Context, req *proto.StatsRequest, rsp *proto.StatsResponse) error {
|
||||
var mstat runtime.MemStats
|
||||
runtime.ReadMemStats(&mstat)
|
||||
|
||||
rsp.Started = uint64(d.started)
|
||||
rsp.Uptime = uint64(time.Now().Unix() - d.started)
|
||||
rsp.Memory = mstat.Alloc
|
||||
rsp.Gc = mstat.PauseTotalNs
|
||||
rsp.Threads = uint64(runtime.NumGoroutine())
|
||||
return nil
|
||||
}
|
108
debug/proto/debug.micro.go
Normal file
108
debug/proto/debug.micro.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: micro/go-micro/debug/proto/debug.proto
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
client "github.com/micro/go-micro/client"
|
||||
server "github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Client API for Debug service
|
||||
|
||||
type DebugService interface {
|
||||
Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error)
|
||||
Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error)
|
||||
}
|
||||
|
||||
type debugService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewDebugService(name string, c client.Client) DebugService {
|
||||
if c == nil {
|
||||
c = client.NewClient()
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = "debug"
|
||||
}
|
||||
return &debugService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *debugService) Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Debug.Health", in)
|
||||
out := new(HealthResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *debugService) Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Debug.Stats", in)
|
||||
out := new(StatsResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Debug service
|
||||
|
||||
type DebugHandler interface {
|
||||
Health(context.Context, *HealthRequest, *HealthResponse) error
|
||||
Stats(context.Context, *StatsRequest, *StatsResponse) error
|
||||
}
|
||||
|
||||
func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.HandlerOption) error {
|
||||
type debug interface {
|
||||
Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error
|
||||
Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error
|
||||
}
|
||||
type Debug struct {
|
||||
debug
|
||||
}
|
||||
h := &debugHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Debug{h}, opts...))
|
||||
}
|
||||
|
||||
type debugHandler struct {
|
||||
DebugHandler
|
||||
}
|
||||
|
||||
func (h *debugHandler) Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error {
|
||||
return h.DebugHandler.Health(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *debugHandler) Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error {
|
||||
return h.DebugHandler.Stats(ctx, in, out)
|
||||
}
|
336
debug/proto/debug.pb.go
Normal file
336
debug/proto/debug.pb.go
Normal file
@@ -0,0 +1,336 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: micro/go-micro/debug/proto/debug.proto
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type HealthRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HealthRequest) Reset() { *m = HealthRequest{} }
|
||||
func (m *HealthRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*HealthRequest) ProtoMessage() {}
|
||||
func (*HealthRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f25415e61bccfa1f, []int{0}
|
||||
}
|
||||
|
||||
func (m *HealthRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HealthRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HealthRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HealthRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *HealthRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HealthRequest.Merge(m, src)
|
||||
}
|
||||
func (m *HealthRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_HealthRequest.Size(m)
|
||||
}
|
||||
func (m *HealthRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HealthRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HealthRequest proto.InternalMessageInfo
|
||||
|
||||
type HealthResponse struct {
|
||||
// default: ok
|
||||
Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HealthResponse) Reset() { *m = HealthResponse{} }
|
||||
func (m *HealthResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*HealthResponse) ProtoMessage() {}
|
||||
func (*HealthResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f25415e61bccfa1f, []int{1}
|
||||
}
|
||||
|
||||
func (m *HealthResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HealthResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HealthResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HealthResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *HealthResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HealthResponse.Merge(m, src)
|
||||
}
|
||||
func (m *HealthResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_HealthResponse.Size(m)
|
||||
}
|
||||
func (m *HealthResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HealthResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HealthResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *HealthResponse) GetStatus() string {
|
||||
if m != nil {
|
||||
return m.Status
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type StatsRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *StatsRequest) Reset() { *m = StatsRequest{} }
|
||||
func (m *StatsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*StatsRequest) ProtoMessage() {}
|
||||
func (*StatsRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f25415e61bccfa1f, []int{2}
|
||||
}
|
||||
|
||||
func (m *StatsRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_StatsRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *StatsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_StatsRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *StatsRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StatsRequest.Merge(m, src)
|
||||
}
|
||||
func (m *StatsRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_StatsRequest.Size(m)
|
||||
}
|
||||
func (m *StatsRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_StatsRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_StatsRequest proto.InternalMessageInfo
|
||||
|
||||
type StatsResponse struct {
|
||||
// unix timestamp
|
||||
Started uint64 `protobuf:"varint,1,opt,name=started,proto3" json:"started,omitempty"`
|
||||
// in seconds
|
||||
Uptime uint64 `protobuf:"varint,2,opt,name=uptime,proto3" json:"uptime,omitempty"`
|
||||
// in bytes
|
||||
Memory uint64 `protobuf:"varint,3,opt,name=memory,proto3" json:"memory,omitempty"`
|
||||
// num threads
|
||||
Threads uint64 `protobuf:"varint,4,opt,name=threads,proto3" json:"threads,omitempty"`
|
||||
// total gc in nanoseconds
|
||||
Gc uint64 `protobuf:"varint,5,opt,name=gc,proto3" json:"gc,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *StatsResponse) Reset() { *m = StatsResponse{} }
|
||||
func (m *StatsResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*StatsResponse) ProtoMessage() {}
|
||||
func (*StatsResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f25415e61bccfa1f, []int{3}
|
||||
}
|
||||
|
||||
func (m *StatsResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_StatsResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *StatsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_StatsResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *StatsResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StatsResponse.Merge(m, src)
|
||||
}
|
||||
func (m *StatsResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_StatsResponse.Size(m)
|
||||
}
|
||||
func (m *StatsResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_StatsResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_StatsResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *StatsResponse) GetStarted() uint64 {
|
||||
if m != nil {
|
||||
return m.Started
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StatsResponse) GetUptime() uint64 {
|
||||
if m != nil {
|
||||
return m.Uptime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StatsResponse) GetMemory() uint64 {
|
||||
if m != nil {
|
||||
return m.Memory
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StatsResponse) GetThreads() uint64 {
|
||||
if m != nil {
|
||||
return m.Threads
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *StatsResponse) GetGc() uint64 {
|
||||
if m != nil {
|
||||
return m.Gc
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*HealthRequest)(nil), "HealthRequest")
|
||||
proto.RegisterType((*HealthResponse)(nil), "HealthResponse")
|
||||
proto.RegisterType((*StatsRequest)(nil), "StatsRequest")
|
||||
proto.RegisterType((*StatsResponse)(nil), "StatsResponse")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("micro/go-micro/debug/proto/debug.proto", fileDescriptor_f25415e61bccfa1f)
|
||||
}
|
||||
|
||||
var fileDescriptor_f25415e61bccfa1f = []byte{
|
||||
// 230 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0x41, 0x4b, 0xc4, 0x30,
|
||||
0x10, 0x85, 0xb7, 0x75, 0x5b, 0x71, 0xb0, 0x59, 0xc8, 0x41, 0xc2, 0x9e, 0x24, 0x07, 0x29, 0x88,
|
||||
0x59, 0xd0, 0xbf, 0xe0, 0xc1, 0x73, 0xbd, 0x0b, 0xd9, 0x76, 0xe8, 0x16, 0xac, 0xa9, 0x99, 0xe9,
|
||||
0xc1, 0xb3, 0x7f, 0x5c, 0x9a, 0xa4, 0x60, 0x6f, 0xef, 0xbd, 0xf0, 0x1e, 0xf9, 0x06, 0x1e, 0xc6,
|
||||
0xa1, 0xf5, 0xee, 0xd4, 0xbb, 0xa7, 0x28, 0x3a, 0x3c, 0xcf, 0xfd, 0x69, 0xf2, 0x8e, 0x93, 0x36,
|
||||
0x41, 0xeb, 0x03, 0x54, 0x6f, 0x68, 0x3f, 0xf9, 0xd2, 0xe0, 0xf7, 0x8c, 0xc4, 0xba, 0x06, 0xb1,
|
||||
0x06, 0x34, 0xb9, 0x2f, 0x42, 0x79, 0x07, 0x25, 0xb1, 0xe5, 0x99, 0x54, 0x76, 0x9f, 0xd5, 0x37,
|
||||
0x4d, 0x72, 0x5a, 0xc0, 0xed, 0x3b, 0x5b, 0xa6, 0xb5, 0xf9, 0x9b, 0x41, 0x95, 0x82, 0xd4, 0x54,
|
||||
0x70, 0x4d, 0x6c, 0x3d, 0x63, 0x17, 0xaa, 0xfb, 0x66, 0xb5, 0xcb, 0xe6, 0x3c, 0xf1, 0x30, 0xa2,
|
||||
0xca, 0xc3, 0x43, 0x72, 0x4b, 0x3e, 0xe2, 0xe8, 0xfc, 0x8f, 0xba, 0x8a, 0x79, 0x74, 0xcb, 0x12,
|
||||
0x5f, 0x3c, 0xda, 0x8e, 0xd4, 0x3e, 0x2e, 0x25, 0x2b, 0x05, 0xe4, 0x7d, 0xab, 0x8a, 0x10, 0xe6,
|
||||
0x7d, 0xfb, 0xfc, 0x01, 0xc5, 0xeb, 0xc2, 0x27, 0x1f, 0xa1, 0x8c, 0x20, 0x52, 0x98, 0x0d, 0xe2,
|
||||
0xf1, 0x60, 0xb6, 0x84, 0x7a, 0x27, 0x6b, 0x28, 0xc2, 0xd7, 0x65, 0x65, 0xfe, 0x33, 0x1d, 0x85,
|
||||
0xd9, 0x10, 0xe9, 0xdd, 0xb9, 0x0c, 0x77, 0x7b, 0xf9, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xb9,
|
||||
0x5f, 0xf7, 0x61, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// DebugClient is the client API for Debug service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type DebugClient interface {
|
||||
Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error)
|
||||
Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error)
|
||||
}
|
||||
|
||||
type debugClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewDebugClient(cc *grpc.ClientConn) DebugClient {
|
||||
return &debugClient{cc}
|
||||
}
|
||||
|
||||
func (c *debugClient) Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) {
|
||||
out := new(HealthResponse)
|
||||
err := c.cc.Invoke(ctx, "/Debug/Health", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *debugClient) Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) {
|
||||
out := new(StatsResponse)
|
||||
err := c.cc.Invoke(ctx, "/Debug/Stats", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DebugServer is the server API for Debug service.
|
||||
type DebugServer interface {
|
||||
Health(context.Context, *HealthRequest) (*HealthResponse, error)
|
||||
Stats(context.Context, *StatsRequest) (*StatsResponse, error)
|
||||
}
|
||||
|
||||
func RegisterDebugServer(s *grpc.Server, srv DebugServer) {
|
||||
s.RegisterService(&_Debug_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Debug_Health_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HealthRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DebugServer).Health(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/Debug/Health",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DebugServer).Health(ctx, req.(*HealthRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Debug_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StatsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DebugServer).Stats(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/Debug/Stats",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DebugServer).Stats(ctx, req.(*StatsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Debug_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "Debug",
|
||||
HandlerType: (*DebugServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Health",
|
||||
Handler: _Debug_Health_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Stats",
|
||||
Handler: _Debug_Stats_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "micro/go-micro/debug/proto/debug.proto",
|
||||
}
|
@@ -1,13 +1,9 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// This is commented out due to import cycles.
|
||||
// But its what we expect the RPC service to
|
||||
// return.
|
||||
//
|
||||
// service Debug {
|
||||
// rpc Health(HealthRequest) returns (HealthResponse) {}
|
||||
// rpc Stats(StatsRequest) returns (StatsResponse) {}
|
||||
// }
|
||||
service Debug {
|
||||
rpc Health(HealthRequest) returns (HealthResponse) {}
|
||||
rpc Stats(StatsRequest) returns (StatsResponse) {}
|
||||
}
|
||||
|
||||
message HealthRequest {
|
||||
}
|
@@ -5,8 +5,8 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
proto "github.com/micro/go-micro/debug/proto"
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
proto "github.com/micro/go-micro/server/debug/proto"
|
||||
)
|
||||
|
||||
func TestFunction(t *testing.T) {
|
||||
|
48
go.mod
48
go.mod
@@ -3,8 +3,9 @@ module github.com/micro/go-micro
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.41.0 // indirect
|
||||
cloud.google.com/go v0.44.0 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
@@ -14,17 +15,17 @@ require (
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
|
||||
github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/fsouza/go-dockerclient v1.4.1
|
||||
github.com/fsouza/go-dockerclient v1.4.2
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gliderlabs/ssh v0.2.2 // indirect
|
||||
github.com/go-kit/kit v0.9.0 // indirect
|
||||
github.com/go-log/log v0.1.0
|
||||
github.com/go-playground/locales v0.12.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.16.0 // indirect
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70 // indirect
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/handlers v1.4.1
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
github.com/gorilla/handlers v1.4.2
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/hashicorp/consul/api v1.1.0
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0 // indirect
|
||||
@@ -33,26 +34,28 @@ require (
|
||||
github.com/hashicorp/go-rootcerts v1.0.1 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/go-version v1.2.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.3 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/hashicorp/mdns v1.0.1 // indirect
|
||||
github.com/hashicorp/memberlist v0.1.4
|
||||
github.com/hashicorp/serf v0.8.3 // indirect
|
||||
github.com/imdario/mergo v0.3.7
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1
|
||||
github.com/json-iterator/go v1.1.6
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c // indirect
|
||||
github.com/json-iterator/go v1.1.7
|
||||
github.com/kisielk/errcheck v1.2.0 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kr/pty v1.1.8 // indirect
|
||||
github.com/leodido/go-urn v1.1.0 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031
|
||||
github.com/lucas-clemente/quic-go v0.12.0
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/micro/cli v0.2.0
|
||||
github.com/micro/mdns v0.2.0
|
||||
github.com/micro/mdns v0.3.0
|
||||
github.com/miekg/dns v1.1.15 // indirect
|
||||
github.com/mitchellh/gox v1.0.1 // indirect
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/nats-io/nats.go v1.8.1
|
||||
github.com/nats-io/nkeys v0.1.0 // indirect
|
||||
github.com/nlopes/slack v0.5.0
|
||||
@@ -61,24 +64,17 @@ require (
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/posener/complete v1.2.1 // indirect
|
||||
github.com/prometheus/common v0.6.0 // indirect
|
||||
github.com/prometheus/procfs v0.0.3 // indirect
|
||||
github.com/prometheus/client_golang v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2 // indirect
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f // indirect
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9 // indirect
|
||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9 // indirect
|
||||
golang.org/x/mod v0.1.0 // indirect
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 // indirect
|
||||
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 // indirect
|
||||
google.golang.org/grpc v1.22.0
|
||||
gopkg.in/go-playground/validator.v9 v9.29.0
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1 // indirect
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0
|
||||
golang.org/x/mobile v0.0.0-20190806162312-597adff16ade // indirect
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
|
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e // indirect
|
||||
golang.org/x/tools v0.0.0-20190809145639-6d4652c779c4 // indirect
|
||||
google.golang.org/grpc v1.22.1
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4
|
||||
honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b // indirect
|
||||
honnef.co/go/tools v0.0.1-2019.2.2 // indirect
|
||||
)
|
||||
|
66
go.sum
66
go.sum
@@ -3,15 +3,22 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
|
||||
cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
|
||||
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
|
||||
cloud.google.com/go v0.44.0/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.13/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -26,6 +33,7 @@ github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
|
||||
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
@@ -43,8 +51,12 @@ github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 h1:dmUn0SuGx7unKFwxyeQ/oLUHhEfZosEDrpmYM+6MTuc=
|
||||
github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b h1:+Ga+YpCDpcY1fln6GI0fiiirpqHGcob5/Vk3oKNuGdU=
|
||||
github.com/docker/docker v1.4.2-0.20190710153559-aa8249ae1b8b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
@@ -61,6 +73,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsouza/go-dockerclient v1.4.1 h1:W7wuJ3IB48WYZv/UBk9dCTIb9oX805+L9KIm65HcUYs=
|
||||
github.com/fsouza/go-dockerclient v1.4.1/go.mod h1:PUNHxbowDqRXfRgZqMz1OeGtbWC6VKyZvJ99hDjB0qs=
|
||||
github.com/fsouza/go-dockerclient v1.4.2 h1:dl6GfIWS5Qn4C6OfSnnoe6YuOV8lvKAE8W/YD1Q7udo=
|
||||
github.com/fsouza/go-dockerclient v1.4.2/go.mod h1:COunfLZrsdwX/j3YVDAG8gIw3KutrI0x1+vGEJ5zxdI=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
@@ -96,9 +110,12 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -108,6 +125,8 @@ github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZs
|
||||
github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/handlers v1.4.1 h1:BHvcRGJe/TrL+OqFxoKQGddTgeibiOjaBssV5a/N9sw=
|
||||
github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
@@ -150,6 +169,8 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
@@ -174,12 +195,16 @@ github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1 h1:lnrOS18wZBYrzdD
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c h1:VAx3LRNjVNvjtgO7KFRuT/3aye/0zJvwn01rHSfoolo=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@@ -197,6 +222,8 @@ github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031 h1:wjcGvg
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190710050138-1441923ab031/go.mod h1:lb5aAxL68VvhZ00e7yYuQVK/9FLggtYy4qo7oI5qzqA=
|
||||
github.com/lucas-clemente/quic-go v0.11.2 h1:Mop0ac3zALaBR3wGs6j8OYe/tcFvFsxTUFMkE/7yUOI=
|
||||
github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||
github.com/lucas-clemente/quic-go v0.12.0 h1:dYHUyB50gEQlK3KqytmNySzuyzAcaQ3iuI2ZReAfVrE=
|
||||
github.com/lucas-clemente/quic-go v0.12.0/go.mod h1:UXJJPE4RfFef/xPO5wQm0tITK8gNfqwTxjbE7s3Vb8s=
|
||||
github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI=
|
||||
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
|
||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
@@ -204,6 +231,8 @@ github.com/marten-seemann/qtls v0.2.4 h1:mCJ6i1jAqcsm9XODrSGvXECodoAb1STta+TkxJC
|
||||
github.com/marten-seemann/qtls v0.2.4/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/marten-seemann/qtls v0.3.1 h1:ySYIvhFjFY2JsNHY6VACvomMEDy3EvdPA6yciUFAiHw=
|
||||
github.com/marten-seemann/qtls v0.3.1/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/marten-seemann/qtls v0.3.2 h1:O7awy4bHEzSX/K3h+fZig3/Vo03s/RxlxgsAk9sYamI=
|
||||
github.com/marten-seemann/qtls v0.3.2/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@@ -219,6 +248,8 @@ github.com/micro/mdns v0.1.1-0.20190729112526-ef68c9635478 h1:L6jnZZ763dMLlvst8P
|
||||
github.com/micro/mdns v0.1.1-0.20190729112526-ef68c9635478/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/micro/mdns v0.2.0 h1:/+/n2PSiJURrXsBIGtfCz0hex/XYKqVsn51GAGdFrOE=
|
||||
github.com/micro/mdns v0.2.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/micro/mdns v0.3.0 h1:bYycYe+98AXR3s8Nq5qvt6C573uFTDPIYzJemWON0QE=
|
||||
github.com/micro/mdns v0.3.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM=
|
||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
@@ -241,11 +272,14 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/nats.go v1.8.1 h1:6lF/f1/NN6kzUDBz6pyvQDEXO39jqXcWRLu/tKjtOUQ=
|
||||
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
|
||||
github.com/nats-io/nkeys v0.0.2 h1:+qM7QpgXnvDDixitZtQUBDY9w/s9mu1ghS+JIbsrx6M=
|
||||
@@ -282,6 +316,7 @@ github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DK
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
@@ -301,6 +336,7 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
@@ -339,9 +375,11 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190618124811-92942e4437e2/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -350,6 +388,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20190806162312-597adff16ade/go.mod h1:AlhUtkH4DA4asiFC5RgK7ZKmauvtkAVcy9L0epCzlWo=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -375,6 +414,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -408,6 +449,12 @@ golang.org/x/sys v0.0.0-20190621062556-bf70e4678053/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e h1:TsjK5I7fXk8f2FQrgu6NS7i5Qih3knl2FL1htyguLRE=
|
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -429,12 +476,19 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
||||
golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190620191750-1fa568393b23/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190710184609-286818132824/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190807201305-8be58fba6352/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190809145639-6d4652c779c4/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -452,12 +506,17 @@ google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dT
|
||||
google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 h1:5pOB7se0B2+IssELuQUs6uoBgYJenkU2AQlvopc2sRw=
|
||||
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -465,18 +524,24 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.0 h1:5ofssLNYgAA/inWn6rTZ4juWpRJUwEnXc1LG2IeXwgQ=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
|
||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1 h1:OkK1DmefDy1Z6Veu82wdNj/cLpYORhdX4qdaYCPwc7s=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.11.0 h1:cJwWgJ0DXifrNrXM6RGN1Y2yR60Rr1zQ9Q5DX5S9qgU=
|
||||
gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0 h1:CKgvBCJCcdfNnyXPYI4Cp8PaDDAmAPEN0CtfEdEAbd8=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0/go.mod h1:zjlNnzc1Wjn43v3Mtii7RVxiReNP0fIu9npcXKzuNp4=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4 h1:hpHWhzn4jTCsAJZZ2loNKfy2QWyPDRJVl3aTFXeMW8g=
|
||||
gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@@ -491,4 +556,5 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw=
|
||||
honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
318
monitor/default.go
Normal file
318
monitor/default.go
Normal file
@@ -0,0 +1,318 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
pb "github.com/micro/go-micro/debug/proto"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/registry/cache"
|
||||
)
|
||||
|
||||
type monitor struct {
|
||||
options Options
|
||||
|
||||
exit chan bool
|
||||
registry cache.Cache
|
||||
client client.Client
|
||||
|
||||
sync.RWMutex
|
||||
running bool
|
||||
services map[string]*Status
|
||||
}
|
||||
|
||||
// check provides binary running/failed status.
|
||||
// In the event Debug.Health cannot be called on a service we reap the node.
|
||||
func (m *monitor) check(service string) (*Status, error) {
|
||||
services, err := m.registry.GetService(service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create debug client
|
||||
debug := pb.NewDebugService(service, m.client)
|
||||
|
||||
var status *Status
|
||||
var gerr error
|
||||
|
||||
// iterate through multiple versions of a service
|
||||
for _, service := range services {
|
||||
for _, node := range service.Nodes {
|
||||
// TODO: checks that are not just RPC based
|
||||
// TODO: better matching of the protocol
|
||||
// TODO: maybe everything has to be a go-micro service?
|
||||
if node.Metadata["server"] != m.client.String() {
|
||||
continue
|
||||
}
|
||||
|
||||
rsp, err := debug.Health(
|
||||
context.Background(),
|
||||
// empty health request
|
||||
&pb.HealthRequest{},
|
||||
// call this specific node
|
||||
client.WithAddress(node.Address),
|
||||
// retry in the event of failure
|
||||
client.WithRetries(3),
|
||||
)
|
||||
if err != nil {
|
||||
// reap the dead node
|
||||
m.registry.Deregister(®istry.Service{
|
||||
Name: service.Name,
|
||||
Version: service.Version,
|
||||
Nodes: []*registry.Node{node},
|
||||
})
|
||||
|
||||
// save the error
|
||||
gerr = err
|
||||
continue
|
||||
}
|
||||
|
||||
// expecting ok response status
|
||||
if rsp.Status != "ok" {
|
||||
gerr = errors.New(rsp.Status)
|
||||
continue
|
||||
}
|
||||
|
||||
// no error set status
|
||||
status = &Status{
|
||||
Code: StatusRunning,
|
||||
Info: "running",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we got the success case return it
|
||||
if status != nil {
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// if gerr is not nil return it
|
||||
if gerr != nil {
|
||||
return &Status{
|
||||
Code: StatusFailed,
|
||||
Info: "not running",
|
||||
Error: gerr.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// otherwise unknown status
|
||||
return &Status{
|
||||
Code: StatusUnknown,
|
||||
Info: "unknown status",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *monitor) reap() {
|
||||
services, err := m.registry.ListServices()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
serviceMap := make(map[string]bool)
|
||||
for _, service := range services {
|
||||
serviceMap[service.Name] = true
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// range over our watched services
|
||||
for service, _ := range m.services {
|
||||
// check if the service exists in the registry
|
||||
if !serviceMap[service] {
|
||||
// if not, delete it in our status map
|
||||
delete(m.services, service)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *monitor) run() {
|
||||
// check the status every tick
|
||||
t := time.NewTicker(time.Minute)
|
||||
defer t.Stop()
|
||||
|
||||
// reap dead services
|
||||
t2 := time.NewTicker(time.Hour)
|
||||
defer t2.Stop()
|
||||
|
||||
// list the known services
|
||||
services, _ := m.registry.ListServices()
|
||||
|
||||
// create a check chan of same length
|
||||
check := make(chan string, len(services))
|
||||
|
||||
// front-load the services to watch
|
||||
for _, service := range services {
|
||||
check <- service.Name
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
// exit if we're told to
|
||||
case <-m.exit:
|
||||
return
|
||||
// check a service when told to
|
||||
case service := <-check:
|
||||
// check the status
|
||||
status, err := m.check(service)
|
||||
if err != nil {
|
||||
status = &Status{
|
||||
Code: StatusUnknown,
|
||||
Info: "unknown status",
|
||||
}
|
||||
}
|
||||
|
||||
// save the status
|
||||
m.Lock()
|
||||
m.services[service] = status
|
||||
m.Unlock()
|
||||
// on the tick interval get all services and issue a check
|
||||
case <-t.C:
|
||||
// create a list of services
|
||||
serviceMap := make(map[string]bool)
|
||||
|
||||
m.RLock()
|
||||
for service, _ := range m.services {
|
||||
serviceMap[service] = true
|
||||
}
|
||||
m.RUnlock()
|
||||
|
||||
go func() {
|
||||
// check the status of all watched services
|
||||
for service, _ := range serviceMap {
|
||||
select {
|
||||
case <-m.exit:
|
||||
return
|
||||
case check <- service:
|
||||
default:
|
||||
// barf if we block
|
||||
}
|
||||
}
|
||||
|
||||
// list services
|
||||
services, _ := m.registry.ListServices()
|
||||
|
||||
for _, service := range services {
|
||||
// start watching the service
|
||||
if ok := serviceMap[service.Name]; !ok {
|
||||
m.Watch(service.Name)
|
||||
}
|
||||
}
|
||||
}()
|
||||
case <-t2.C:
|
||||
// reap any dead/non-existent services
|
||||
m.reap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *monitor) Reap(service string) error {
|
||||
services, err := m.registry.GetService(service)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
delete(m.services, service)
|
||||
for _, service := range services {
|
||||
m.registry.Deregister(service)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *monitor) Status(service string) (Status, error) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
if status, ok := m.services[service]; ok {
|
||||
return *status, nil
|
||||
}
|
||||
return Status{}, ErrNotWatching
|
||||
}
|
||||
|
||||
func (m *monitor) Watch(service string) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// check if we're watching
|
||||
if _, ok := m.services[service]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get the status
|
||||
status, err := m.check(service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set the status
|
||||
m.services[service] = status
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *monitor) Run() error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if m.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
// reset the exit channel
|
||||
m.exit = make(chan bool)
|
||||
// setup a new cache
|
||||
m.registry = cache.New(m.options.Registry)
|
||||
|
||||
// start running
|
||||
go m.run()
|
||||
|
||||
// set to running
|
||||
m.running = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *monitor) Stop() error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if !m.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case <-m.exit:
|
||||
return nil
|
||||
default:
|
||||
close(m.exit)
|
||||
for s, _ := range m.services {
|
||||
delete(m.services, s)
|
||||
}
|
||||
m.registry.Stop()
|
||||
m.running = false
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMonitor(opts ...Option) Monitor {
|
||||
options := Options{
|
||||
Client: client.DefaultClient,
|
||||
Registry: registry.DefaultRegistry,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
return &monitor{
|
||||
options: options,
|
||||
exit: make(chan bool),
|
||||
client: options.Client,
|
||||
registry: cache.New(options.Registry),
|
||||
services: make(map[string]*Status),
|
||||
}
|
||||
}
|
37
monitor/default_test.go
Normal file
37
monitor/default_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMonitor(t *testing.T) {
|
||||
// create new monitor
|
||||
m := NewMonitor()
|
||||
|
||||
if err := m.Run(); err != nil {
|
||||
t.Fatalf("failed to stop monitor: %v", err)
|
||||
}
|
||||
|
||||
services := []string{"foo", "bar", "baz"}
|
||||
|
||||
for _, service := range services {
|
||||
_, err := m.Status(service)
|
||||
if err == nil {
|
||||
t.Fatal("expected status error for unknown service")
|
||||
}
|
||||
|
||||
if err := m.Watch(service); err == nil {
|
||||
t.Fatal("expected watch error for unknown service")
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// 1. start a service
|
||||
// 2. watch service
|
||||
// 3. get service status
|
||||
}
|
||||
|
||||
// stop monitor
|
||||
if err := m.Stop(); err != nil {
|
||||
t.Fatalf("failed to stop monitor: %v", err)
|
||||
}
|
||||
}
|
43
monitor/monitor.go
Normal file
43
monitor/monitor.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Package monitor monitors service health
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
StatusUnknown StatusCode = iota
|
||||
StatusRunning
|
||||
StatusFailed
|
||||
)
|
||||
|
||||
type StatusCode int
|
||||
|
||||
// Monitor monitors a service and reaps dead instances
|
||||
type Monitor interface {
|
||||
// Reap a service and stop monitoring
|
||||
Reap(service string) error
|
||||
// Status of the service
|
||||
Status(service string) (Status, error)
|
||||
// Watch starts watching the service
|
||||
Watch(service string) error
|
||||
// Run the monitor to watch all services
|
||||
Run() error
|
||||
// Stop monitoring
|
||||
Stop() error
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
Code StatusCode
|
||||
Info string
|
||||
Error string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotWatching = errors.New("not watching")
|
||||
)
|
||||
|
||||
// NewMonitor returns a new monitor
|
||||
func NewMonitor(opts ...Option) Monitor {
|
||||
return newMonitor(opts...)
|
||||
}
|
25
monitor/options.go
Normal file
25
monitor/options.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/registry"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Client client.Client
|
||||
Registry registry.Registry
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
func Client(c client.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.Client = c
|
||||
}
|
||||
}
|
||||
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Registry = r
|
||||
}
|
||||
}
|
@@ -99,14 +99,14 @@ func (p *Proxy) getRoute(service string) ([]string, error) {
|
||||
p.Routes[service] = make(map[uint64]router.Route)
|
||||
p.Unlock()
|
||||
|
||||
// if the router is broken return error
|
||||
if status := p.Router.Status(); status.Code == router.Error {
|
||||
return nil, status.Error
|
||||
}
|
||||
|
||||
// lookup the routes in the router
|
||||
results, err := p.Router.Lookup(router.NewQuery(router.QueryService(service)))
|
||||
if err != nil {
|
||||
// check the status of the router
|
||||
if status := p.Router.Status(); status.Code == router.Error {
|
||||
return nil, status.Error
|
||||
}
|
||||
// otherwise return the error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -220,6 +220,23 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
||||
// create new request with raw bytes body
|
||||
creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType()))
|
||||
|
||||
// not a stream so make a client.Call request
|
||||
if !req.Stream() {
|
||||
crsp := new(bytes.Frame)
|
||||
|
||||
// make a call to the backend
|
||||
if err := p.Client.Call(ctx, creq, crsp, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write the response
|
||||
if err := rsp.Write(crsp.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// create new stream
|
||||
stream, err := p.Client.Stream(ctx, creq, opts...)
|
||||
if err != nil {
|
||||
@@ -227,7 +244,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
|
||||
}
|
||||
defer stream.Close()
|
||||
|
||||
// create client request read loop
|
||||
// create client request read loop if streaming
|
||||
go readLoop(req, stream)
|
||||
|
||||
// get raw response
|
||||
|
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
"github.com/micro/go-micro/registry"
|
||||
mnet "github.com/micro/go-micro/util/net"
|
||||
hash "github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
@@ -97,24 +98,33 @@ func configure(c *consulRegistry, opts ...registry.Option) {
|
||||
}
|
||||
|
||||
// check if there are any addrs
|
||||
if len(c.opts.Addrs) > 0 {
|
||||
addr, port, err := net.SplitHostPort(c.opts.Addrs[0])
|
||||
var addrs []string
|
||||
|
||||
// iterate the options addresses
|
||||
for _, address := range c.opts.Addrs {
|
||||
// check we have a port
|
||||
addr, port, err := net.SplitHostPort(address)
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "8500"
|
||||
addr = c.opts.Addrs[0]
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
addr = address
|
||||
addrs = append(addrs, fmt.Sprintf("%s:%s", addr, port))
|
||||
} else if err == nil {
|
||||
config.Address = fmt.Sprintf("%s:%s", addr, port)
|
||||
addrs = append(addrs, fmt.Sprintf("%s:%s", addr, port))
|
||||
}
|
||||
}
|
||||
|
||||
// set the addrs
|
||||
if len(addrs) > 0 {
|
||||
c.Address = addrs
|
||||
config.Address = c.Address[0]
|
||||
}
|
||||
|
||||
if config.HttpClient == nil {
|
||||
config.HttpClient = new(http.Client)
|
||||
}
|
||||
|
||||
// requires secure connection?
|
||||
if c.opts.Secure || c.opts.TLSConfig != nil {
|
||||
|
||||
config.Scheme = "https"
|
||||
// We're going to support InsecureSkipVerify
|
||||
config.HttpClient.Transport = newTransport(c.opts.TLSConfig)
|
||||
@@ -125,11 +135,13 @@ func configure(c *consulRegistry, opts ...registry.Option) {
|
||||
config.HttpClient.Timeout = c.opts.Timeout
|
||||
}
|
||||
|
||||
// set address
|
||||
c.Address = c.opts.Addrs
|
||||
|
||||
// set the config
|
||||
c.config = config
|
||||
|
||||
// remove client
|
||||
c.client = nil
|
||||
|
||||
// setup the client
|
||||
c.Client()
|
||||
}
|
||||
|
||||
@@ -239,6 +251,9 @@ func (c *consulRegistry) Register(s *registry.Service, opts ...registry.Register
|
||||
}
|
||||
|
||||
host, pt, _ := net.SplitHostPort(node.Address)
|
||||
if host == "" {
|
||||
host = node.Address
|
||||
}
|
||||
port, _ := strconv.Atoi(pt)
|
||||
|
||||
// register the service
|
||||
@@ -340,7 +355,7 @@ func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
|
||||
|
||||
svc.Nodes = append(svc.Nodes, ®istry.Node{
|
||||
Id: id,
|
||||
Address: fmt.Sprintf("%s:%d", address, s.Service.Port),
|
||||
Address: mnet.HostPort(address, s.Service.Port),
|
||||
Metadata: decodeMetadata(s.Service.Tags),
|
||||
})
|
||||
}
|
||||
@@ -384,20 +399,28 @@ func (c *consulRegistry) Client() *consul.Client {
|
||||
return c.client
|
||||
}
|
||||
|
||||
if len(c.Address) == 0 {
|
||||
tmp, _ := consul.NewClient(c.config)
|
||||
return tmp
|
||||
for _, addr := range c.Address {
|
||||
// set the address
|
||||
c.config.Address = addr
|
||||
|
||||
// create a new client
|
||||
tmpClient, _ := consul.NewClient(c.config)
|
||||
|
||||
// test the client
|
||||
_, err := tmpClient.Agent().Host()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// set the client
|
||||
c.client = tmpClient
|
||||
return c.client
|
||||
}
|
||||
|
||||
c.config.Address = c.Address[0]
|
||||
tmpClint, _ := consul.NewClient(c.config)
|
||||
_, err := tmpClint.Agent().Host()
|
||||
if err != nil {
|
||||
c.Address = c.Address[1:]
|
||||
return c.Client()
|
||||
}
|
||||
// set the default
|
||||
c.client, _ = consul.NewClient(c.config)
|
||||
|
||||
c.client = tmpClint
|
||||
// return the client
|
||||
return c.client
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ var (
|
||||
DefaultRegistry = NewRegistry()
|
||||
|
||||
// Not found error when GetService is called
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrNotFound = errors.New("service not found")
|
||||
// Watcher stopped error when watcher is stopped
|
||||
ErrWatcherStopped = errors.New("watcher stopped")
|
||||
)
|
||||
|
@@ -43,10 +43,9 @@ var (
|
||||
// router implements default router
|
||||
type router struct {
|
||||
sync.RWMutex
|
||||
// embed the table
|
||||
table *table
|
||||
opts Options
|
||||
status Status
|
||||
table *table
|
||||
exit chan struct{}
|
||||
errChan chan error
|
||||
eventChan chan *Event
|
||||
@@ -67,33 +66,41 @@ func newRouter(opts ...Option) Router {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
r := &router{
|
||||
table: newTable(),
|
||||
// set initial status to Stopped
|
||||
status := Status{Code: Stopped, Error: nil}
|
||||
|
||||
return &router{
|
||||
opts: options,
|
||||
status: Status{Code: Stopped, Error: nil},
|
||||
status: status,
|
||||
table: newTable(),
|
||||
advertWg: &sync.WaitGroup{},
|
||||
wg: &sync.WaitGroup{},
|
||||
subscribers: make(map[string]chan *Advert),
|
||||
}
|
||||
|
||||
go r.run()
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Init initializes router with given options
|
||||
func (r *router) Init(opts ...Option) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
for _, o := range opts {
|
||||
o(&r.opts)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options returns router options
|
||||
func (r *router) Options() Options {
|
||||
return r.opts
|
||||
r.Lock()
|
||||
opts := r.opts
|
||||
r.Unlock()
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// Table returns routing table
|
||||
func (r *router) Table() Table {
|
||||
return r.table
|
||||
}
|
||||
@@ -475,11 +482,12 @@ func (r *router) watchErrors() {
|
||||
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
// if the router is not stopped, stop it
|
||||
if r.status.Code != Stopped {
|
||||
// notify all goroutines to finish
|
||||
close(r.exit)
|
||||
|
||||
// drain the advertise channel only if advertising
|
||||
// drain the advertise channel only if the router is advertising
|
||||
if r.status.Code == Advertising {
|
||||
// drain the event channel
|
||||
for range r.eventChan {
|
||||
@@ -495,69 +503,67 @@ func (r *router) watchErrors() {
|
||||
}
|
||||
}
|
||||
|
||||
// Run runs the router.
|
||||
func (r *router) run() {
|
||||
// Start starts the router
|
||||
func (r *router) Start() error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
switch r.status.Code {
|
||||
case Stopped, Error:
|
||||
// add all local service routes into the routing table
|
||||
if err := r.manageRegistryRoutes(r.opts.Registry, "create"); err != nil {
|
||||
r.status = Status{Code: Error, Error: fmt.Errorf("failed adding registry routes: %s", err)}
|
||||
return
|
||||
}
|
||||
|
||||
// add default gateway into routing table
|
||||
if r.opts.Gateway != "" {
|
||||
// note, the only non-default value is the gateway
|
||||
route := Route{
|
||||
Service: "*",
|
||||
Address: "*",
|
||||
Gateway: r.opts.Gateway,
|
||||
Network: "*",
|
||||
Metric: DefaultLocalMetric,
|
||||
}
|
||||
if err := r.table.Create(route); err != nil {
|
||||
r.status = Status{Code: Error, Error: fmt.Errorf("failed adding default gateway route: %s", err)}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// create error and exit channels
|
||||
r.errChan = make(chan error, 1)
|
||||
r.exit = make(chan struct{})
|
||||
|
||||
// registry watcher
|
||||
regWatcher, err := r.opts.Registry.Watch()
|
||||
if err != nil {
|
||||
r.status = Status{Code: Error, Error: fmt.Errorf("failed creating registry watcher: %v", err)}
|
||||
return
|
||||
}
|
||||
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
defer r.wg.Done()
|
||||
select {
|
||||
case r.errChan <- r.watchRegistry(regWatcher):
|
||||
case <-r.exit:
|
||||
}
|
||||
}()
|
||||
|
||||
// watch for errors and cleanup
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
defer r.wg.Done()
|
||||
r.watchErrors()
|
||||
}()
|
||||
|
||||
// mark router as Running and set its Error to nil
|
||||
r.status = Status{Code: Running, Error: nil}
|
||||
|
||||
return
|
||||
// add all local service routes into the routing table
|
||||
if err := r.manageRegistryRoutes(r.opts.Registry, "create"); err != nil {
|
||||
e := fmt.Errorf("failed adding registry routes: %s", err)
|
||||
r.status = Status{Code: Error, Error: e}
|
||||
return e
|
||||
}
|
||||
|
||||
return
|
||||
// add default gateway into routing table
|
||||
if r.opts.Gateway != "" {
|
||||
// note, the only non-default value is the gateway
|
||||
route := Route{
|
||||
Service: "*",
|
||||
Address: "*",
|
||||
Gateway: r.opts.Gateway,
|
||||
Network: "*",
|
||||
Metric: DefaultLocalMetric,
|
||||
}
|
||||
if err := r.table.Create(route); err != nil {
|
||||
e := fmt.Errorf("failed adding default gateway route: %s", err)
|
||||
r.status = Status{Code: Error, Error: e}
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
// create error and exit channels
|
||||
r.errChan = make(chan error, 1)
|
||||
r.exit = make(chan struct{})
|
||||
|
||||
// registry watcher
|
||||
regWatcher, err := r.opts.Registry.Watch()
|
||||
if err != nil {
|
||||
e := fmt.Errorf("failed creating registry watcher: %v", err)
|
||||
r.status = Status{Code: Error, Error: e}
|
||||
return e
|
||||
}
|
||||
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
defer r.wg.Done()
|
||||
select {
|
||||
case r.errChan <- r.watchRegistry(regWatcher):
|
||||
case <-r.exit:
|
||||
}
|
||||
}()
|
||||
|
||||
// watch for errors and cleanup
|
||||
r.wg.Add(1)
|
||||
go func() {
|
||||
defer r.wg.Done()
|
||||
r.watchErrors()
|
||||
}()
|
||||
|
||||
// mark router as Running
|
||||
r.status = Status{Code: Running, Error: nil}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Advertise stars advertising the routes to the network and returns the advertisements channel to consume from.
|
||||
@@ -578,6 +584,7 @@ func (r *router) Advertise() (<-chan *Advert, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed listing routes: %s", err)
|
||||
}
|
||||
|
||||
// collect all the added routes before we attempt to add default gateway
|
||||
events := make([]*Event, len(routes))
|
||||
for i, route := range routes {
|
||||
|
@@ -21,6 +21,8 @@ type Router interface {
|
||||
Lookup(Query) ([]Route, error)
|
||||
// Watch returns a watcher which tracks updates to the routing table
|
||||
Watch(opts ...WatchOption) (Watcher, error)
|
||||
// Start starts the router
|
||||
Start() error
|
||||
// Status returns router status
|
||||
Status() Status
|
||||
// Stop stops the router
|
||||
@@ -76,10 +78,15 @@ func (s StatusCode) String() string {
|
||||
|
||||
// Status is router status
|
||||
type Status struct {
|
||||
// Error is router error
|
||||
Error error
|
||||
// Code defines router status
|
||||
Code StatusCode
|
||||
// Error contains error description
|
||||
Error error
|
||||
}
|
||||
|
||||
// String returns human readable status
|
||||
func (s Status) String() string {
|
||||
return s.Code.String()
|
||||
}
|
||||
|
||||
// AdvertType is route advertisement type
|
||||
|
@@ -43,9 +43,16 @@ func NewRouter(opts ...router.Option) router.Router {
|
||||
cli = options.Client
|
||||
}
|
||||
|
||||
// set the status to Stopped
|
||||
status := &router.Status{
|
||||
Code: router.Stopped,
|
||||
Error: nil,
|
||||
}
|
||||
|
||||
// NOTE: should we have Client/Service option in router.Options?
|
||||
s := &svc{
|
||||
opts: options,
|
||||
status: status,
|
||||
router: pb.NewRouterService(router.DefaultName, cli),
|
||||
}
|
||||
|
||||
@@ -63,21 +70,43 @@ func NewRouter(opts ...router.Option) router.Router {
|
||||
|
||||
// Init initializes router with given options
|
||||
func (s *svc) Init(opts ...router.Option) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
for _, o := range opts {
|
||||
o(&s.opts)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Options returns router options
|
||||
func (s *svc) Options() router.Options {
|
||||
return s.opts
|
||||
s.Lock()
|
||||
opts := s.opts
|
||||
s.Unlock()
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// Table returns routing table
|
||||
func (s *svc) Table() router.Table {
|
||||
return s.table
|
||||
}
|
||||
|
||||
// Start starts the service
|
||||
func (s *svc) Start() error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.status = &router.Status{
|
||||
Code: router.Running,
|
||||
Error: nil,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *svc) advertiseEvents(advertChan chan *router.Advert, stream pb.Router_AdvertiseService) error {
|
||||
go func() {
|
||||
<-s.exit
|
||||
@@ -140,10 +169,7 @@ func (s *svc) Advertise() (<-chan *router.Advert, error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
// get the status
|
||||
status := s.Status()
|
||||
|
||||
switch status.Code {
|
||||
switch s.status.Code {
|
||||
case router.Running, router.Advertising:
|
||||
stream, err := s.router.Advertise(context.Background(), &pb.Request{}, s.callOpts...)
|
||||
if err != nil {
|
||||
@@ -154,15 +180,7 @@ func (s *svc) Advertise() (<-chan *router.Advert, error) {
|
||||
go s.advertiseEvents(advertChan, stream)
|
||||
return advertChan, nil
|
||||
case router.Stopped:
|
||||
// check if our router is stopped
|
||||
select {
|
||||
case <-s.exit:
|
||||
s.exit = make(chan bool)
|
||||
// call advertise again
|
||||
return s.Advertise()
|
||||
default:
|
||||
return nil, fmt.Errorf("not running")
|
||||
}
|
||||
return nil, fmt.Errorf("not running")
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("error: %s", s.status.Error)
|
||||
|
@@ -1,9 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/server/debug"
|
||||
)
|
||||
|
||||
func registerDebugHandler(s Server) {
|
||||
s.Handle(s.NewHandler(&debug.Debug{s.Options().DebugHandler}, InternalHandler(true)))
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
proto "github.com/micro/go-micro/server/debug/proto"
|
||||
)
|
||||
|
||||
// The debug handler represents an internal server handler
|
||||
// used to determine health, status and env info about
|
||||
// a service node. It's akin to Google's /statusz, /healthz,
|
||||
// and /varz
|
||||
type Handler interface {
|
||||
Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error
|
||||
Stats(ctx context.Context, req *proto.StatsRequest, rsp *proto.StatsResponse) error
|
||||
}
|
||||
|
||||
// Our own internal handler
|
||||
type debug struct {
|
||||
started int64
|
||||
}
|
||||
|
||||
// We use this to wrap any debug handlers so we preserve the signature Debug.{Method}
|
||||
type Debug struct {
|
||||
Handler
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultHandler Handler = newDebug()
|
||||
)
|
||||
|
||||
func newDebug() *debug {
|
||||
return &debug{
|
||||
started: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error {
|
||||
rsp.Status = "ok"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *debug) Stats(ctx context.Context, req *proto.StatsRequest, rsp *proto.StatsResponse) error {
|
||||
var mstat runtime.MemStats
|
||||
runtime.ReadMemStats(&mstat)
|
||||
|
||||
rsp.Started = uint64(d.started)
|
||||
rsp.Uptime = uint64(time.Now().Unix() - d.started)
|
||||
rsp.Memory = mstat.Alloc
|
||||
rsp.Gc = mstat.PauseTotalNs
|
||||
rsp.Threads = uint64(runtime.NumGoroutine())
|
||||
return nil
|
||||
}
|
@@ -1,100 +0,0 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: github.com/micro/go-micro/server/debug/proto/debug.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package debug is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/micro/go-micro/server/debug/proto/debug.proto
|
||||
|
||||
It has these top-level messages:
|
||||
HealthRequest
|
||||
HealthResponse
|
||||
StatsRequest
|
||||
StatsResponse
|
||||
*/
|
||||
package debug
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type HealthRequest struct {
|
||||
}
|
||||
|
||||
func (m *HealthRequest) Reset() { *m = HealthRequest{} }
|
||||
func (m *HealthRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*HealthRequest) ProtoMessage() {}
|
||||
func (*HealthRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
type HealthResponse struct {
|
||||
// default: ok
|
||||
Status string `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (m *HealthResponse) Reset() { *m = HealthResponse{} }
|
||||
func (m *HealthResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*HealthResponse) ProtoMessage() {}
|
||||
func (*HealthResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
type StatsRequest struct {
|
||||
}
|
||||
|
||||
func (m *StatsRequest) Reset() { *m = StatsRequest{} }
|
||||
func (m *StatsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*StatsRequest) ProtoMessage() {}
|
||||
func (*StatsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
type StatsResponse struct {
|
||||
// unix timestamp
|
||||
Started uint64 `protobuf:"varint,1,opt,name=started" json:"started,omitempty"`
|
||||
// in seconds
|
||||
Uptime uint64 `protobuf:"varint,2,opt,name=uptime" json:"uptime,omitempty"`
|
||||
// in bytes
|
||||
Memory uint64 `protobuf:"varint,3,opt,name=memory" json:"memory,omitempty"`
|
||||
// num threads
|
||||
Threads uint64 `protobuf:"varint,4,opt,name=threads" json:"threads,omitempty"`
|
||||
// in nanoseconds
|
||||
Gc uint64 `protobuf:"varint,5,opt,name=gc" json:"gc,omitempty"`
|
||||
}
|
||||
|
||||
func (m *StatsResponse) Reset() { *m = StatsResponse{} }
|
||||
func (m *StatsResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*StatsResponse) ProtoMessage() {}
|
||||
func (*StatsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*HealthRequest)(nil), "HealthRequest")
|
||||
proto.RegisterType((*HealthResponse)(nil), "HealthResponse")
|
||||
proto.RegisterType((*StatsRequest)(nil), "StatsRequest")
|
||||
proto.RegisterType((*StatsResponse)(nil), "StatsResponse")
|
||||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 201 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x34, 0x8f, 0xbd, 0x6e, 0x83, 0x30,
|
||||
0x14, 0x85, 0x05, 0xa5, 0x54, 0xbd, 0x2a, 0x54, 0x62, 0xa8, 0x3c, 0x56, 0x4c, 0x2c, 0xc5, 0x43,
|
||||
0x97, 0x3e, 0x42, 0x67, 0xf2, 0x04, 0xfc, 0x5c, 0x19, 0xa4, 0x38, 0x26, 0xbe, 0xd7, 0x91, 0x32,
|
||||
0xe7, 0xc5, 0x03, 0xb6, 0xd9, 0xce, 0xf7, 0xd9, 0xe7, 0x48, 0x17, 0xfe, 0xd4, 0xc2, 0xb3, 0x1b,
|
||||
0xda, 0xd1, 0x68, 0xa9, 0x97, 0xd1, 0x1a, 0xa9, 0xcc, 0x4f, 0x08, 0x84, 0xf6, 0x86, 0x56, 0x4e,
|
||||
0x38, 0x38, 0x25, 0x57, 0x6b, 0xd8, 0x84, 0xdc, 0xfa, 0x5c, 0x7f, 0x42, 0xf1, 0x8f, 0xfd, 0x99,
|
||||
0xe7, 0x0e, 0xaf, 0x0e, 0x89, 0xeb, 0x06, 0xca, 0x43, 0xd0, 0x6a, 0x2e, 0x84, 0xd5, 0x17, 0xe4,
|
||||
0xc4, 0x3d, 0x3b, 0x12, 0xc9, 0x77, 0xd2, 0xbc, 0x77, 0x91, 0xea, 0x12, 0x3e, 0x4e, 0x5b, 0xa2,
|
||||
0xa3, 0xf9, 0x48, 0xa0, 0x88, 0x22, 0x36, 0x05, 0xbc, 0x6d, 0x7f, 0x2d, 0xe3, 0xe4, 0xab, 0x59,
|
||||
0x77, 0xe0, 0xbe, 0xe9, 0x56, 0x5e, 0x34, 0x8a, 0xd4, 0x3f, 0x44, 0xda, 0xbd, 0x46, 0x6d, 0xec,
|
||||
0x5d, 0xbc, 0x04, 0x1f, 0x68, 0x5f, 0xe2, 0xd9, 0x62, 0x3f, 0x91, 0xc8, 0xc2, 0x52, 0xc4, 0xaa,
|
||||
0x84, 0x54, 0x8d, 0xe2, 0xd5, 0xcb, 0x2d, 0x0d, 0xb9, 0xbf, 0xeb, 0xf7, 0x19, 0x00, 0x00, 0xff,
|
||||
0xff, 0xc6, 0x75, 0x51, 0x35, 0x13, 0x01, 0x00, 0x00,
|
||||
}
|
@@ -20,10 +20,6 @@ func extractValue(v reflect.Type, d int) *registry.Value {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if len(v.Name()) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
arg := ®istry.Value{
|
||||
Name: v.Name(),
|
||||
Type: v.Name(),
|
||||
@@ -65,10 +61,6 @@ func extractValue(v reflect.Type, d int) *registry.Value {
|
||||
p = p.Elem()
|
||||
}
|
||||
arg.Type = "[]" + p.Name()
|
||||
val := extractValue(v.Elem(), d+1)
|
||||
if val != nil {
|
||||
arg.Values = append(arg.Values, val)
|
||||
}
|
||||
}
|
||||
|
||||
return arg
|
||||
|
@@ -1,10 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/server/debug"
|
||||
)
|
||||
|
||||
func registerDebugHandler(s server.Server) {
|
||||
s.Handle(s.NewHandler(&debug.Debug{s.Options().DebugHandler}, server.InternalHandler(true)))
|
||||
}
|
@@ -56,10 +56,6 @@ func extractValue(v reflect.Type, d int) *registry.Value {
|
||||
p = p.Elem()
|
||||
}
|
||||
arg.Type = "[]" + p.Name()
|
||||
val := extractValue(v.Elem(), d+1)
|
||||
if val != nil {
|
||||
arg.Values = append(arg.Values, val)
|
||||
}
|
||||
}
|
||||
|
||||
return arg
|
||||
|
@@ -271,12 +271,12 @@ func (g *grpcServer) handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
g.rpc.mu.Unlock()
|
||||
|
||||
if service == nil {
|
||||
return status.New(codes.Unimplemented, fmt.Sprintf("unknown service %v", service)).Err()
|
||||
return status.New(codes.Unimplemented, fmt.Sprintf("unknown service %s", serviceName)).Err()
|
||||
}
|
||||
|
||||
mtype := service.method[methodName]
|
||||
if mtype == nil {
|
||||
return status.New(codes.Unimplemented, fmt.Sprintf("unknown service %v", service)).Err()
|
||||
return status.New(codes.Unimplemented, fmt.Sprintf("unknown service %s.%s", serviceName, methodName)).Err()
|
||||
}
|
||||
|
||||
// process unary
|
||||
@@ -700,7 +700,6 @@ func (g *grpcServer) Deregister() error {
|
||||
}
|
||||
|
||||
func (g *grpcServer) Start() error {
|
||||
registerDebugHandler(g)
|
||||
config := g.opts
|
||||
|
||||
// micro: config.Transport.Listen(config.Address)
|
||||
@@ -719,10 +718,10 @@ func (g *grpcServer) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
baddr := strings.Join(config.Broker.Options().Addrs, ",")
|
||||
baddr := config.Broker.Address()
|
||||
bname := config.Broker.String()
|
||||
|
||||
log.Logf("Broker [%s] Listening on %s", bname, baddr)
|
||||
log.Logf("Broker [%s] Connected to %s", bname, baddr)
|
||||
|
||||
// announce self to the world
|
||||
if err := g.Register(); err != nil {
|
||||
|
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/micro/go-micro/codec"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/server/debug"
|
||||
"github.com/micro/go-micro/transport"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/encoding"
|
||||
@@ -89,10 +88,6 @@ func newOptions(opt ...server.Option) server.Options {
|
||||
opts.Transport = transport.DefaultTransport
|
||||
}
|
||||
|
||||
if opts.DebugHandler == nil {
|
||||
opts.DebugHandler = debug.DefaultHandler
|
||||
}
|
||||
|
||||
if len(opts.Address) == 0 {
|
||||
opts.Address = server.DefaultAddress
|
||||
}
|
||||
|
@@ -1,18 +1,15 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/codec"
|
||||
"github.com/micro/go-micro/metadata"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/util/buf"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -175,7 +172,7 @@ func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broke
|
||||
msg.Header["Content-Type"] = defaultContentType
|
||||
ct = defaultContentType
|
||||
}
|
||||
cf, err := g.newCodec(ct)
|
||||
cf, err := g.newGRPCCodec(ct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -205,15 +202,7 @@ func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broke
|
||||
req = req.Elem()
|
||||
}
|
||||
|
||||
b := buf.New(bytes.NewBuffer(msg.Body))
|
||||
co := cf(b)
|
||||
defer co.Close()
|
||||
|
||||
if err := co.ReadHeader(&codec.Message{}, codec.Event); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := co.ReadBody(req.Interface()); err != nil {
|
||||
if err := cf.Unmarshal(msg.Body, req.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/codec"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/server/debug"
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
@@ -36,9 +35,6 @@ type Options struct {
|
||||
// The router for requests
|
||||
Router Router
|
||||
|
||||
// Debug Handler which can be set by a user
|
||||
DebugHandler debug.Handler
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
@@ -66,10 +62,6 @@ func newOptions(opt ...Option) Options {
|
||||
opts.Transport = transport.DefaultTransport
|
||||
}
|
||||
|
||||
if opts.DebugHandler == nil {
|
||||
opts.DebugHandler = debug.DefaultHandler
|
||||
}
|
||||
|
||||
if opts.RegisterCheck == nil {
|
||||
opts.RegisterCheck = DefaultRegisterCheck
|
||||
}
|
||||
@@ -156,13 +148,6 @@ func Transport(t transport.Transport) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// DebugHandler for this server
|
||||
func DebugHandler(d debug.Handler) Option {
|
||||
return func(o *Options) {
|
||||
o.DebugHandler = d
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata associated with the server
|
||||
func Metadata(md map[string]string) Option {
|
||||
return func(o *Options) {
|
||||
|
@@ -154,11 +154,10 @@ func setupProtocol(msg *transport.Message) codec.NewCodec {
|
||||
|
||||
func newRpcCodec(req *transport.Message, socket transport.Socket, c codec.NewCodec) codec.Codec {
|
||||
rwc := &readWriteCloser{
|
||||
rbuf: bytes.NewBuffer(req.Body),
|
||||
rbuf: bytes.NewBuffer(nil),
|
||||
wbuf: bytes.NewBuffer(nil),
|
||||
}
|
||||
r := &rpcCodec{
|
||||
first: true,
|
||||
buf: rwc,
|
||||
codec: c(rwc),
|
||||
req: req,
|
||||
@@ -174,33 +173,27 @@ func (c *rpcCodec) ReadHeader(r *codec.Message, t codec.MessageType) error {
|
||||
Body: c.req.Body,
|
||||
}
|
||||
|
||||
// if its a follow on request read it
|
||||
if !c.first {
|
||||
var tm transport.Message
|
||||
var tm transport.Message
|
||||
|
||||
// read off the socket
|
||||
if err := c.socket.Recv(&tm); err != nil {
|
||||
return err
|
||||
}
|
||||
// reset the read buffer
|
||||
c.buf.rbuf.Reset()
|
||||
// read off the socket
|
||||
if err := c.socket.Recv(&tm); err != nil {
|
||||
return err
|
||||
}
|
||||
// reset the read buffer
|
||||
c.buf.rbuf.Reset()
|
||||
|
||||
// write the body to the buffer
|
||||
if _, err := c.buf.rbuf.Write(tm.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set the message header
|
||||
m.Header = tm.Header
|
||||
// set the message body
|
||||
m.Body = tm.Body
|
||||
|
||||
// set req
|
||||
c.req = &tm
|
||||
// write the body to the buffer
|
||||
if _, err := c.buf.rbuf.Write(tm.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// no longer first read
|
||||
c.first = false
|
||||
// set the message header
|
||||
m.Header = tm.Header
|
||||
// set the message body
|
||||
m.Body = tm.Body
|
||||
|
||||
// set req
|
||||
c.req = &tm
|
||||
|
||||
// set some internal things
|
||||
getHeaders(&m)
|
||||
|
@@ -16,6 +16,7 @@ type rpcRequest struct {
|
||||
body []byte
|
||||
rawBody interface{}
|
||||
stream bool
|
||||
first bool
|
||||
}
|
||||
|
||||
type rpcMessage struct {
|
||||
@@ -54,9 +55,9 @@ func (r *rpcRequest) Body() interface{} {
|
||||
|
||||
func (r *rpcRequest) Read() ([]byte, error) {
|
||||
// got a body
|
||||
if r.body != nil {
|
||||
if r.first {
|
||||
b := r.body
|
||||
r.body = nil
|
||||
r.first = false
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/micro/go-micro/util/addr"
|
||||
log "github.com/micro/go-micro/util/log"
|
||||
mnet "github.com/micro/go-micro/util/net"
|
||||
"github.com/micro/go-micro/util/socket"
|
||||
)
|
||||
|
||||
type rpcServer struct {
|
||||
@@ -70,23 +71,99 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
|
||||
}
|
||||
}()
|
||||
|
||||
// multiplex the streams on a single socket by Micro-Stream
|
||||
var mtx sync.RWMutex
|
||||
sockets := make(map[string]*socket.Socket)
|
||||
|
||||
for {
|
||||
var msg transport.Message
|
||||
if err := sock.Recv(&msg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// use Micro-Stream as the stream identifier
|
||||
// in the event its blank we'll always process
|
||||
// on the same socket
|
||||
id := msg.Header["Micro-Stream"]
|
||||
|
||||
// if there's no stream id then its a standard request
|
||||
// use the Micro-Id
|
||||
if len(id) == 0 {
|
||||
id = msg.Header["Micro-Id"]
|
||||
}
|
||||
|
||||
// add to wait group if "wait" is opt-in
|
||||
if s.wg != nil {
|
||||
s.wg.Add(1)
|
||||
}
|
||||
|
||||
// check we have an existing socket
|
||||
mtx.RLock()
|
||||
psock, ok := sockets[id]
|
||||
mtx.RUnlock()
|
||||
|
||||
// got the socket
|
||||
if ok {
|
||||
// accept the message
|
||||
if err := psock.Accept(&msg); err != nil {
|
||||
// delete the socket
|
||||
mtx.Lock()
|
||||
delete(sockets, id)
|
||||
mtx.Unlock()
|
||||
}
|
||||
|
||||
// done(1)
|
||||
if s.wg != nil {
|
||||
s.wg.Done()
|
||||
}
|
||||
|
||||
// continue to the next message
|
||||
continue
|
||||
}
|
||||
|
||||
// no socket was found
|
||||
psock = socket.New()
|
||||
psock.SetLocal(sock.Local())
|
||||
psock.SetRemote(sock.Remote())
|
||||
|
||||
// load the socket
|
||||
psock.Accept(&msg)
|
||||
|
||||
// save a new socket
|
||||
mtx.Lock()
|
||||
sockets[id] = psock
|
||||
mtx.Unlock()
|
||||
|
||||
// process the outbound messages from the socket
|
||||
go func(id string, psock *socket.Socket) {
|
||||
defer psock.Close()
|
||||
|
||||
for {
|
||||
// get the message from our internal handler/stream
|
||||
m := new(transport.Message)
|
||||
if err := psock.Process(m); err != nil {
|
||||
// delete the socket
|
||||
mtx.Lock()
|
||||
delete(sockets, id)
|
||||
mtx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// send the message back over the socket
|
||||
if err := sock.Send(m); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}(id, psock)
|
||||
|
||||
// now walk the usual path
|
||||
|
||||
// we use this Timeout header to set a server deadline
|
||||
to := msg.Header["Timeout"]
|
||||
// we use this Content-Type header to identify the codec needed
|
||||
ct := msg.Header["Content-Type"]
|
||||
|
||||
// strip our headers
|
||||
// copy the message headers
|
||||
hdr := make(map[string]string)
|
||||
for k, v := range msg.Header {
|
||||
hdr[k] = v
|
||||
@@ -96,17 +173,17 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
|
||||
hdr["Local"] = sock.Local()
|
||||
hdr["Remote"] = sock.Remote()
|
||||
|
||||
// create new context
|
||||
// create new context with the metadata
|
||||
ctx := metadata.NewContext(context.Background(), hdr)
|
||||
|
||||
// set the timeout if we have it
|
||||
// set the timeout from the header if we have it
|
||||
if len(to) > 0 {
|
||||
if n, err := strconv.ParseUint(to, 10, 64); err == nil {
|
||||
ctx, _ = context.WithTimeout(ctx, time.Duration(n))
|
||||
}
|
||||
}
|
||||
|
||||
// no content type
|
||||
// if there's no content type default it
|
||||
if len(ct) == 0 {
|
||||
msg.Header["Content-Type"] = DefaultContentType
|
||||
ct = DefaultContentType
|
||||
@@ -133,7 +210,13 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
|
||||
}
|
||||
}
|
||||
|
||||
rcodec := newRpcCodec(&msg, sock, cf)
|
||||
rcodec := newRpcCodec(&msg, psock, cf)
|
||||
|
||||
// check stream id
|
||||
var stream bool
|
||||
if v := getHeader("Micro-Stream", msg.Header); len(v) > 0 {
|
||||
stream = true
|
||||
}
|
||||
|
||||
// internal request
|
||||
request := &rpcRequest{
|
||||
@@ -144,14 +227,14 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
|
||||
codec: rcodec,
|
||||
header: msg.Header,
|
||||
body: msg.Body,
|
||||
socket: sock,
|
||||
stream: true,
|
||||
socket: psock,
|
||||
stream: stream,
|
||||
}
|
||||
|
||||
// internal response
|
||||
response := &rpcResponse{
|
||||
header: make(map[string]string),
|
||||
socket: sock,
|
||||
socket: psock,
|
||||
codec: rcodec,
|
||||
}
|
||||
|
||||
@@ -174,28 +257,34 @@ func (s *rpcServer) ServeConn(sock transport.Socket) {
|
||||
r = rpcRouter{handler}
|
||||
}
|
||||
|
||||
// serve the actual request using the request router
|
||||
if err := r.ServeRequest(ctx, request, response); err != nil {
|
||||
// write an error response
|
||||
err = rcodec.Write(&codec.Message{
|
||||
Header: msg.Header,
|
||||
Error: err.Error(),
|
||||
Type: codec.Error,
|
||||
}, nil)
|
||||
// could not write the error response
|
||||
if err != nil {
|
||||
log.Logf("rpc: unable to write error response: %v", err)
|
||||
// serve the request in a go routine as this may be a stream
|
||||
go func(id string, psock *socket.Socket) {
|
||||
// serve the actual request using the request router
|
||||
if err := r.ServeRequest(ctx, request, response); err != nil {
|
||||
// write an error response
|
||||
err = rcodec.Write(&codec.Message{
|
||||
Header: msg.Header,
|
||||
Error: err.Error(),
|
||||
Type: codec.Error,
|
||||
}, nil)
|
||||
|
||||
// could not write the error response
|
||||
if err != nil {
|
||||
log.Logf("rpc: unable to write error response: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
mtx.Lock()
|
||||
delete(sockets, id)
|
||||
mtx.Unlock()
|
||||
|
||||
// signal we're done
|
||||
if s.wg != nil {
|
||||
s.wg.Done()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// done
|
||||
if s.wg != nil {
|
||||
s.wg.Done()
|
||||
}
|
||||
}(id, psock)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,10 +404,15 @@ func (s *rpcServer) Register() error {
|
||||
md[k] = v
|
||||
}
|
||||
|
||||
// mq-rpc(eg. nats) doesn't need the port. its addr is queue name.
|
||||
if port != "" {
|
||||
addr = mnet.HostPort(addr, port)
|
||||
}
|
||||
|
||||
// register service
|
||||
node := ®istry.Node{
|
||||
Id: config.Name + "-" + config.Id,
|
||||
Address: mnet.HostPort(addr, port),
|
||||
Address: addr,
|
||||
Metadata: md,
|
||||
}
|
||||
|
||||
@@ -447,9 +541,14 @@ func (s *rpcServer) Deregister() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// mq-rpc(eg. nats) doesn't need the port. its addr is queue name.
|
||||
if port != "" {
|
||||
addr = mnet.HostPort(addr, port)
|
||||
}
|
||||
|
||||
node := ®istry.Node{
|
||||
Id: config.Name + "-" + config.Id,
|
||||
Address: mnet.HostPort(addr, port),
|
||||
Address: addr,
|
||||
}
|
||||
|
||||
service := ®istry.Service{
|
||||
@@ -485,7 +584,6 @@ func (s *rpcServer) Deregister() error {
|
||||
}
|
||||
|
||||
func (s *rpcServer) Start() error {
|
||||
registerDebugHandler(s)
|
||||
config := s.Options()
|
||||
|
||||
// start listening on the transport
|
||||
|
@@ -2,6 +2,8 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/go-micro/codec"
|
||||
@@ -59,6 +61,20 @@ func (r *rpcStream) Recv(msg interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// check the error
|
||||
if len(req.Error) > 0 {
|
||||
// Check the client closed the stream
|
||||
switch req.Error {
|
||||
case lastStreamResponseError.Error():
|
||||
// discard body
|
||||
r.codec.ReadBody(nil)
|
||||
r.err = io.EOF
|
||||
return io.EOF
|
||||
default:
|
||||
return errors.New(req.Error)
|
||||
}
|
||||
}
|
||||
|
||||
// we need to stay up to date with sequence numbers
|
||||
r.id = req.Id
|
||||
if err := r.codec.ReadBody(msg); err != nil {
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/config/cmd"
|
||||
"github.com/micro/go-micro/debug/handler"
|
||||
"github.com/micro/go-micro/metadata"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
@@ -113,6 +114,14 @@ func (s *service) Stop() error {
|
||||
}
|
||||
|
||||
func (s *service) Run() error {
|
||||
// register the debug handler
|
||||
s.opts.Server.Handle(
|
||||
s.opts.Server.NewHandler(
|
||||
handler.DefaultHandler,
|
||||
server.InternalHandler(true),
|
||||
),
|
||||
)
|
||||
|
||||
if err := s.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
220
service/options.go
Normal file
220
service/options.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/broker"
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/registry"
|
||||
"github.com/micro/go-micro/server"
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Broker broker.Broker
|
||||
Client client.Client
|
||||
Server server.Server
|
||||
Registry registry.Registry
|
||||
Transport transport.Transport
|
||||
|
||||
// Before and After funcs
|
||||
BeforeStart []func() error
|
||||
BeforeStop []func() error
|
||||
AfterStart []func() error
|
||||
AfterStop []func() error
|
||||
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{
|
||||
Broker: broker.DefaultBroker,
|
||||
Client: client.DefaultClient,
|
||||
Server: server.DefaultServer,
|
||||
Registry: registry.DefaultRegistry,
|
||||
Transport: transport.DefaultTransport,
|
||||
Context: context.Background(),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
func Broker(b broker.Broker) Option {
|
||||
return func(o *Options) {
|
||||
o.Broker = b
|
||||
// Update Client and Server
|
||||
o.Client.Init(client.Broker(b))
|
||||
o.Server.Init(server.Broker(b))
|
||||
}
|
||||
}
|
||||
|
||||
func Client(c client.Client) Option {
|
||||
return func(o *Options) {
|
||||
o.Client = c
|
||||
}
|
||||
}
|
||||
|
||||
// Context specifies a context for the service.
|
||||
// Can be used to signal shutdown of the service.
|
||||
// Can be used for extra option values.
|
||||
func Context(ctx context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = ctx
|
||||
}
|
||||
}
|
||||
|
||||
func Server(s server.Server) Option {
|
||||
return func(o *Options) {
|
||||
o.Server = s
|
||||
}
|
||||
}
|
||||
|
||||
// Registry sets the registry for the service
|
||||
// and the underlying components
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Registry = r
|
||||
// Update Client and Server
|
||||
o.Client.Init(client.Registry(r))
|
||||
o.Server.Init(server.Registry(r))
|
||||
// Update Broker
|
||||
o.Broker.Init(broker.Registry(r))
|
||||
}
|
||||
}
|
||||
|
||||
// Transport sets the transport for the service
|
||||
// and the underlying components
|
||||
func Transport(t transport.Transport) Option {
|
||||
return func(o *Options) {
|
||||
o.Transport = t
|
||||
// Update Client and Server
|
||||
o.Client.Init(client.Transport(t))
|
||||
o.Server.Init(server.Transport(t))
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience options
|
||||
|
||||
// Address sets the address of the server
|
||||
func Address(addr string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Address(addr))
|
||||
}
|
||||
}
|
||||
|
||||
// Name of the service
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Name(n))
|
||||
}
|
||||
}
|
||||
|
||||
// Version of the service
|
||||
func Version(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Version(v))
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata associated with the service
|
||||
func Metadata(md map[string]string) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.Metadata(md))
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterTTL specifies the TTL to use when registering the service
|
||||
func RegisterTTL(t time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.RegisterTTL(t))
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterInterval specifies the interval on which to re-register
|
||||
func RegisterInterval(t time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.Server.Init(server.RegisterInterval(t))
|
||||
}
|
||||
}
|
||||
|
||||
// WrapClient is a convenience method for wrapping a Client with
|
||||
// some middleware component. A list of wrappers can be provided.
|
||||
// Wrappers are applied in reverse order so the last is executed first.
|
||||
func WrapClient(w ...client.Wrapper) Option {
|
||||
return func(o *Options) {
|
||||
// apply in reverse
|
||||
for i := len(w); i > 0; i-- {
|
||||
o.Client = w[i-1](o.Client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WrapCall is a convenience method for wrapping a Client CallFunc
|
||||
func WrapCall(w ...client.CallWrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.Client.Init(client.WrapCall(w...))
|
||||
}
|
||||
}
|
||||
|
||||
// WrapHandler adds a handler Wrapper to a list of options passed into the server
|
||||
func WrapHandler(w ...server.HandlerWrapper) Option {
|
||||
return func(o *Options) {
|
||||
var wrappers []server.Option
|
||||
|
||||
for _, wrap := range w {
|
||||
wrappers = append(wrappers, server.WrapHandler(wrap))
|
||||
}
|
||||
|
||||
// Init once
|
||||
o.Server.Init(wrappers...)
|
||||
}
|
||||
}
|
||||
|
||||
// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server
|
||||
func WrapSubscriber(w ...server.SubscriberWrapper) Option {
|
||||
return func(o *Options) {
|
||||
var wrappers []server.Option
|
||||
|
||||
for _, wrap := range w {
|
||||
wrappers = append(wrappers, server.WrapSubscriber(wrap))
|
||||
}
|
||||
|
||||
// Init once
|
||||
o.Server.Init(wrappers...)
|
||||
}
|
||||
}
|
||||
|
||||
// Before and Afters
|
||||
|
||||
func BeforeStart(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.BeforeStart = append(o.BeforeStart, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func BeforeStop(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.BeforeStop = append(o.BeforeStop, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func AfterStart(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.AfterStart = append(o.AfterStart, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func AfterStop(fn func() error) Option {
|
||||
return func(o *Options) {
|
||||
o.AfterStop = append(o.AfterStop, fn)
|
||||
}
|
||||
}
|
@@ -1,2 +1,16 @@
|
||||
// Package service encapsulates the client, server and other interfaces to provide a complete micro service.
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/client"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
Init(...Option)
|
||||
Options() Options
|
||||
Client() client.Client
|
||||
Server() server.Server
|
||||
Run() error
|
||||
String() string
|
||||
}
|
||||
|
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
glog "github.com/go-log/log"
|
||||
"github.com/micro/go-micro/client"
|
||||
proto "github.com/micro/go-micro/debug/proto"
|
||||
"github.com/micro/go-micro/registry/memory"
|
||||
proto "github.com/micro/go-micro/server/debug/proto"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
|
@@ -113,7 +113,7 @@ func (q *quicTransport) Dial(addr string, opts ...transport.DialOption) (transpo
|
||||
NextProtos: []string{"http/1.1"},
|
||||
}
|
||||
}
|
||||
s, err := quic.DialAddr(addr, config, nil)
|
||||
s, err := quic.DialAddr(addr, config, &quic.Config{KeepAlive: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (q *quicTransport) Listen(addr string, opts ...transport.ListenOption) (tra
|
||||
}
|
||||
}
|
||||
|
||||
l, err := quic.ListenAddr(addr, config, nil)
|
||||
l, err := quic.ListenAddr(addr, config, &quic.Config{KeepAlive: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -1,79 +1,593 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// KeepAliveTime defines time interval we send keepalive messages to outbound links
|
||||
KeepAliveTime = 30 * time.Second
|
||||
// ReconnectTime defines time interval we periodically attempt to reconnect dead links
|
||||
ReconnectTime = 5 * time.Second
|
||||
)
|
||||
|
||||
// tun represents a network tunnel
|
||||
type tun struct {
|
||||
options Options
|
||||
|
||||
sync.RWMutex
|
||||
tr transport.Transport
|
||||
options Options
|
||||
|
||||
// tunnel token
|
||||
token string
|
||||
|
||||
// to indicate if we're connected or not
|
||||
connected bool
|
||||
closed chan bool
|
||||
|
||||
// the send channel for all messages
|
||||
send chan *message
|
||||
|
||||
// close channel
|
||||
closed chan bool
|
||||
|
||||
// a map of sockets based on Micro-Tunnel-Id
|
||||
sockets map[string]*socket
|
||||
|
||||
// outbound links
|
||||
links map[string]*link
|
||||
|
||||
// listener
|
||||
listener transport.Listener
|
||||
}
|
||||
|
||||
func newTunnel(opts ...Option) Tunnel {
|
||||
// initialize default options
|
||||
options := DefaultOptions()
|
||||
type link struct {
|
||||
transport.Socket
|
||||
id string
|
||||
loopback bool
|
||||
lastKeepAlive time.Time
|
||||
}
|
||||
|
||||
// create new tunnel on top of a link
|
||||
func newTunnel(opts ...Option) *tun {
|
||||
options := DefaultOptions()
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// tunnel transport
|
||||
tr := newTransport()
|
||||
|
||||
t := &tun{
|
||||
tr: tr,
|
||||
return &tun{
|
||||
options: options,
|
||||
token: uuid.New().String(),
|
||||
send: make(chan *message, 128),
|
||||
closed: make(chan bool),
|
||||
sockets: make(map[string]*socket),
|
||||
links: make(map[string]*link),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes tunnel options
|
||||
func (t *tun) Init(opts ...Option) error {
|
||||
for _, o := range opts {
|
||||
o(&t.options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSocket returns a socket from the internal socket map.
|
||||
// It does this based on the Micro-Tunnel-Id and Micro-Tunnel-Session
|
||||
func (t *tun) getSocket(id, session string) (*socket, bool) {
|
||||
// get the socket
|
||||
t.RLock()
|
||||
s, ok := t.sockets[id+session]
|
||||
t.RUnlock()
|
||||
return s, ok
|
||||
}
|
||||
|
||||
// newSocket creates a new socket and saves it
|
||||
func (t *tun) newSocket(id, session string) (*socket, bool) {
|
||||
// hash the id
|
||||
h := sha256.New()
|
||||
h.Write([]byte(id))
|
||||
id = fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
||||
// new socket
|
||||
s := &socket{
|
||||
id: id,
|
||||
session: session,
|
||||
closed: make(chan bool),
|
||||
recv: make(chan *message, 128),
|
||||
send: t.send,
|
||||
wait: make(chan bool),
|
||||
}
|
||||
|
||||
return t
|
||||
// save socket
|
||||
t.Lock()
|
||||
_, ok := t.sockets[id+session]
|
||||
if ok {
|
||||
// socket already exists
|
||||
t.Unlock()
|
||||
return nil, false
|
||||
}
|
||||
|
||||
t.sockets[id+session] = s
|
||||
t.Unlock()
|
||||
|
||||
// return socket
|
||||
return s, true
|
||||
}
|
||||
|
||||
// Id returns tunnel id
|
||||
func (t *tun) Id() string {
|
||||
return t.options.Id
|
||||
// TODO: use tunnel id as part of the session
|
||||
func (t *tun) newSession() string {
|
||||
return uuid.New().String()
|
||||
}
|
||||
|
||||
// Options returns tunnel options
|
||||
func (t *tun) Options() Options {
|
||||
return t.options
|
||||
// monitor monitors outbound links and attempts to reconnect to the failed ones
|
||||
func (t *tun) monitor() {
|
||||
reconnect := time.NewTicker(ReconnectTime)
|
||||
defer reconnect.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.closed:
|
||||
return
|
||||
case <-reconnect.C:
|
||||
for _, node := range t.options.Nodes {
|
||||
t.Lock()
|
||||
if _, ok := t.links[node]; !ok {
|
||||
link, err := t.setupLink(node)
|
||||
if err != nil {
|
||||
log.Debugf("Tunnel failed to setup node link to %s: %v", node, err)
|
||||
t.Unlock()
|
||||
continue
|
||||
}
|
||||
t.links[node] = link
|
||||
}
|
||||
t.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Address returns tunnel listen address
|
||||
func (t *tun) Address() string {
|
||||
return t.options.Address
|
||||
// process outgoing messages sent by all local sockets
|
||||
func (t *tun) process() {
|
||||
// manage the send buffer
|
||||
// all pseudo sockets throw everything down this
|
||||
for {
|
||||
select {
|
||||
case msg := <-t.send:
|
||||
newMsg := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
Body: msg.data.Body,
|
||||
}
|
||||
|
||||
for k, v := range msg.data.Header {
|
||||
newMsg.Header[k] = v
|
||||
}
|
||||
|
||||
// set the tunnel id on the outgoing message
|
||||
newMsg.Header["Micro-Tunnel-Id"] = msg.id
|
||||
|
||||
// set the session id
|
||||
newMsg.Header["Micro-Tunnel-Session"] = msg.session
|
||||
|
||||
// set the tunnel token
|
||||
newMsg.Header["Micro-Tunnel-Token"] = t.token
|
||||
|
||||
// send the message via the interface
|
||||
t.Lock()
|
||||
if len(t.links) == 0 {
|
||||
log.Debugf("No links to send to")
|
||||
}
|
||||
for node, link := range t.links {
|
||||
if link.loopback && msg.outbound {
|
||||
continue
|
||||
}
|
||||
log.Debugf("Sending %+v to %s", newMsg, node)
|
||||
if err := link.Send(newMsg); err != nil {
|
||||
log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, err)
|
||||
delete(t.links, node)
|
||||
continue
|
||||
}
|
||||
}
|
||||
t.Unlock()
|
||||
case <-t.closed:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transport returns tunnel client transport
|
||||
func (t *tun) Transport() transport.Transport {
|
||||
return t.tr
|
||||
// process incoming messages
|
||||
func (t *tun) listen(link *link) {
|
||||
for {
|
||||
// process anything via the net interface
|
||||
msg := new(transport.Message)
|
||||
err := link.Recv(msg)
|
||||
if err != nil {
|
||||
log.Debugf("Tunnel link %s receive error: %#v", link.Remote(), err)
|
||||
t.Lock()
|
||||
delete(t.links, link.Remote())
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
switch msg.Header["Micro-Tunnel"] {
|
||||
case "connect":
|
||||
log.Debugf("Tunnel link %s received connect message", link.Remote())
|
||||
// check the Micro-Tunnel-Token
|
||||
token, ok := msg.Header["Micro-Tunnel-Token"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// are we connecting to ourselves?
|
||||
if token == t.token {
|
||||
link.loopback = true
|
||||
}
|
||||
continue
|
||||
case "close":
|
||||
log.Debugf("Tunnel link %s closing connection", link.Remote())
|
||||
// TODO: handle the close message
|
||||
// maybe report io.EOF or kill the link
|
||||
continue
|
||||
case "keepalive":
|
||||
log.Debugf("Tunnel link %s received keepalive", link.Remote())
|
||||
link.lastKeepAlive = time.Now()
|
||||
continue
|
||||
}
|
||||
|
||||
// the tunnel id
|
||||
id := msg.Header["Micro-Tunnel-Id"]
|
||||
delete(msg.Header, "Micro-Tunnel-Id")
|
||||
|
||||
// the session id
|
||||
session := msg.Header["Micro-Tunnel-Session"]
|
||||
delete(msg.Header, "Micro-Tunnel-Session")
|
||||
|
||||
// if the session id is blank there's nothing we can do
|
||||
// TODO: check this is the case, is there any reason
|
||||
// why we'd have a blank session? Is the tunnel
|
||||
// used for some other purpose?
|
||||
if len(id) == 0 || len(session) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var s *socket
|
||||
var exists bool
|
||||
|
||||
log.Debugf("Received %+v from %s", msg, link.Remote())
|
||||
|
||||
switch {
|
||||
case link.loopback:
|
||||
s, exists = t.getSocket(id, "listener")
|
||||
default:
|
||||
// get the socket based on the tunnel id and session
|
||||
// this could be something we dialed in which case
|
||||
// we have a session for it otherwise its a listener
|
||||
s, exists = t.getSocket(id, session)
|
||||
if !exists {
|
||||
// try get it based on just the tunnel id
|
||||
// the assumption here is that a listener
|
||||
// has no session but its set a listener session
|
||||
s, exists = t.getSocket(id, "listener")
|
||||
}
|
||||
}
|
||||
// bail if no socket has been found
|
||||
if !exists {
|
||||
log.Debugf("Tunnel skipping no socket exists")
|
||||
// drop it, we don't care about
|
||||
// messages we don't know about
|
||||
continue
|
||||
}
|
||||
log.Debugf("Tunnel using socket %s %s", s.id, s.session)
|
||||
|
||||
// is the socket closed?
|
||||
select {
|
||||
case <-s.closed:
|
||||
// closed
|
||||
delete(t.sockets, id)
|
||||
continue
|
||||
default:
|
||||
// process
|
||||
}
|
||||
|
||||
// is the socket new?
|
||||
select {
|
||||
// if its new the socket is actually blocked waiting
|
||||
// for a connection. so we check if its waiting.
|
||||
case <-s.wait:
|
||||
// if its waiting e.g its new then we close it
|
||||
default:
|
||||
// set remote address of the socket
|
||||
s.remote = msg.Header["Remote"]
|
||||
close(s.wait)
|
||||
}
|
||||
|
||||
// construct a new transport message
|
||||
tmsg := &transport.Message{
|
||||
Header: msg.Header,
|
||||
Body: msg.Body,
|
||||
}
|
||||
|
||||
// construct the internal message
|
||||
imsg := &message{
|
||||
id: id,
|
||||
session: session,
|
||||
data: tmsg,
|
||||
}
|
||||
|
||||
// append to recv backlog
|
||||
// we don't block if we can't pass it on
|
||||
select {
|
||||
case s.recv <- imsg:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connect connects establishes point to point tunnel
|
||||
// keepalive periodically sends keepalive messages to link
|
||||
func (t *tun) keepalive(link *link) {
|
||||
keepalive := time.NewTicker(KeepAliveTime)
|
||||
defer keepalive.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.closed:
|
||||
return
|
||||
case <-keepalive.C:
|
||||
// send keepalive message
|
||||
log.Debugf("Tunnel sending keepalive to link: %v", link.Remote())
|
||||
if err := link.Send(&transport.Message{
|
||||
Header: map[string]string{
|
||||
"Micro-Tunnel": "keepalive",
|
||||
"Micro-Tunnel-Token": t.token,
|
||||
},
|
||||
}); err != nil {
|
||||
log.Debugf("Error sending keepalive to link %v: %v", link.Remote(), err)
|
||||
t.Lock()
|
||||
delete(t.links, link.Remote())
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setupLink connects to node and returns link if successful
|
||||
// It returns error if the link failed to be established
|
||||
func (t *tun) setupLink(node string) (*link, error) {
|
||||
log.Debugf("Tunnel dialing %s", node)
|
||||
c, err := t.options.Transport.Dial(node)
|
||||
if err != nil {
|
||||
log.Debugf("Tunnel failed to connect to %s: %v", node, err)
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Tunnel connected to %s", node)
|
||||
|
||||
if err := c.Send(&transport.Message{
|
||||
Header: map[string]string{
|
||||
"Micro-Tunnel": "connect",
|
||||
"Micro-Tunnel-Token": t.token,
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// save the link
|
||||
id := uuid.New().String()
|
||||
link := &link{
|
||||
Socket: c,
|
||||
id: id,
|
||||
}
|
||||
t.links[node] = link
|
||||
|
||||
// process incoming messages
|
||||
go t.listen(link)
|
||||
|
||||
// start keepalive monitor
|
||||
go t.keepalive(link)
|
||||
|
||||
return link, nil
|
||||
}
|
||||
|
||||
// connect the tunnel to all the nodes and listen for incoming tunnel connections
|
||||
func (t *tun) connect() error {
|
||||
l, err := t.options.Transport.Listen(t.options.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save the listener
|
||||
t.listener = l
|
||||
|
||||
go func() {
|
||||
// accept inbound connections
|
||||
err := l.Accept(func(sock transport.Socket) {
|
||||
log.Debugf("Tunnel accepted connection from %s", sock.Remote())
|
||||
// save the link
|
||||
id := uuid.New().String()
|
||||
t.Lock()
|
||||
link := &link{
|
||||
Socket: sock,
|
||||
id: id,
|
||||
}
|
||||
t.links[sock.Remote()] = link
|
||||
t.Unlock()
|
||||
|
||||
// delete the link
|
||||
defer func() {
|
||||
log.Debugf("Tunnel deleting connection from %s", sock.Remote())
|
||||
t.Lock()
|
||||
delete(t.links, sock.Remote())
|
||||
t.Unlock()
|
||||
}()
|
||||
|
||||
// listen for inbound messages
|
||||
t.listen(link)
|
||||
})
|
||||
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
// still connected but the tunnel died
|
||||
if err != nil && t.connected {
|
||||
log.Logf("Tunnel listener died: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, node := range t.options.Nodes {
|
||||
// skip zero length nodes
|
||||
if len(node) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// connect to node and return link
|
||||
link, err := t.setupLink(node)
|
||||
if err != nil {
|
||||
log.Debugf("Tunnel failed to establish node link to %s: %v", node, err)
|
||||
continue
|
||||
}
|
||||
// save the link
|
||||
t.links[node] = link
|
||||
}
|
||||
|
||||
// process outbound messages to be sent
|
||||
// process sends to all links
|
||||
go t.process()
|
||||
|
||||
// monitor links
|
||||
go t.monitor()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Connect the tunnel
|
||||
func (t *tun) Connect() error {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
// already connected
|
||||
if t.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
// send the connect message
|
||||
if err := t.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set as connected
|
||||
t.connected = true
|
||||
// create new close channel
|
||||
t.closed = make(chan bool)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the tunnel
|
||||
func (t *tun) close() error {
|
||||
// close all the links
|
||||
for node, link := range t.links {
|
||||
link.Send(&transport.Message{
|
||||
Header: map[string]string{
|
||||
"Micro-Tunnel": "close",
|
||||
"Micro-Tunnel-Token": t.token,
|
||||
},
|
||||
})
|
||||
link.Close()
|
||||
delete(t.links, node)
|
||||
}
|
||||
|
||||
// close the listener
|
||||
return t.listener.Close()
|
||||
}
|
||||
|
||||
// Close the tunnel
|
||||
func (t *tun) Close() error {
|
||||
return nil
|
||||
}
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
if !t.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status returns tunnel status
|
||||
func (t *tun) Status() Status {
|
||||
select {
|
||||
case <-t.closed:
|
||||
return Closed
|
||||
return nil
|
||||
default:
|
||||
return Connected
|
||||
// close all the sockets
|
||||
for id, s := range t.sockets {
|
||||
s.Close()
|
||||
delete(t.sockets, id)
|
||||
}
|
||||
// close the connection
|
||||
close(t.closed)
|
||||
t.connected = false
|
||||
|
||||
// send a close message
|
||||
// we don't close the link
|
||||
// just the tunnel
|
||||
return t.close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tun) String() string {
|
||||
return "micro"
|
||||
// Dial an address
|
||||
func (t *tun) Dial(addr string) (Conn, error) {
|
||||
log.Debugf("Tunnel dialing %s", addr)
|
||||
c, ok := t.newSocket(addr, t.newSession())
|
||||
if !ok {
|
||||
return nil, errors.New("error dialing " + addr)
|
||||
}
|
||||
// set remote
|
||||
c.remote = addr
|
||||
// set local
|
||||
c.local = "local"
|
||||
// outbound socket
|
||||
c.outbound = true
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Accept a connection on the address
|
||||
func (t *tun) Listen(addr string) (Listener, error) {
|
||||
log.Debugf("Tunnel listening on %s", addr)
|
||||
// create a new socket by hashing the address
|
||||
c, ok := t.newSocket(addr, "listener")
|
||||
if !ok {
|
||||
return nil, errors.New("already listening on " + addr)
|
||||
}
|
||||
|
||||
// set remote. it will be replaced by the first message received
|
||||
c.remote = "remote"
|
||||
// set local
|
||||
c.local = addr
|
||||
|
||||
tl := &tunListener{
|
||||
addr: addr,
|
||||
// the accept channel
|
||||
accept: make(chan *socket, 128),
|
||||
// the channel to close
|
||||
closed: make(chan bool),
|
||||
// tunnel closed channel
|
||||
tunClosed: t.closed,
|
||||
// the connection
|
||||
conn: c,
|
||||
// the listener socket
|
||||
socket: c,
|
||||
}
|
||||
|
||||
// this kicks off the internal message processor
|
||||
// for the listener so it can create pseudo sockets
|
||||
// per session if they do not exist or pass messages
|
||||
// to the existign sessions
|
||||
go tl.process()
|
||||
|
||||
// return the listener
|
||||
return tl, nil
|
||||
}
|
||||
|
109
tunnel/listener.go
Normal file
109
tunnel/listener.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
type tunListener struct {
|
||||
// address of the listener
|
||||
addr string
|
||||
// the accept channel
|
||||
accept chan *socket
|
||||
// the channel to close
|
||||
closed chan bool
|
||||
// the tunnel closed channel
|
||||
tunClosed chan bool
|
||||
// the connection
|
||||
conn Conn
|
||||
// the listener socket
|
||||
socket *socket
|
||||
}
|
||||
|
||||
func (t *tunListener) process() {
|
||||
// our connection map for session
|
||||
conns := make(map[string]*socket)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.closed:
|
||||
return
|
||||
// receive a new message
|
||||
case m := <-t.socket.recv:
|
||||
// get a socket
|
||||
sock, ok := conns[m.session]
|
||||
log.Debugf("Tunnel listener received id %s session %s exists: %t", m.id, m.session, ok)
|
||||
if !ok {
|
||||
// create a new socket session
|
||||
sock = &socket{
|
||||
// our tunnel id
|
||||
id: m.id,
|
||||
// the session id
|
||||
session: m.session,
|
||||
// close chan
|
||||
closed: make(chan bool),
|
||||
// recv called by the acceptor
|
||||
recv: make(chan *message, 128),
|
||||
// use the internal send buffer
|
||||
send: t.socket.send,
|
||||
// wait
|
||||
wait: make(chan bool),
|
||||
}
|
||||
|
||||
// save the socket
|
||||
conns[m.session] = sock
|
||||
|
||||
// send to accept chan
|
||||
select {
|
||||
case <-t.closed:
|
||||
return
|
||||
case t.accept <- sock:
|
||||
}
|
||||
}
|
||||
|
||||
// send this to the accept chan
|
||||
select {
|
||||
case <-sock.closed:
|
||||
delete(conns, m.session)
|
||||
case sock.recv <- m:
|
||||
log.Debugf("Tunnel listener sent to recv chan id %s session %s", m.id, m.session)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunListener) Addr() string {
|
||||
return t.addr
|
||||
}
|
||||
|
||||
// Close closes tunnel listener
|
||||
func (t *tunListener) Close() error {
|
||||
select {
|
||||
case <-t.closed:
|
||||
return nil
|
||||
default:
|
||||
close(t.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Everytime accept is called we essentially block till we get a new connection
|
||||
func (t *tunListener) Accept() (Conn, error) {
|
||||
select {
|
||||
// if the socket is closed return
|
||||
case <-t.closed:
|
||||
return nil, io.EOF
|
||||
case <-t.tunClosed:
|
||||
// close the listener when the tunnel closes
|
||||
t.Close()
|
||||
return nil, io.EOF
|
||||
// wait for a new connection
|
||||
case c, ok := <-t.accept:
|
||||
if !ok {
|
||||
return nil, io.EOF
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
@@ -3,6 +3,7 @@ package tunnel
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/transport/quic"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -39,7 +40,7 @@ func Address(a string) Option {
|
||||
}
|
||||
|
||||
// Nodes specify remote network nodes
|
||||
func Nodes(n []string) Option {
|
||||
func Nodes(n ...string) Option {
|
||||
return func(o *Options) {
|
||||
o.Nodes = n
|
||||
}
|
||||
@@ -57,7 +58,6 @@ func DefaultOptions() Options {
|
||||
return Options{
|
||||
Id: uuid.New().String(),
|
||||
Address: DefaultAddress,
|
||||
Nodes: make([]string, 0),
|
||||
Transport: transport.DefaultTransport,
|
||||
Transport: quic.NewTransport(),
|
||||
}
|
||||
}
|
||||
|
116
tunnel/socket.go
116
tunnel/socket.go
@@ -1,25 +1,115 @@
|
||||
package tunnel
|
||||
|
||||
import "github.com/micro/go-micro/transport"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
type tunSocket struct{}
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/util/log"
|
||||
)
|
||||
|
||||
func (s *tunSocket) Recv(m *transport.Message) error {
|
||||
// socket is our pseudo socket for transport.Socket
|
||||
type socket struct {
|
||||
// socket id based on Micro-Tunnel
|
||||
id string
|
||||
// the session id based on Micro.Tunnel-Session
|
||||
session string
|
||||
// closed
|
||||
closed chan bool
|
||||
// remote addr
|
||||
remote string
|
||||
// local addr
|
||||
local string
|
||||
// send chan
|
||||
send chan *message
|
||||
// recv chan
|
||||
recv chan *message
|
||||
// wait until we have a connection
|
||||
wait chan bool
|
||||
// outbound marks the socket as outbound
|
||||
outbound bool
|
||||
}
|
||||
|
||||
// message is sent over the send channel
|
||||
type message struct {
|
||||
// tunnel id
|
||||
id string
|
||||
// the session id
|
||||
session string
|
||||
// outbound marks the message as outbound
|
||||
outbound bool
|
||||
// transport data
|
||||
data *transport.Message
|
||||
}
|
||||
|
||||
func (s *socket) Remote() string {
|
||||
return s.remote
|
||||
}
|
||||
|
||||
func (s *socket) Local() string {
|
||||
return s.local
|
||||
}
|
||||
|
||||
func (s *socket) Id() string {
|
||||
return s.id
|
||||
}
|
||||
|
||||
func (s *socket) Session() string {
|
||||
return s.session
|
||||
}
|
||||
|
||||
func (s *socket) Send(m *transport.Message) error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return errors.New("socket is closed")
|
||||
default:
|
||||
// no op
|
||||
}
|
||||
|
||||
// make copy
|
||||
data := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
Body: m.Body,
|
||||
}
|
||||
|
||||
for k, v := range m.Header {
|
||||
data.Header[k] = v
|
||||
}
|
||||
|
||||
// append to backlog
|
||||
msg := &message{
|
||||
id: s.id,
|
||||
session: s.session,
|
||||
outbound: s.outbound,
|
||||
data: data,
|
||||
}
|
||||
log.Debugf("Appending %+v to send backlog", msg)
|
||||
s.send <- msg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tunSocket) Send(m *transport.Message) error {
|
||||
func (s *socket) Recv(m *transport.Message) error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return errors.New("socket is closed")
|
||||
default:
|
||||
// no op
|
||||
}
|
||||
// recv from backlog
|
||||
msg := <-s.recv
|
||||
log.Debugf("Received %+v from recv backlog", msg)
|
||||
// set message
|
||||
*m = *msg.data
|
||||
// return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tunSocket) Close() error {
|
||||
// Close closes the socket
|
||||
func (s *socket) Close() error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
// no op
|
||||
default:
|
||||
close(s.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *tunSocket) Local() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *tunSocket) Remote() string {
|
||||
return ""
|
||||
}
|
||||
|
@@ -1,51 +0,0 @@
|
||||
package tunnel
|
||||
|
||||
import "github.com/micro/go-micro/transport"
|
||||
|
||||
type tunTransport struct {
|
||||
options transport.Options
|
||||
}
|
||||
|
||||
type tunClient struct {
|
||||
*tunSocket
|
||||
options transport.DialOptions
|
||||
}
|
||||
|
||||
type tunListener struct {
|
||||
conn chan *tunSocket
|
||||
}
|
||||
|
||||
func newTransport(opts ...transport.Option) transport.Transport {
|
||||
var options transport.Options
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
return &tunTransport{
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunTransport) Init(opts ...transport.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&t.options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunTransport) Options() transport.Options {
|
||||
return t.options
|
||||
}
|
||||
|
||||
func (t *tunTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *tunTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *tunTransport) String() string {
|
||||
return "micro"
|
||||
}
|
30
tunnel/transport/listener.go
Normal file
30
tunnel/transport/listener.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/tunnel"
|
||||
)
|
||||
|
||||
type tunListener struct {
|
||||
l tunnel.Listener
|
||||
}
|
||||
|
||||
func (t *tunListener) Addr() string {
|
||||
return t.l.Addr()
|
||||
}
|
||||
|
||||
func (t *tunListener) Close() error {
|
||||
return t.l.Close()
|
||||
}
|
||||
|
||||
func (t *tunListener) Accept(fn func(socket transport.Socket)) error {
|
||||
for {
|
||||
// accept connection
|
||||
c, err := t.l.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// execute the function
|
||||
go fn(c)
|
||||
}
|
||||
}
|
113
tunnel/transport/transport.go
Normal file
113
tunnel/transport/transport.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Package transport provides a tunnel transport
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/transport"
|
||||
"github.com/micro/go-micro/tunnel"
|
||||
)
|
||||
|
||||
type tunTransport struct {
|
||||
options transport.Options
|
||||
|
||||
tunnel tunnel.Tunnel
|
||||
}
|
||||
|
||||
type tunnelKey struct{}
|
||||
|
||||
type transportKey struct{}
|
||||
|
||||
func (t *tunTransport) Init(opts ...transport.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&t.options)
|
||||
}
|
||||
|
||||
// close the existing tunnel
|
||||
if t.tunnel != nil {
|
||||
t.tunnel.Close()
|
||||
}
|
||||
|
||||
// get the tunnel
|
||||
tun, ok := t.options.Context.Value(tunnelKey{}).(tunnel.Tunnel)
|
||||
if !ok {
|
||||
tun = tunnel.NewTunnel()
|
||||
}
|
||||
|
||||
// get the transport
|
||||
tr, ok := t.options.Context.Value(transportKey{}).(transport.Transport)
|
||||
if ok {
|
||||
tun.Init(tunnel.Transport(tr))
|
||||
}
|
||||
|
||||
// set the tunnel
|
||||
t.tunnel = tun
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunTransport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) {
|
||||
if err := t.tunnel.Connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := t.tunnel.Dial(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (t *tunTransport) Listen(addr string, opts ...transport.ListenOption) (transport.Listener, error) {
|
||||
if err := t.tunnel.Connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l, err := t.tunnel.Listen(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tunListener{l}, nil
|
||||
}
|
||||
|
||||
func (t *tunTransport) Options() transport.Options {
|
||||
return t.options
|
||||
}
|
||||
|
||||
func (t *tunTransport) String() string {
|
||||
return "tunnel"
|
||||
}
|
||||
|
||||
// NewTransport honours the initialiser used in
|
||||
func NewTransport(opts ...transport.Option) transport.Transport {
|
||||
t := &tunTransport{
|
||||
options: transport.Options{},
|
||||
}
|
||||
|
||||
// initialise
|
||||
t.Init(opts...)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// WithTransport sets the internal tunnel
|
||||
func WithTunnel(t tunnel.Tunnel) transport.Option {
|
||||
return func(o *transport.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, tunnelKey{}, t)
|
||||
}
|
||||
}
|
||||
|
||||
// WithTransport sets the internal transport
|
||||
func WithTransport(t transport.Transport) transport.Option {
|
||||
return func(o *transport.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, transportKey{}, t)
|
||||
}
|
||||
}
|
@@ -1,39 +1,44 @@
|
||||
// Package tunnel provides micro network tunnelling
|
||||
// Package tunnel provides gre network tunnelling
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
// Status is tunnel status
|
||||
type Status int
|
||||
|
||||
const (
|
||||
// Connected means the tunnel is alive
|
||||
Connected Status = iota
|
||||
// Closed meands the tunnel has been disconnected
|
||||
Closed
|
||||
)
|
||||
|
||||
// Tunnel creates a p2p network tunnel.
|
||||
// Tunnel creates a gre network tunnel on top of a link.
|
||||
// It establishes multiple streams using the Micro-Tunnel-Id header
|
||||
// and Micro-Tunnel-Session header. The tunnel id is a hash of
|
||||
// the address being requested.
|
||||
type Tunnel interface {
|
||||
// Id returns tunnel id
|
||||
Id() string
|
||||
// Options returns the tunnel options
|
||||
Options() Options
|
||||
// Address returns tunnel address
|
||||
Address() string
|
||||
// Transport to use by tunne clients
|
||||
Transport() transport.Transport
|
||||
Init(opts ...Option) error
|
||||
// Connect connects the tunnel
|
||||
Connect() error
|
||||
// Close closes the tunnel
|
||||
Close() error
|
||||
// Status returns tunnel status
|
||||
Status() Status
|
||||
// Dial an endpoint
|
||||
Dial(addr string) (Conn, error)
|
||||
// Accept connections
|
||||
Listen(addr string) (Listener, error)
|
||||
}
|
||||
|
||||
// NewTunnel creates a new tunnel on top of a link
|
||||
// The listener provides similar constructs to the transport.Listener
|
||||
type Listener interface {
|
||||
Addr() string
|
||||
Close() error
|
||||
Accept() (Conn, error)
|
||||
}
|
||||
|
||||
// Conn is a connection dialed or accepted which includes the tunnel id and session
|
||||
type Conn interface {
|
||||
// Specifies the tunnel id
|
||||
Id() string
|
||||
// The session
|
||||
Session() string
|
||||
// a transport socket
|
||||
transport.Socket
|
||||
}
|
||||
|
||||
// NewTunnel creates a new tunnel
|
||||
func NewTunnel(opts ...Option) Tunnel {
|
||||
return newTunnel(opts...)
|
||||
}
|
||||
|
300
tunnel/tunnel_test.go
Normal file
300
tunnel/tunnel_test.go
Normal file
@@ -0,0 +1,300 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
// testAccept will accept connections on the transport, create a new link and tunnel on top
|
||||
func testAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) {
|
||||
// listen on some virtual address
|
||||
tl, err := tun.Listen("test-tunnel")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// receiver ready; notify sender
|
||||
wait <- true
|
||||
|
||||
// accept a connection
|
||||
c, err := tl.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// get a message
|
||||
for {
|
||||
// accept the message
|
||||
m := new(transport.Message)
|
||||
if err := c.Recv(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v := m.Header["test"]; v != "send" {
|
||||
t.Fatalf("Accept side expected test:send header. Received: %s", v)
|
||||
}
|
||||
|
||||
// now respond
|
||||
m.Header["test"] = "accept"
|
||||
if err := c.Send(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// testSend will create a new link to an address and then a tunnel on top
|
||||
func testSend(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
// wait for the listener to get ready
|
||||
<-wait
|
||||
|
||||
// dial a new session
|
||||
c, err := tun.Dial("test-tunnel")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
m := transport.Message{
|
||||
Header: map[string]string{
|
||||
"test": "send",
|
||||
},
|
||||
}
|
||||
|
||||
// send the message
|
||||
if err := c.Send(&m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// now wait for the response
|
||||
mr := new(transport.Message)
|
||||
if err := c.Recv(mr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v := mr.Header["test"]; v != "accept" {
|
||||
t.Fatalf("Message not received from accepted side. Received: %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTunnel(t *testing.T) {
|
||||
// create a new tunnel client
|
||||
tunA := NewTunnel(
|
||||
Address("127.0.0.1:9096"),
|
||||
Nodes("127.0.0.1:9097"),
|
||||
)
|
||||
|
||||
// create a new tunnel server
|
||||
tunB := NewTunnel(
|
||||
Address("127.0.0.1:9097"),
|
||||
)
|
||||
|
||||
// start tunB
|
||||
err := tunB.Connect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tunB.Close()
|
||||
|
||||
// start tunA
|
||||
err = tunA.Connect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tunA.Close()
|
||||
|
||||
wait := make(chan bool)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
// start the listener
|
||||
go testAccept(t, tunB, wait, &wg)
|
||||
|
||||
wg.Add(1)
|
||||
// start the client
|
||||
go testSend(t, tunA, wait, &wg)
|
||||
|
||||
// wait until done
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestLoopbackTunnel(t *testing.T) {
|
||||
// create a new tunnel
|
||||
tun := NewTunnel(
|
||||
Address("127.0.0.1:9096"),
|
||||
Nodes("127.0.0.1:9096"),
|
||||
)
|
||||
|
||||
// start tunnel
|
||||
err := tun.Connect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tun.Close()
|
||||
|
||||
wait := make(chan bool)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
// start the listener
|
||||
go testAccept(t, tun, wait, &wg)
|
||||
|
||||
wg.Add(1)
|
||||
// start the client
|
||||
go testSend(t, tun, wait, &wg)
|
||||
|
||||
// wait until done
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func testBrokenTunAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
// listen on some virtual address
|
||||
tl, err := tun.Listen("test-tunnel")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// receiver ready; notify sender
|
||||
wait <- true
|
||||
|
||||
// accept a connection
|
||||
c, err := tl.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// accept the message and close the tunnel
|
||||
// we do this to simulate loss of network connection
|
||||
m := new(transport.Message)
|
||||
if err := c.Recv(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tun.Close()
|
||||
|
||||
// re-start tunnel
|
||||
err = tun.Connect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tun.Close()
|
||||
|
||||
// listen on some virtual address
|
||||
tl, err = tun.Listen("test-tunnel")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// receiver ready; notify sender
|
||||
wait <- true
|
||||
|
||||
// accept a connection
|
||||
c, err = tl.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// accept the message
|
||||
m = new(transport.Message)
|
||||
if err := c.Recv(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// notify sender we have received the message
|
||||
<-wait
|
||||
}
|
||||
|
||||
func testBrokenTunSend(t *testing.T, tun Tunnel, wait chan bool, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
// wait for the listener to get ready
|
||||
<-wait
|
||||
|
||||
// dial a new session
|
||||
c, err := tun.Dial("test-tunnel")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
m := transport.Message{
|
||||
Header: map[string]string{
|
||||
"test": "send",
|
||||
},
|
||||
}
|
||||
|
||||
// send the message
|
||||
if err := c.Send(&m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for the listener to get ready
|
||||
<-wait
|
||||
|
||||
// give it time to reconnect
|
||||
time.Sleep(2 * ReconnectTime)
|
||||
|
||||
// send the message
|
||||
if err := c.Send(&m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for the listener to receive the message
|
||||
// c.Send merely enqueues the message to the link send queue and returns
|
||||
// in order to verify it was received we wait for the listener to tell us
|
||||
wait <- true
|
||||
}
|
||||
|
||||
func TestReconnectTunnel(t *testing.T) {
|
||||
// create a new tunnel client
|
||||
tunA := NewTunnel(
|
||||
Address("127.0.0.1:9096"),
|
||||
Nodes("127.0.0.1:9097"),
|
||||
)
|
||||
|
||||
// create a new tunnel server
|
||||
tunB := NewTunnel(
|
||||
Address("127.0.0.1:9097"),
|
||||
)
|
||||
|
||||
// start tunnel
|
||||
err := tunB.Connect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// we manually override the tunnel.ReconnectTime value here
|
||||
// this is so that we make the reconnects faster than the default 5s
|
||||
ReconnectTime = 200 * time.Millisecond
|
||||
|
||||
// start tunnel
|
||||
err = tunA.Connect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wait := make(chan bool)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
// start tunnel listener
|
||||
go testBrokenTunAccept(t, tunB, wait, &wg)
|
||||
|
||||
wg.Add(1)
|
||||
// start tunnel sender
|
||||
go testBrokenTunSend(t, tunA, wait, &wg)
|
||||
|
||||
// wait until done
|
||||
wg.Wait()
|
||||
}
|
40
util/io/io.go
Normal file
40
util/io/io.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Package io is for io management
|
||||
package io
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
type rwc struct {
|
||||
socket transport.Socket
|
||||
}
|
||||
|
||||
func (r *rwc) Read(p []byte) (n int, err error) {
|
||||
m := new(transport.Message)
|
||||
if err := r.socket.Recv(m); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(p, m.Body)
|
||||
return len(m.Body), nil
|
||||
}
|
||||
|
||||
func (r *rwc) Write(p []byte) (n int, err error) {
|
||||
err = r.socket.Send(&transport.Message{
|
||||
Body: p,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (r *rwc) Close() error {
|
||||
return r.socket.Close()
|
||||
}
|
||||
|
||||
// NewRWC returns a new ReadWriteCloser
|
||||
func NewRWC(sock transport.Socket) io.ReadWriteCloser {
|
||||
return &rwc{sock}
|
||||
}
|
@@ -12,20 +12,36 @@ import (
|
||||
type Level int
|
||||
|
||||
const (
|
||||
trace Level = iota
|
||||
debug
|
||||
info
|
||||
fatal
|
||||
LevelFatal Level = iota
|
||||
LevelInfo
|
||||
LevelWarn
|
||||
LevelDebug
|
||||
LevelTrace
|
||||
)
|
||||
|
||||
var (
|
||||
// the local logger
|
||||
logger log.Logger = golog.New()
|
||||
|
||||
// default log level is debug
|
||||
level = info
|
||||
// default log level is info
|
||||
level = LevelInfo
|
||||
)
|
||||
|
||||
func init() {
|
||||
switch os.Getenv("MICRO_LOG_LEVEL") {
|
||||
case "debug":
|
||||
level = LevelDebug
|
||||
case "info":
|
||||
level = LevelInfo
|
||||
case "trace":
|
||||
level = LevelTrace
|
||||
case "fatal":
|
||||
level = LevelFatal
|
||||
case "warn":
|
||||
level = LevelWarn
|
||||
}
|
||||
}
|
||||
|
||||
// Log makes use of github.com/go-log/log.Log
|
||||
func Log(v ...interface{}) {
|
||||
logger.Log(v...)
|
||||
@@ -38,7 +54,7 @@ func Logf(format string, v ...interface{}) {
|
||||
|
||||
// WithLevel logs with the level specified
|
||||
func WithLevel(l Level, v ...interface{}) {
|
||||
if l < level {
|
||||
if l > level {
|
||||
return
|
||||
}
|
||||
Log(v...)
|
||||
@@ -46,7 +62,7 @@ func WithLevel(l Level, v ...interface{}) {
|
||||
|
||||
// WithLevel logs with the level specified
|
||||
func WithLevelf(l Level, format string, v ...interface{}) {
|
||||
if l < level {
|
||||
if l > level {
|
||||
return
|
||||
}
|
||||
Logf(format, v...)
|
||||
@@ -54,43 +70,53 @@ func WithLevelf(l Level, format string, v ...interface{}) {
|
||||
|
||||
// Trace provides trace level logging
|
||||
func Trace(v ...interface{}) {
|
||||
WithLevel(trace, v...)
|
||||
WithLevel(LevelTrace, v...)
|
||||
}
|
||||
|
||||
// Tracef provides trace level logging
|
||||
func Tracef(format string, v ...interface{}) {
|
||||
WithLevelf(trace, format, v...)
|
||||
WithLevelf(LevelTrace, format, v...)
|
||||
}
|
||||
|
||||
// Debug provides debug level logging
|
||||
func Debug(v ...interface{}) {
|
||||
WithLevel(debug, v...)
|
||||
WithLevel(LevelDebug, v...)
|
||||
}
|
||||
|
||||
// Debugf provides debug level logging
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
WithLevelf(debug, format, v...)
|
||||
WithLevelf(LevelDebug, format, v...)
|
||||
}
|
||||
|
||||
// Info provides info level logging
|
||||
func Info(v ...interface{}) {
|
||||
WithLevel(info, v...)
|
||||
WithLevel(LevelInfo, v...)
|
||||
}
|
||||
|
||||
// Infof provides info level logging
|
||||
func Infof(format string, v ...interface{}) {
|
||||
WithLevelf(info, format, v...)
|
||||
WithLevelf(LevelInfo, format, v...)
|
||||
}
|
||||
|
||||
// Warn provides warn level logging
|
||||
func Warn(v ...interface{}) {
|
||||
WithLevel(LevelWarn, v...)
|
||||
}
|
||||
|
||||
// Warnf provides warn level logging
|
||||
func Warnf(format string, v ...interface{}) {
|
||||
WithLevelf(LevelWarn, format, v...)
|
||||
}
|
||||
|
||||
// Fatal logs with Log and then exits with os.Exit(1)
|
||||
func Fatal(v ...interface{}) {
|
||||
WithLevel(fatal, v...)
|
||||
WithLevel(LevelFatal, v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalf logs with Logf and then exits with os.Exit(1)
|
||||
func Fatalf(format string, v ...interface{}) {
|
||||
WithLevelf(fatal, format, v...)
|
||||
WithLevelf(LevelFatal, format, v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
47
util/mux/mux.go
Normal file
47
util/mux/mux.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Package mux provides proxy muxing
|
||||
package mux
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/go-micro/debug/handler"
|
||||
"github.com/micro/go-micro/proxy"
|
||||
"github.com/micro/go-micro/server"
|
||||
)
|
||||
|
||||
// Server is a proxy muxer that incudes the use of the DefaultHandler
|
||||
type Server struct {
|
||||
// name of service
|
||||
Name string
|
||||
// Proxy handler
|
||||
Proxy proxy.Proxy
|
||||
}
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func (s *Server) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
|
||||
if req.Service() == s.Name {
|
||||
return server.DefaultRouter.ServeRequest(ctx, req, rsp)
|
||||
}
|
||||
return s.Proxy.ServeRequest(ctx, req, rsp)
|
||||
}
|
||||
|
||||
func New(name string, p proxy.Proxy) *Server {
|
||||
// only register this once
|
||||
once.Do(func() {
|
||||
server.DefaultRouter.Handle(
|
||||
server.DefaultRouter.NewHandler(
|
||||
handler.DefaultHandler,
|
||||
server.InternalHandler(true),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
return &Server{
|
||||
Name: name,
|
||||
Proxy: p,
|
||||
}
|
||||
}
|
@@ -14,12 +14,13 @@ func HostPort(addr string, port interface{}) string {
|
||||
if strings.Count(addr, ":") > 0 {
|
||||
host = fmt.Sprintf("[%s]", addr)
|
||||
}
|
||||
// TODO check for NATS case
|
||||
if v, ok := port.(string); ok {
|
||||
if v == "" {
|
||||
return fmt.Sprintf("%s", host)
|
||||
}
|
||||
// when port is blank or 0, host is a queue name
|
||||
if v, ok := port.(string); ok && v == "" {
|
||||
return host
|
||||
} else if v, ok := port.(int); ok && v == 0 && net.ParseIP(host) == nil {
|
||||
return host
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%v", host, port)
|
||||
}
|
||||
|
||||
|
137
util/socket/socket.go
Normal file
137
util/socket/socket.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// Package socket provides a pseudo socket
|
||||
package socket
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/micro/go-micro/transport"
|
||||
)
|
||||
|
||||
// Socket is our pseudo socket for transport.Socket
|
||||
type Socket struct {
|
||||
// closed
|
||||
closed chan bool
|
||||
// remote addr
|
||||
remote string
|
||||
// local addr
|
||||
local string
|
||||
// send chan
|
||||
send chan *transport.Message
|
||||
// recv chan
|
||||
recv chan *transport.Message
|
||||
}
|
||||
|
||||
func (s *Socket) SetLocal(l string) {
|
||||
s.local = l
|
||||
}
|
||||
|
||||
func (s *Socket) SetRemote(r string) {
|
||||
s.remote = r
|
||||
}
|
||||
|
||||
// Accept passes a message to the socket which will be processed by the call to Recv
|
||||
func (s *Socket) Accept(m *transport.Message) error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return io.EOF
|
||||
case s.recv <- m:
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process takes the next message off the send queue created by a call to Send
|
||||
func (s *Socket) Process(m *transport.Message) error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return io.EOF
|
||||
case msg := <-s.send:
|
||||
*m = *msg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Socket) Remote() string {
|
||||
return s.remote
|
||||
}
|
||||
|
||||
func (s *Socket) Local() string {
|
||||
return s.local
|
||||
}
|
||||
|
||||
func (s *Socket) Send(m *transport.Message) error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return io.EOF
|
||||
default:
|
||||
// no op
|
||||
}
|
||||
|
||||
// make copy
|
||||
msg := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
Body: make([]byte, len(m.Body)),
|
||||
}
|
||||
|
||||
// copy headers
|
||||
for k, v := range m.Header {
|
||||
msg.Header[k] = v
|
||||
}
|
||||
|
||||
// copy body
|
||||
copy(msg.Body, m.Body)
|
||||
|
||||
// send a message
|
||||
select {
|
||||
case s.send <- msg:
|
||||
case <-s.closed:
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Socket) Recv(m *transport.Message) error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
return io.EOF
|
||||
default:
|
||||
// no op
|
||||
}
|
||||
|
||||
// receive a message
|
||||
select {
|
||||
case msg := <-s.recv:
|
||||
// set message
|
||||
*m = *msg
|
||||
case <-s.closed:
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
// return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the socket
|
||||
func (s *Socket) Close() error {
|
||||
select {
|
||||
case <-s.closed:
|
||||
// no op
|
||||
default:
|
||||
close(s.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new pseudo socket which can be used in the place of a transport socket.
|
||||
// Messages are sent to the socket via Accept and receives from the socket via Process.
|
||||
// SetLocal/SetRemote should be called before using the socket.
|
||||
func New() *Socket {
|
||||
return &Socket{
|
||||
closed: make(chan bool),
|
||||
local: "local",
|
||||
remote: "remote",
|
||||
send: make(chan *transport.Message, 128),
|
||||
recv: make(chan *transport.Message, 128),
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user