Merge pull request #5 from micro/master

pull request from go-micro
This commit is contained in:
potato 2019-09-30 17:22:51 +08:00 committed by GitHub
commit f933457cc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
122 changed files with 9934 additions and 1892 deletions

4
.gitignore vendored
View File

@ -1,3 +1,7 @@
# Develop tools
/.vscode/
/.idea/
# Binaries for programs and plugins # Binaries for programs and plugins
*.exe *.exe
*.exe~ *.exe~

View File

@ -32,5 +32,5 @@ Go Micro把分布式系统的各种细节抽象出来。下面是它的主要特
## 快速上手 ## 快速上手
更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/go-micro_cn.html)。 更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/cn/)。

View File

@ -30,6 +30,7 @@ var (
"application/proto": protoCodec{}, "application/proto": protoCodec{},
"application/protobuf": protoCodec{}, "application/protobuf": protoCodec{},
"application/octet-stream": protoCodec{}, "application/octet-stream": protoCodec{},
"application/grpc": protoCodec{},
"application/grpc+json": jsonCodec{}, "application/grpc+json": jsonCodec{},
"application/grpc+proto": protoCodec{}, "application/grpc+proto": protoCodec{},
"application/grpc+bytes": bytesCodec{}, "application/grpc+bytes": bytesCodec{},

View File

@ -18,7 +18,6 @@ import (
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/buf"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding" "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 // get next nodes from the selector
next, err := g.opts.Selector.Select(service, opts.SelectOptions...) next, err := g.opts.Selector.Select(service, opts.SelectOptions...)
if err != nil && err == selector.ErrNotFound { if err != nil {
return nil, errors.NotFound("go.micro.client", "service %s not found: %v", service, err.Error()) if err == selector.ErrNotFound {
} else if err != nil { return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
return nil, errors.InternalServerError("go.micro.client", err.Error()) }
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
} }
return next, nil return next, nil
@ -350,15 +350,17 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
// select next node // select next node
node, err := next() node, err := next()
if err != nil && err == selector.ErrNotFound { service := req.Service()
return errors.NotFound("go.micro.client", "service %s not found: %v", req.Service(), err.Error()) if err != nil {
} else if err != nil { if err == selector.ErrNotFound {
return errors.InternalServerError("go.micro.client", err.Error()) 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 // make the call
err = gcall(ctx, node, req, rsp, callOpts) err = gcall(ctx, node, req, rsp, callOpts)
g.opts.Selector.Mark(req.Service(), node, err) g.opts.Selector.Mark(service, node, err)
return err return err
} }
@ -429,14 +431,16 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
} }
node, err := next() node, err := next()
if err != nil && err == selector.ErrNotFound { service := req.Service()
return nil, errors.NotFound("go.micro.client", "service %s not found: %v", req.Service(), err.Error()) if err != nil {
} else if err != nil { if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", err.Error()) 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) 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 return stream, err
} }
@ -486,14 +490,13 @@ func (g *grpcClient) Publish(ctx context.Context, p client.Message, opts ...clie
} }
md["Content-Type"] = p.ContentType() md["Content-Type"] = p.ContentType()
cf, err := g.newCodec(p.ContentType()) cf, err := g.newGRPCCodec(p.ContentType())
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) return errors.InternalServerError("go.micro.client", err.Error())
} }
b := buf.New(nil) b, err := cf.Marshal(p.Payload())
if err != nil {
if err := cf(b).Write(&codec.Message{Type: codec.Event}, p.Payload()); err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) 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{ return g.opts.Broker.Publish(p.Topic(), &broker.Message{
Header: md, Header: md,
Body: b.Bytes(), Body: b,
}) })
} }

View File

@ -31,37 +31,37 @@ var _ context.Context
var _ client.Option var _ client.Option
var _ server.Option var _ server.Option
// Client API for Micro service // Client API for Client service
type MicroService interface { type ClientService interface {
// Call allows a single request to be made // Call allows a single request to be made
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
// Stream is a bidirectional stream // Stream is a bidirectional stream
Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error) Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error)
// Publish publishes a message and returns an empty Message // Publish publishes a message and returns an empty Message
Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error)
} }
type microService struct { type clientService struct {
c client.Client c client.Client
name string name string
} }
func NewMicroService(name string, c client.Client) MicroService { func NewClientService(name string, c client.Client) ClientService {
if c == nil { if c == nil {
c = client.NewClient() c = client.NewClient()
} }
if len(name) == 0 { if len(name) == 0 {
name = "go.micro.client" name = "go.micro.client"
} }
return &microService{ return &clientService{
c: c, c: c,
name: name, name: name,
} }
} }
func (c *microService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { func (c *clientService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Micro.Call", in) req := c.c.NewRequest(c.name, "Client.Call", in)
out := new(Response) out := new(Response)
err := c.c.Call(ctx, req, out, opts...) err := c.c.Call(ctx, req, out, opts...)
if err != nil { if err != nil {
@ -70,16 +70,16 @@ func (c *microService) Call(ctx context.Context, in *Request, opts ...client.Cal
return out, nil return out, nil
} }
func (c *microService) Stream(ctx context.Context, opts ...client.CallOption) (Micro_StreamService, error) { func (c *clientService) Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) {
req := c.c.NewRequest(c.name, "Micro.Stream", &Request{}) req := c.c.NewRequest(c.name, "Client.Stream", &Request{})
stream, err := c.c.Stream(ctx, req, opts...) stream, err := c.c.Stream(ctx, req, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &microServiceStream{stream}, nil return &clientServiceStream{stream}, nil
} }
type Micro_StreamService interface { type Client_StreamService interface {
SendMsg(interface{}) error SendMsg(interface{}) error
RecvMsg(interface{}) error RecvMsg(interface{}) error
Close() error Close() error
@ -87,27 +87,27 @@ type Micro_StreamService interface {
Recv() (*Response, error) Recv() (*Response, error)
} }
type microServiceStream struct { type clientServiceStream struct {
stream client.Stream stream client.Stream
} }
func (x *microServiceStream) Close() error { func (x *clientServiceStream) Close() error {
return x.stream.Close() return x.stream.Close()
} }
func (x *microServiceStream) SendMsg(m interface{}) error { func (x *clientServiceStream) SendMsg(m interface{}) error {
return x.stream.Send(m) return x.stream.Send(m)
} }
func (x *microServiceStream) RecvMsg(m interface{}) error { func (x *clientServiceStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m) return x.stream.Recv(m)
} }
func (x *microServiceStream) Send(m *Request) error { func (x *clientServiceStream) Send(m *Request) error {
return x.stream.Send(m) return x.stream.Send(m)
} }
func (x *microServiceStream) Recv() (*Response, error) { func (x *clientServiceStream) Recv() (*Response, error) {
m := new(Response) m := new(Response)
err := x.stream.Recv(m) err := x.stream.Recv(m)
if err != nil { if err != nil {
@ -116,8 +116,8 @@ func (x *microServiceStream) Recv() (*Response, error) {
return m, nil return m, nil
} }
func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) { func (c *clientService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
req := c.c.NewRequest(c.name, "Micro.Publish", in) req := c.c.NewRequest(c.name, "Client.Publish", in)
out := new(Message) out := new(Message)
err := c.c.Call(ctx, req, out, opts...) err := c.c.Call(ctx, req, out, opts...)
if err != nil { if err != nil {
@ -126,43 +126,43 @@ func (c *microService) Publish(ctx context.Context, in *Message, opts ...client.
return out, nil return out, nil
} }
// Server API for Micro service // Server API for Client service
type MicroHandler interface { type ClientHandler interface {
// Call allows a single request to be made // Call allows a single request to be made
Call(context.Context, *Request, *Response) error Call(context.Context, *Request, *Response) error
// Stream is a bidirectional stream // Stream is a bidirectional stream
Stream(context.Context, Micro_StreamStream) error Stream(context.Context, Client_StreamStream) error
// Publish publishes a message and returns an empty Message // Publish publishes a message and returns an empty Message
Publish(context.Context, *Message, *Message) error Publish(context.Context, *Message, *Message) error
} }
func RegisterMicroHandler(s server.Server, hdlr MicroHandler, opts ...server.HandlerOption) error { func RegisterClientHandler(s server.Server, hdlr ClientHandler, opts ...server.HandlerOption) error {
type micro interface { type client interface {
Call(ctx context.Context, in *Request, out *Response) error Call(ctx context.Context, in *Request, out *Response) error
Stream(ctx context.Context, stream server.Stream) error Stream(ctx context.Context, stream server.Stream) error
Publish(ctx context.Context, in *Message, out *Message) error Publish(ctx context.Context, in *Message, out *Message) error
} }
type Micro struct { type Client struct {
micro client
} }
h := &microHandler{hdlr} h := &clientHandler{hdlr}
return s.Handle(s.NewHandler(&Micro{h}, opts...)) return s.Handle(s.NewHandler(&Client{h}, opts...))
} }
type microHandler struct { type clientHandler struct {
MicroHandler ClientHandler
} }
func (h *microHandler) Call(ctx context.Context, in *Request, out *Response) error { func (h *clientHandler) Call(ctx context.Context, in *Request, out *Response) error {
return h.MicroHandler.Call(ctx, in, out) return h.ClientHandler.Call(ctx, in, out)
} }
func (h *microHandler) Stream(ctx context.Context, stream server.Stream) error { func (h *clientHandler) Stream(ctx context.Context, stream server.Stream) error {
return h.MicroHandler.Stream(ctx, &microStreamStream{stream}) return h.ClientHandler.Stream(ctx, &clientStreamStream{stream})
} }
type Micro_StreamStream interface { type Client_StreamStream interface {
SendMsg(interface{}) error SendMsg(interface{}) error
RecvMsg(interface{}) error RecvMsg(interface{}) error
Close() error Close() error
@ -170,27 +170,27 @@ type Micro_StreamStream interface {
Recv() (*Request, error) Recv() (*Request, error)
} }
type microStreamStream struct { type clientStreamStream struct {
stream server.Stream stream server.Stream
} }
func (x *microStreamStream) Close() error { func (x *clientStreamStream) Close() error {
return x.stream.Close() return x.stream.Close()
} }
func (x *microStreamStream) SendMsg(m interface{}) error { func (x *clientStreamStream) SendMsg(m interface{}) error {
return x.stream.Send(m) return x.stream.Send(m)
} }
func (x *microStreamStream) RecvMsg(m interface{}) error { func (x *clientStreamStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m) return x.stream.Recv(m)
} }
func (x *microStreamStream) Send(m *Response) error { func (x *clientStreamStream) Send(m *Response) error {
return x.stream.Send(m) return x.stream.Send(m)
} }
func (x *microStreamStream) Recv() (*Request, error) { func (x *clientStreamStream) Recv() (*Request, error) {
m := new(Request) m := new(Request)
if err := x.stream.Recv(m); err != nil { if err := x.stream.Recv(m); err != nil {
return nil, err return nil, err
@ -198,6 +198,6 @@ func (x *microStreamStream) Recv() (*Request, error) {
return m, nil return m, nil
} }
func (h *microHandler) Publish(ctx context.Context, in *Message, out *Message) error { func (h *clientHandler) Publish(ctx context.Context, in *Message, out *Message) error {
return h.MicroHandler.Publish(ctx, in, out) return h.ClientHandler.Publish(ctx, in, out)
} }

View File

@ -191,23 +191,23 @@ func init() {
var fileDescriptor_7d733ae29171347b = []byte{ var fileDescriptor_7d733ae29171347b = []byte{
// 270 bytes of a gzipped FileDescriptorProto // 270 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x3f, 0x4f, 0xc3, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0x41, 0x4b, 0xc3, 0x40,
0x10, 0xc5, 0xeb, 0xfe, 0x4b, 0x39, 0x2a, 0x21, 0x9d, 0x18, 0x4c, 0x06, 0x54, 0x32, 0x65, 0xc1, 0x10, 0x85, 0xbb, 0x6d, 0x4c, 0xea, 0x58, 0x10, 0x06, 0x0f, 0x6b, 0x0e, 0x52, 0x73, 0xca, 0xc5,
0x45, 0x30, 0x23, 0x86, 0xce, 0x95, 0x50, 0x40, 0xac, 0x28, 0x71, 0x4f, 0xc1, 0x52, 0x6a, 0x9b, 0x54, 0xf4, 0x2c, 0x1e, 0x72, 0x16, 0x24, 0x8a, 0x57, 0x49, 0xb6, 0x43, 0x5c, 0x48, 0x77, 0xd7,
0xd8, 0xad, 0x94, 0xef, 0xc8, 0x87, 0x42, 0x38, 0x29, 0x45, 0xd0, 0x2e, 0x6c, 0xf7, 0xee, 0x67, 0xec, 0xb6, 0x90, 0x1f, 0xe9, 0x7f, 0x12, 0x36, 0xa9, 0x15, 0x6d, 0x2f, 0xbd, 0xcd, 0x9b, 0x6f,
0xbd, 0x3b, 0xbf, 0x83, 0x74, 0xad, 0x64, 0x6d, 0xe6, 0xa5, 0xb9, 0x6e, 0x0b, 0x59, 0x29, 0xd2, 0x79, 0x33, 0xfb, 0x06, 0xd2, 0x95, 0x14, 0xad, 0x5e, 0xd4, 0xfa, 0xa6, 0x2f, 0x44, 0x23, 0x49,
0x7e, 0x6e, 0x6b, 0xe3, 0x77, 0x42, 0x04, 0x81, 0x67, 0xa5, 0x11, 0xe1, 0x8d, 0x68, 0xdb, 0xc9, 0xb9, 0x85, 0x69, 0xb5, 0xdb, 0x8a, 0xcc, 0x0b, 0x3c, 0xaf, 0x75, 0xe6, 0xdf, 0x64, 0x7d, 0x3b,
0x16, 0xa2, 0x8c, 0xde, 0x37, 0xe4, 0x3c, 0x72, 0x88, 0x1c, 0xd5, 0x5b, 0x25, 0x89, 0xb3, 0x19, 0xd9, 0x40, 0x54, 0xd0, 0xe7, 0x9a, 0xac, 0x43, 0x0e, 0x91, 0xa5, 0x76, 0x23, 0x05, 0x71, 0x36,
0x4b, 0x4f, 0xb2, 0x9d, 0xc4, 0x18, 0x26, 0xa4, 0x57, 0xd6, 0x28, 0xed, 0x79, 0x3f, 0xa0, 0x6f, 0x67, 0xe9, 0x69, 0xb1, 0x95, 0x18, 0xc3, 0x94, 0xd4, 0xd2, 0x68, 0xa9, 0x1c, 0x1f, 0x7b, 0xf4,
0x8d, 0x57, 0x30, 0x95, 0x46, 0x7b, 0xd2, 0xfe, 0xd5, 0x37, 0x96, 0xf8, 0x20, 0xf0, 0xd3, 0xae, 0xa3, 0xf1, 0x1a, 0x66, 0x42, 0x2b, 0x47, 0xca, 0xbd, 0xbb, 0xce, 0x10, 0x9f, 0x78, 0x7e, 0x36,
0xf7, 0xdc, 0x58, 0x42, 0x84, 0x61, 0x61, 0x56, 0x0d, 0x1f, 0xce, 0x58, 0x3a, 0xcd, 0x42, 0x9d, 0xf4, 0x5e, 0x3b, 0x43, 0x88, 0x10, 0x54, 0x7a, 0xd9, 0xf1, 0x60, 0xce, 0xd2, 0x59, 0xe1, 0xeb,
0x5c, 0xc2, 0x24, 0x23, 0x67, 0x8d, 0x76, 0x7b, 0xce, 0x7e, 0xf0, 0x17, 0x88, 0x96, 0xe4, 0x5c, 0xe4, 0x0a, 0xa6, 0x05, 0x59, 0xa3, 0x95, 0xdd, 0x71, 0xf6, 0x8b, 0xbf, 0x41, 0xf4, 0x44, 0xd6,
0x5e, 0x12, 0x9e, 0xc3, 0xc8, 0x1b, 0xab, 0x64, 0xb7, 0x55, 0x2b, 0xfe, 0xcc, 0xed, 0x1f, 0x9f, 0x96, 0x35, 0xe1, 0x05, 0x9c, 0x38, 0x6d, 0xa4, 0x18, 0xb6, 0xea, 0xc5, 0xbf, 0xb9, 0xe3, 0xc3,
0x3b, 0xd8, 0xfb, 0xde, 0x7e, 0x30, 0x18, 0x2d, 0xbf, 0x02, 0xc0, 0x7b, 0x18, 0x2e, 0xf2, 0xaa, 0x73, 0x27, 0x3b, 0xdf, 0xbb, 0x2f, 0x06, 0x61, 0xee, 0xbf, 0x8e, 0x0f, 0x10, 0xe4, 0x65, 0xd3,
0x42, 0x2e, 0x7e, 0x65, 0x22, 0xba, 0x40, 0xe2, 0x8b, 0x03, 0xa4, 0x5d, 0x39, 0xe9, 0xe1, 0x02, 0x20, 0xcf, 0xfe, 0x84, 0x92, 0x0d, 0x89, 0xc4, 0x97, 0x7b, 0x48, 0xbf, 0x73, 0x32, 0xc2, 0x1c,
0xc6, 0x4f, 0xbe, 0xa6, 0x7c, 0xfd, 0x4f, 0x83, 0x94, 0xdd, 0x30, 0x7c, 0x80, 0xe8, 0x71, 0x53, 0xc2, 0x17, 0xd7, 0x52, 0xb9, 0x3a, 0xd2, 0x20, 0x65, 0xb7, 0x0c, 0x1f, 0x21, 0x7a, 0x5e, 0x57,
0x54, 0xca, 0xbd, 0x1d, 0x70, 0xe9, 0xfe, 0x1f, 0x1f, 0x25, 0x49, 0xaf, 0x18, 0x87, 0xb3, 0xde, 0x8d, 0xb4, 0x1f, 0x7b, 0x5c, 0x86, 0x00, 0xe2, 0x83, 0x24, 0x19, 0x55, 0xa1, 0xbf, 0xeb, 0xfd,
0x7d, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x63, 0x94, 0x1a, 0x02, 0x02, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x76, 0x1f, 0x51, 0x03, 0x02, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -218,59 +218,59 @@ var _ grpc.ClientConn
// is compatible with the grpc package it is being compiled against. // is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4 const _ = grpc.SupportPackageIsVersion4
// MicroClient is the client API for Micro service. // ClientClient is the client API for Client service.
// //
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type MicroClient interface { type ClientClient interface {
// Call allows a single request to be made // Call allows a single request to be made
Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
// Stream is a bidirectional stream // Stream is a bidirectional stream
Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error) Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error)
// Publish publishes a message and returns an empty Message // Publish publishes a message and returns an empty Message
Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
} }
type microClient struct { type clientClient struct {
cc *grpc.ClientConn cc *grpc.ClientConn
} }
func NewMicroClient(cc *grpc.ClientConn) MicroClient { func NewClientClient(cc *grpc.ClientConn) ClientClient {
return &microClient{cc} return &clientClient{cc}
} }
func (c *microClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { func (c *clientClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response) out := new(Response)
err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Call", in, out, opts...) err := c.cc.Invoke(ctx, "/go.micro.client.Client/Call", in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
func (c *microClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Micro_StreamClient, error) { func (c *clientClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) {
stream, err := c.cc.NewStream(ctx, &_Micro_serviceDesc.Streams[0], "/go.micro.client.Micro/Stream", opts...) stream, err := c.cc.NewStream(ctx, &_Client_serviceDesc.Streams[0], "/go.micro.client.Client/Stream", opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
x := &microStreamClient{stream} x := &clientStreamClient{stream}
return x, nil return x, nil
} }
type Micro_StreamClient interface { type Client_StreamClient interface {
Send(*Request) error Send(*Request) error
Recv() (*Response, error) Recv() (*Response, error)
grpc.ClientStream grpc.ClientStream
} }
type microStreamClient struct { type clientStreamClient struct {
grpc.ClientStream grpc.ClientStream
} }
func (x *microStreamClient) Send(m *Request) error { func (x *clientStreamClient) Send(m *Request) error {
return x.ClientStream.SendMsg(m) return x.ClientStream.SendMsg(m)
} }
func (x *microStreamClient) Recv() (*Response, error) { func (x *clientStreamClient) Recv() (*Response, error) {
m := new(Response) m := new(Response)
if err := x.ClientStream.RecvMsg(m); err != nil { if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err return nil, err
@ -278,66 +278,66 @@ func (x *microStreamClient) Recv() (*Response, error) {
return m, nil return m, nil
} }
func (c *microClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) { func (c *clientClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
out := new(Message) out := new(Message)
err := c.cc.Invoke(ctx, "/go.micro.client.Micro/Publish", in, out, opts...) err := c.cc.Invoke(ctx, "/go.micro.client.Client/Publish", in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
// MicroServer is the server API for Micro service. // ClientServer is the server API for Client service.
type MicroServer interface { type ClientServer interface {
// Call allows a single request to be made // Call allows a single request to be made
Call(context.Context, *Request) (*Response, error) Call(context.Context, *Request) (*Response, error)
// Stream is a bidirectional stream // Stream is a bidirectional stream
Stream(Micro_StreamServer) error Stream(Client_StreamServer) error
// Publish publishes a message and returns an empty Message // Publish publishes a message and returns an empty Message
Publish(context.Context, *Message) (*Message, error) Publish(context.Context, *Message) (*Message, error)
} }
func RegisterMicroServer(s *grpc.Server, srv MicroServer) { func RegisterClientServer(s *grpc.Server, srv ClientServer) {
s.RegisterService(&_Micro_serviceDesc, srv) s.RegisterService(&_Client_serviceDesc, srv)
} }
func _Micro_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _Client_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request) in := new(Request)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(MicroServer).Call(ctx, in) return srv.(ClientServer).Call(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/go.micro.client.Micro/Call", FullMethod: "/go.micro.client.Client/Call",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Call(ctx, req.(*Request)) return srv.(ClientServer).Call(ctx, req.(*Request))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Micro_Stream_Handler(srv interface{}, stream grpc.ServerStream) error { func _Client_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(MicroServer).Stream(&microStreamServer{stream}) return srv.(ClientServer).Stream(&clientStreamServer{stream})
} }
type Micro_StreamServer interface { type Client_StreamServer interface {
Send(*Response) error Send(*Response) error
Recv() (*Request, error) Recv() (*Request, error)
grpc.ServerStream grpc.ServerStream
} }
type microStreamServer struct { type clientStreamServer struct {
grpc.ServerStream grpc.ServerStream
} }
func (x *microStreamServer) Send(m *Response) error { func (x *clientStreamServer) Send(m *Response) error {
return x.ServerStream.SendMsg(m) return x.ServerStream.SendMsg(m)
} }
func (x *microStreamServer) Recv() (*Request, error) { func (x *clientStreamServer) Recv() (*Request, error) {
m := new(Request) m := new(Request)
if err := x.ServerStream.RecvMsg(m); err != nil { if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err return nil, err
@ -345,41 +345,41 @@ func (x *microStreamServer) Recv() (*Request, error) {
return m, nil return m, nil
} }
func _Micro_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _Client_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Message) in := new(Message)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(MicroServer).Publish(ctx, in) return srv.(ClientServer).Publish(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/go.micro.client.Micro/Publish", FullMethod: "/go.micro.client.Client/Publish",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MicroServer).Publish(ctx, req.(*Message)) return srv.(ClientServer).Publish(ctx, req.(*Message))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
var _Micro_serviceDesc = grpc.ServiceDesc{ var _Client_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.client.Micro", ServiceName: "go.micro.client.Client",
HandlerType: (*MicroServer)(nil), HandlerType: (*ClientServer)(nil),
Methods: []grpc.MethodDesc{ Methods: []grpc.MethodDesc{
{ {
MethodName: "Call", MethodName: "Call",
Handler: _Micro_Call_Handler, Handler: _Client_Call_Handler,
}, },
{ {
MethodName: "Publish", MethodName: "Publish",
Handler: _Micro_Publish_Handler, Handler: _Client_Publish_Handler,
}, },
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {
StreamName: "Stream", StreamName: "Stream",
Handler: _Micro_Stream_Handler, Handler: _Client_Stream_Handler,
ServerStreams: true, ServerStreams: true,
ClientStreams: true, ClientStreams: true,
}, },

View File

@ -2,8 +2,8 @@ syntax = "proto3";
package go.micro.client; package go.micro.client;
// Micro is the micro client interface // Client is the micro client interface
service Micro { service Client {
// Call allows a single request to be made // Call allows a single request to be made
rpc Call(Request) returns (Response) {}; rpc Call(Request) returns (Response) {};
// Stream is a bidirectional stream // Stream is a bidirectional stream

View File

@ -96,19 +96,22 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
} }
} }
var grr error dOpts := []transport.DialOption{
c, err := r.pool.Get(address, transport.WithTimeout(opts.DialTimeout)) transport.WithStream(),
}
if opts.DialTimeout >= 0 {
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
}
c, err := r.pool.Get(address, dOpts...)
if err != nil { if err != nil {
return errors.InternalServerError("go.micro.client", "connection error: %v", err) 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) seq := atomic.LoadUint64(&r.seq)
atomic.AddUint64(&r.seq, 1) atomic.AddUint64(&r.seq, 1)
codec := newRpcCodec(msg, c, cf) codec := newRpcCodec(msg, c, cf, "")
rsp := &rpcResponse{ rsp := &rpcResponse{
socket: c, socket: c,
@ -116,15 +119,19 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
} }
stream := &rpcStream{ stream := &rpcStream{
id: fmt.Sprintf("%v", seq),
context: ctx, context: ctx,
request: req, request: req,
response: rsp, response: rsp,
codec: codec, codec: codec,
closed: make(chan bool), 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() defer stream.Close()
// wait for error response
ch := make(chan error, 1) ch := make(chan error, 1)
go func() { go func() {
@ -150,14 +157,26 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
ch <- nil ch <- nil
}() }()
var grr error
select { select {
case err := <-ch: case err := <-ch:
grr = err grr = err
return err return err
case <-ctx.Done(): case <-ctx.Done():
grr = ctx.Err() grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
return 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) { func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) {
@ -206,7 +225,13 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err) 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{ rsp := &rpcResponse{
socket: c, socket: c,
@ -219,16 +244,24 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
} }
stream := &rpcStream{ stream := &rpcStream{
id: id,
context: ctx, context: ctx,
request: req, request: req,
response: rsp, response: rsp,
closed: make(chan bool),
codec: codec, codec: codec,
// used to close the stream
closed: make(chan bool),
// signal the end of stream,
sendEOS: true,
// release func
release: func(err error) { c.Close() },
} }
// wait for error response
ch := make(chan error, 1) ch := make(chan error, 1)
go func() { go func() {
// send the first message
ch <- stream.Send(req.Body()) ch <- stream.Send(req.Body())
}() }()
@ -242,6 +275,12 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request
} }
if grr != nil { if grr != nil {
// set the error
stream.Lock()
stream.err = grr
stream.Unlock()
// close the stream
stream.Close() stream.Close()
return nil, grr return nil, grr
} }
@ -312,10 +351,11 @@ func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, erro
// get next nodes from the selector // get next nodes from the selector
next, err := r.opts.Selector.Select(service, opts.SelectOptions...) next, err := r.opts.Selector.Select(service, opts.SelectOptions...)
if err != nil && err == selector.ErrNotFound { if err != nil {
return nil, errors.NotFound("go.micro.client", "service %s: %v", service, err.Error()) if err == selector.ErrNotFound {
} else if err != nil { return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %v", service, err.Error()) }
return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
} }
return next, nil return next, nil
@ -375,15 +415,17 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
// select next node // select next node
node, err := next() node, err := next()
if err != nil && err == selector.ErrNotFound { service := request.Service()
return errors.NotFound("go.micro.client", "service %s: %v", request.Service(), err.Error()) if err != nil {
} else if err != nil { if err == selector.ErrNotFound {
return errors.InternalServerError("go.micro.client", "error getting next %s node: %v", request.Service(), err.Error()) 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 // make the call
err = rcall(ctx, node, request, response, callOpts) err = rcall(ctx, node, request, response, callOpts)
r.opts.Selector.Mark(request.Service(), node, err) r.opts.Selector.Mark(service, node, err)
return err return err
} }
@ -452,14 +494,16 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
} }
node, err := next() node, err := next()
if err != nil && err == selector.ErrNotFound { service := request.Service()
return nil, errors.NotFound("go.micro.client", "service %s: %v", request.Service(), err.Error()) if err != nil {
} else if err != nil { if err == selector.ErrNotFound {
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %v", request.Service(), err.Error()) 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) 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 return stream, err
} }

View File

@ -39,6 +39,9 @@ type rpcCodec struct {
req *transport.Message req *transport.Message
buf *readWriteCloser buf *readWriteCloser
// signify if its a stream
stream string
} }
type readWriteCloser struct { 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) { set := func(hdr, v string) {
if len(v) == 0 { if len(v) == 0 {
return return
@ -126,6 +129,11 @@ func setHeaders(m *codec.Message) {
set("Micro-Service", m.Target) set("Micro-Service", m.Target)
set("Micro-Method", m.Method) set("Micro-Method", m.Method)
set("Micro-Endpoint", m.Endpoint) set("Micro-Endpoint", m.Endpoint)
set("Micro-Error", m.Error)
if len(stream) > 0 {
set("Micro-Stream", stream)
}
} }
// setupProtocol sets up the old protocol // 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"]] 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{ rwc := &readWriteCloser{
wbuf: bytes.NewBuffer(nil), wbuf: bytes.NewBuffer(nil),
rbuf: bytes.NewBuffer(nil), rbuf: bytes.NewBuffer(nil),
@ -159,6 +167,7 @@ func newRpcCodec(req *transport.Message, client transport.Client, c codec.NewCod
client: client, client: client,
codec: c(rwc), codec: c(rwc),
req: req, req: req,
stream: stream,
} }
return r return r
} }
@ -177,7 +186,7 @@ func (c *rpcCodec) Write(m *codec.Message, body interface{}) error {
} }
// set the mucp headers // set the mucp headers
setHeaders(m) setHeaders(m, c.stream)
// if body is bytes Frame don't encode // if body is bytes Frame don't encode
if body != nil { 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 { func (c *rpcCodec) ReadBody(b interface{}) error {
// read body // 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 { if err := c.codec.ReadBody(b); err != nil {
return errors.InternalServerError("go.micro.client.codec", err.Error()) return errors.InternalServerError("go.micro.client.codec", err.Error())
} }

View File

@ -18,6 +18,12 @@ type rpcStream struct {
response Response response Response
codec codec.Codec codec codec.Codec
context context.Context 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 { func (r *rpcStream) isClosed() bool {
@ -120,6 +126,26 @@ func (r *rpcStream) Close() error {
return nil return nil
default: default:
close(r.closed) 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
} }
} }

View File

@ -51,6 +51,9 @@ func (c *registrySelector) Select(service string, opts ...SelectOption) (Next, e
// if that fails go directly to the registry // if that fails go directly to the registry
services, err := c.rc.GetService(service) services, err := c.rc.GetService(service)
if err != nil { if err != nil {
if err == registry.ErrNotFound {
return nil, ErrNotFound
}
return nil, err return nil, err
} }

View File

@ -89,9 +89,22 @@ func (c *Codec) Write(m *codec.Message, b interface{}) error {
m.Header[":authority"] = m.Target m.Header[":authority"] = m.Target
m.Header["content-type"] = c.ContentType m.Header["content-type"] = c.ContentType
case codec.Response: case codec.Response:
m.Header["Trailer"] = "grpc-status, grpc-message" m.Header["Trailer"] = "grpc-status" //, grpc-message"
m.Header["content-type"] = c.ContentType
m.Header[":status"] = "200"
m.Header["grpc-status"] = "0" m.Header["grpc-status"] = "0"
m.Header["grpc-message"] = "" // m.Header["grpc-message"] = ""
case codec.Error:
m.Header["Trailer"] = "grpc-status, grpc-message"
// micro end of stream
if m.Error == "EOS" {
m.Header["grpc-status"] = "0"
} else {
m.Header["grpc-message"] = m.Error
m.Header["grpc-status"] = "13"
}
return nil
} }
// marshal content // marshal content

View File

@ -30,6 +30,7 @@ import (
"github.com/micro/go-micro/registry/gossip" "github.com/micro/go-micro/registry/gossip"
"github.com/micro/go-micro/registry/mdns" "github.com/micro/go-micro/registry/mdns"
rmem "github.com/micro/go-micro/registry/memory" rmem "github.com/micro/go-micro/registry/memory"
regSrv "github.com/micro/go-micro/registry/service"
// selectors // selectors
"github.com/micro/go-micro/client/selector" "github.com/micro/go-micro/client/selector"
@ -95,11 +96,13 @@ var (
cli.IntFlag{ cli.IntFlag{
Name: "register_ttl", Name: "register_ttl",
EnvVar: "MICRO_REGISTER_TTL", EnvVar: "MICRO_REGISTER_TTL",
Value: 60,
Usage: "Register TTL in seconds", Usage: "Register TTL in seconds",
}, },
cli.IntFlag{ cli.IntFlag{
Name: "register_interval", Name: "register_interval",
EnvVar: "MICRO_REGISTER_INTERVAL", EnvVar: "MICRO_REGISTER_INTERVAL",
Value: 30,
Usage: "Register interval in seconds", Usage: "Register interval in seconds",
}, },
cli.StringFlag{ cli.StringFlag{
@ -188,6 +191,8 @@ var (
} }
DefaultRegistries = map[string]func(...registry.Option) registry.Registry{ DefaultRegistries = map[string]func(...registry.Option) registry.Registry{
"go.micro.registry": regSrv.NewRegistry,
"service": regSrv.NewRegistry,
"consul": consul.NewRegistry, "consul": consul.NewRegistry,
"gossip": gossip.NewRegistry, "gossip": gossip.NewRegistry,
"mdns": mdns.NewRegistry, "mdns": mdns.NewRegistry,
@ -417,11 +422,11 @@ func (c *cmd) Before(ctx *cli.Context) error {
serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise"))) serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise")))
} }
if ttl := time.Duration(ctx.GlobalInt("register_ttl")); ttl > 0 { if ttl := time.Duration(ctx.GlobalInt("register_ttl")); ttl >= 0 {
serverOpts = append(serverOpts, server.RegisterTTL(ttl*time.Second)) serverOpts = append(serverOpts, server.RegisterTTL(ttl*time.Second))
} }
if val := time.Duration(ctx.GlobalInt("register_interval")); val > 0 { if val := time.Duration(ctx.GlobalInt("register_interval")); val >= 0 {
serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second)) serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second))
} }

View File

@ -12,6 +12,10 @@ import (
"github.com/micro/go-micro/config/source/file" "github.com/micro/go-micro/config/source/file"
) )
var (
sep = string(os.PathSeparator)
)
func createFileForIssue18(t *testing.T, content string) *os.File { func createFileForIssue18(t *testing.T, content string) *os.File {
data := []byte(content) data := []byte(content)
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano())) path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))

View File

@ -44,6 +44,6 @@ Load the source into config
// Create new config // Create new config
conf := config.NewConfig() conf := config.NewConfig()
// Load file source // Load consul source
conf.Load(consulSource) conf.Load(consulSource)
``` ```

View File

@ -1,7 +1,6 @@
package consul package consul
import ( import (
"errors"
"time" "time"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
@ -80,7 +79,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
case cs := <-w.ch: case cs := <-w.ch:
return cs, nil return cs, nil
case <-w.exit: case <-w.exit:
return nil, errors.New("watcher stopped") return nil, source.ErrWatcherStopped
} }
} }

View File

@ -91,6 +91,6 @@ Load the source into config
// Create new config // Create new config
conf := config.NewConfig() conf := config.NewConfig()
// Load file source // Load env source
conf.Load(src) conf.Load(src)
``` ```

View File

@ -86,8 +86,8 @@ func TestEnvvar_Prefixes(t *testing.T) {
} }
func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) { func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) {
source := NewSource(WithStrippedPrefix("GOMICRO_")) src := NewSource(WithStrippedPrefix("GOMICRO_"))
w, err := source.Watch() w, err := src.Watch()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -97,7 +97,7 @@ func TestEnvvar_WatchNextNoOpsUntilStop(t *testing.T) {
w.Stop() w.Stop()
}() }()
if _, err := w.Next(); err.Error() != "watcher stopped" { if _, err := w.Next(); err != source.ErrWatcherStopped {
t.Errorf("expected watcher stopped error, got %v", err) t.Errorf("expected watcher stopped error, got %v", err)
} }
} }

View File

@ -1,8 +1,6 @@
package env package env
import ( import (
"errors"
"github.com/micro/go-micro/config/source" "github.com/micro/go-micro/config/source"
) )
@ -13,7 +11,7 @@ type watcher struct {
func (w *watcher) Next() (*source.ChangeSet, error) { func (w *watcher) Next() (*source.ChangeSet, error) {
<-w.exit <-w.exit
return nil, errors.New("watcher stopped") return nil, source.ErrWatcherStopped
} }
func (w *watcher) Stop() error { func (w *watcher) Stop() error {

View File

@ -1,7 +1,8 @@
//+build !linux
package file package file
import ( import (
"errors"
"os" "os"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
@ -34,7 +35,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
// is it closed? // is it closed?
select { select {
case <-w.exit: case <-w.exit:
return nil, errors.New("watcher stopped") return nil, source.ErrWatcherStopped
default: default:
} }
@ -57,7 +58,7 @@ func (w *watcher) Next() (*source.ChangeSet, error) {
case err := <-w.fw.Errors: case err := <-w.fw.Errors:
return nil, err return nil, err
case <-w.exit: case <-w.exit:
return nil, errors.New("watcher stopped") return nil, source.ErrWatcherStopped
} }
} }

View File

@ -0,0 +1,71 @@
//+build linux
package file
import (
"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, source.ErrWatcherStopped
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, source.ErrWatcherStopped
}
}
func (w *watcher) Stop() error {
return w.fw.Close()
}

View File

@ -42,6 +42,6 @@ Load the source into config
// Create new config // Create new config
conf := config.NewConfig() conf := config.NewConfig()
// Load file source // Load flag source
conf.Load(flagSource) conf.Load(flagSource)
``` ```

View File

@ -12,13 +12,14 @@ var (
dbpw = flag.String("database-password", "", "db pw") dbpw = flag.String("database-password", "", "db pw")
) )
func init() { func initTestFlags() {
flag.Set("database-host", "localhost") flag.Set("database-host", "localhost")
flag.Set("database-password", "some-password") flag.Set("database-password", "some-password")
flag.Parse() flag.Parse()
} }
func TestFlagsrc_Read(t *testing.T) { func TestFlagsrc_Read(t *testing.T) {
initTestFlags()
source := NewSource() source := NewSource()
c, err := source.Read() c, err := source.Read()
if err != nil { if err != nil {
@ -46,6 +47,7 @@ func TestFlagsrc_Read(t *testing.T) {
} }
func TestFlagsrc_ReadAll(t *testing.T) { func TestFlagsrc_ReadAll(t *testing.T) {
initTestFlags()
source := NewSource(IncludeUnset(true)) source := NewSource(IncludeUnset(true))
c, err := source.Read() c, err := source.Read()
if err != nil { if err != nil {

View File

@ -39,6 +39,6 @@ Load the source into config
// Create new config // Create new config
conf := config.NewConfig() conf := config.NewConfig()
// Load file source // Load memory source
conf.Load(memorySource) conf.Load(memorySource)
``` ```

View File

@ -2,9 +2,15 @@
package source package source
import ( import (
"errors"
"time" "time"
) )
var (
// ErrWatcherStopped is returned when source watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// Source is the source from which config is loaded // Source is the source from which config is loaded
type Source interface { type Source interface {
Read() (*ChangeSet, error) Read() (*ChangeSet, error)

12
go.mod
View File

@ -3,7 +3,7 @@ module github.com/micro/go-micro
go 1.12 go 1.12
require ( require (
cloud.google.com/go v0.43.0 // indirect cloud.google.com/go v0.44.0 // indirect
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/Microsoft/go-winio v0.4.14 // indirect github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
@ -22,6 +22,7 @@ require (
github.com/go-playground/locales v0.12.1 // indirect github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/golang/protobuf v1.3.2 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/pprof v0.0.0-20190723021845-34ac40c74b70 // indirect
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.4.2 github.com/gorilla/handlers v1.4.2
@ -48,12 +49,10 @@ require (
github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/micro/cli v0.2.0 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/miekg/dns v1.1.15 // indirect
github.com/mitchellh/gox v1.0.1 // indirect github.com/mitchellh/gox v1.0.1 // indirect
github.com/mitchellh/hashstructure v1.0.0 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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/nats-io/nats.go v1.8.1 github.com/nats-io/nats.go v1.8.1
github.com/nats-io/nkeys v0.1.0 // indirect github.com/nats-io/nkeys v0.1.0 // indirect
@ -69,9 +68,8 @@ require (
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/mobile v0.0.0-20190806162312-597adff16ade // indirect golang.org/x/mobile v0.0.0-20190806162312-597adff16ade // indirect
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa // indirect golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e // indirect
golang.org/x/tools v0.0.0-20190807201305-8be58fba6352 // indirect golang.org/x/tools v0.0.0-20190809145639-6d4652c779c4 // indirect
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 // indirect
google.golang.org/grpc v1.22.1 google.golang.org/grpc v1.22.1
gopkg.in/go-playground/validator.v9 v9.29.1 gopkg.in/go-playground/validator.v9 v9.29.1
gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/src-d/go-git.v4 v4.13.1

8
go.sum
View File

@ -4,6 +4,7 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro= 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.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.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/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 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -109,6 +110,7 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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.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.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/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/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-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -246,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.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 h1:/+/n2PSiJURrXsBIGtfCz0hex/XYKqVsn51GAGdFrOE=
github.com/micro/mdns v0.2.0/go.mod h1:KJ0dW7KmicXU2BV++qkLlmHYcVv7/hHnbtguSWt9Aoc= 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.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 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@ -449,6 +453,8 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/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 h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -477,10 +483,12 @@ golang.org/x/tools v0.0.0-20190710184609-286818132824/go.mod h1:jcCCGcm9btYwXyDq
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/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-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-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= 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.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= 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.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.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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

View File

@ -24,6 +24,22 @@ type monitor struct {
services map[string]*Status services map[string]*Status
} }
func (m *monitor) Check(service string) error {
status, err := m.check(service)
if err != nil {
return err
}
m.Lock()
m.services[service] = status
m.Unlock()
if status.Code != StatusRunning {
return errors.New(status.Info)
}
return nil
}
// check provides binary running/failed status. // check provides binary running/failed status.
// In the event Debug.Health cannot be called on a service we reap the node. // In the event Debug.Health cannot be called on a service we reap the node.
func (m *monitor) check(service string) (*Status, error) { func (m *monitor) check(service string) (*Status, error) {
@ -41,6 +57,17 @@ func (m *monitor) check(service string) (*Status, error) {
// iterate through multiple versions of a service // iterate through multiple versions of a service
for _, service := range services { for _, service := range services {
for _, node := range service.Nodes { 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
}
// check the transport matches
if node.Metadata["transport"] != m.client.Options().Transport.String() {
continue
}
rsp, err := debug.Health( rsp, err := debug.Health(
context.Background(), context.Background(),
// empty health request // empty health request

View File

@ -17,6 +17,8 @@ type StatusCode int
type Monitor interface { type Monitor interface {
// Reap a service and stop monitoring // Reap a service and stop monitoring
Reap(service string) error Reap(service string) error
// Check the status of the service now
Check(service string) error
// Status of the service // Status of the service
Status(service string) (Status, error) Status(service string) (Status, error)
// Watch starts watching the service // Watch starts watching the service

892
network/default.go Normal file
View File

@ -0,0 +1,892 @@
package network
import (
"errors"
"fmt"
"hash/fnv"
"sync"
"time"
"github.com/golang/protobuf/proto"
"github.com/micro/go-micro/client"
rtr "github.com/micro/go-micro/client/selector/router"
pbNet "github.com/micro/go-micro/network/proto"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/router"
pbRtr "github.com/micro/go-micro/router/proto"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/tunnel"
tun "github.com/micro/go-micro/tunnel/transport"
"github.com/micro/go-micro/util/log"
)
var (
// NetworkChannel is the name of the tunnel channel for passing network messages
NetworkChannel = "network"
// ControlChannel is the name of the tunnel channel for passing control message
ControlChannel = "control"
// DefaultLink is default network link
DefaultLink = "network"
)
var (
// ErrClientNotFound is returned when client for tunnel channel could not be found
ErrClientNotFound = errors.New("client not found")
)
// network implements Network interface
type network struct {
// node is network node
*node
// options configure the network
options Options
// rtr is network router
router router.Router
// prx is network proxy
proxy proxy.Proxy
// tun is network tunnel
tunnel tunnel.Tunnel
// server is network server
server server.Server
// client is network client
client client.Client
// tunClient is a map of tunnel clients keyed over tunnel channel names
tunClient map[string]transport.Client
sync.RWMutex
// connected marks the network as connected
connected bool
// closed closes the network
closed chan bool
}
// newNetwork returns a new network node
func newNetwork(opts ...Option) Network {
options := DefaultOptions()
for _, o := range opts {
o(&options)
}
// init tunnel address to the network bind address
options.Tunnel.Init(
tunnel.Address(options.Address),
tunnel.Nodes(options.Peers...),
)
// init router Id to the network id
options.Router.Init(
router.Id(options.Id),
)
// create tunnel client with tunnel transport
tunTransport := tun.NewTransport(
tun.WithTunnel(options.Tunnel),
)
// set the address to advertise
address := options.Address
if len(options.Advertise) > 0 {
address = options.Advertise
}
// server is network server
server := server.NewServer(
server.Id(options.Id),
server.Address(options.Id),
server.Advertise(address),
server.Name(options.Name),
server.Transport(tunTransport),
)
// client is network client
client := client.NewClient(
client.Transport(tunTransport),
client.Selector(
rtr.NewSelector(
rtr.WithRouter(options.Router),
),
),
)
network := &network{
node: &node{
id: options.Id,
address: address,
peers: make(map[string]*node),
},
options: options,
router: options.Router,
proxy: options.Proxy,
tunnel: options.Tunnel,
server: server,
client: client,
tunClient: make(map[string]transport.Client),
}
network.node.network = network
return network
}
// Options returns network options
func (n *network) Options() Options {
n.RLock()
defer n.RUnlock()
options := n.options
return options
}
// Name returns network name
func (n *network) Name() string {
return n.options.Name
}
// resolveNodes resolves network nodes to addresses
func (n *network) resolveNodes() ([]string, error) {
// resolve the network address to network nodes
records, err := n.options.Resolver.Resolve(n.options.Name)
if err != nil {
return nil, err
}
nodeMap := make(map[string]bool)
// collect network node addresses
var nodes []string
for _, record := range records {
nodes = append(nodes, record.Address)
nodeMap[record.Address] = true
}
// append seed nodes if we have them
for _, node := range n.options.Peers {
if _, ok := nodeMap[node]; !ok {
nodes = append(nodes, node)
}
}
return nodes, nil
}
// resolve continuously resolves network nodes and initializes network tunnel with resolved addresses
func (n *network) resolve() {
resolve := time.NewTicker(ResolveTime)
defer resolve.Stop()
for {
select {
case <-n.closed:
return
case <-resolve.C:
nodes, err := n.resolveNodes()
if err != nil {
log.Debugf("Network failed to resolve nodes: %v", err)
continue
}
// initialize the tunnel
n.tunnel.Init(
tunnel.Nodes(nodes...),
)
}
}
}
// handleNetConn handles network announcement messages
func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message) {
for {
m := new(transport.Message)
if err := sess.Recv(m); err != nil {
log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err)
return
}
select {
case msg <- m:
case <-n.closed:
return
}
}
}
// acceptNetConn accepts connections from NetworkChannel
func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message) {
for {
// accept a connection
conn, err := l.Accept()
if err != nil {
// TODO: handle this
log.Debugf("Network tunnel [%s] accept error: %v", NetworkChannel, err)
return
}
select {
case <-n.closed:
return
default:
// go handle NetworkChannel connection
go n.handleNetConn(conn, recv)
}
}
}
// processNetChan processes messages received on NetworkChannel
func (n *network) processNetChan(client transport.Client, listener tunnel.Listener) {
// receive network message queue
recv := make(chan *transport.Message, 128)
// accept NetworkChannel connections
go n.acceptNetConn(listener, recv)
for {
select {
case m := <-recv:
// switch on type of message and take action
switch m.Header["Micro-Method"] {
case "connect":
// mark the time the message has been received
now := time.Now()
pbNetConnect := &pbNet.Connect{}
if err := proto.Unmarshal(m.Body, pbNetConnect); err != nil {
log.Debugf("Network tunnel [%s] connect unmarshal error: %v", NetworkChannel, err)
continue
}
// don't process your own messages
if pbNetConnect.Node.Id == n.options.Id {
continue
}
log.Debugf("Network received connect message from: %s", pbNetConnect.Node.Id)
peer := &node{
id: pbNetConnect.Node.Id,
address: pbNetConnect.Node.Address,
peers: make(map[string]*node),
lastSeen: now,
}
if err := n.node.AddPeer(peer); err == ErrPeerExists {
log.Debugf("Network peer exists, refreshing: %s", peer.id)
// update lastSeen time for the existing node
if err := n.RefreshPeer(peer.id, now); err != nil {
log.Debugf("Network failed refreshing peer %s: %v", peer.id, err)
}
continue
}
// get node peers down to MaxDepth encoded in protobuf
msg := PeersToProto(n.node, MaxDepth)
// advertise yourself to the network
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to advertise peers: %v", err)
}
// advertise all the routes when a new node has connected
if err := n.router.Solicit(); err != nil {
log.Debugf("Network failed to solicit routes: %s", err)
}
case "peer":
// mark the time the message has been received
now := time.Now()
pbNetPeer := &pbNet.Peer{}
if err := proto.Unmarshal(m.Body, pbNetPeer); err != nil {
log.Debugf("Network tunnel [%s] peer unmarshal error: %v", NetworkChannel, err)
continue
}
// don't process your own messages
if pbNetPeer.Node.Id == n.options.Id {
continue
}
log.Debugf("Network received peer message from: %s", pbNetPeer.Node.Id)
peer := &node{
id: pbNetPeer.Node.Id,
address: pbNetPeer.Node.Address,
peers: make(map[string]*node),
lastSeen: now,
}
if err := n.node.AddPeer(peer); err == nil {
// send a solicit message when discovering new peer
msg := &pbRtr.Solicit{
Id: n.options.Id,
}
if err := n.sendMsg("solicit", msg, ControlChannel); err != nil {
log.Debugf("Network failed to send solicit message: %s", err)
}
continue
// we're expecting any error to be ErrPeerExists
} else if err != ErrPeerExists {
log.Debugf("Network got error adding peer %v", err)
continue
}
log.Debugf("Network peer exists, refreshing: %s", pbNetPeer.Node.Id)
// update lastSeen time for the peer
if err := n.RefreshPeer(pbNetPeer.Node.Id, now); err != nil {
log.Debugf("Network failed refreshing peer %s: %v", pbNetPeer.Node.Id, err)
}
// NOTE: we don't unpack MaxDepth toplogy
peer = UnpackPeerTopology(pbNetPeer, now, MaxDepth-1)
log.Debugf("Network updating topology of node: %s", n.node.id)
if err := n.node.UpdatePeer(peer); err != nil {
log.Debugf("Network failed to update peers: %v", err)
}
case "close":
pbNetClose := &pbNet.Close{}
if err := proto.Unmarshal(m.Body, pbNetClose); err != nil {
log.Debugf("Network tunnel [%s] close unmarshal error: %v", NetworkChannel, err)
continue
}
// don't process your own messages
if pbNetClose.Node.Id == n.options.Id {
continue
}
log.Debugf("Network received close message from: %s", pbNetClose.Node.Id)
peer := &node{
id: pbNetClose.Node.Id,
address: pbNetClose.Node.Address,
}
if err := n.DeletePeerNode(peer.id); err != nil {
log.Debugf("Network failed to delete node %s routes: %v", peer.id, err)
}
if err := n.prunePeerRoutes(peer); err != nil {
log.Debugf("Network failed pruning peer %s routes: %v", peer.id, err)
}
}
case <-n.closed:
return
}
}
}
// sendMsg sends a message to the tunnel channel
func (n *network) sendMsg(method string, msg proto.Message, channel string) error {
body, err := proto.Marshal(msg)
if err != nil {
return err
}
// create transport message and chuck it down the pipe
m := transport.Message{
Header: map[string]string{
"Micro-Method": method,
},
Body: body,
}
// check if the channel client is initialized
n.RLock()
client, ok := n.tunClient[channel]
if !ok || client == nil {
n.RUnlock()
return ErrClientNotFound
}
n.RUnlock()
log.Debugf("Network sending %s message from: %s", method, n.options.Id)
if err := client.Send(&m); err != nil {
return err
}
return nil
}
// announce announces node peers to the network
func (n *network) announce(client transport.Client) {
announce := time.NewTicker(AnnounceTime)
defer announce.Stop()
for {
select {
case <-n.closed:
return
case <-announce.C:
msg := PeersToProto(n.node, MaxDepth)
// advertise yourself to the network
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to advertise peers: %v", err)
continue
}
}
}
}
// pruneRoutes prunes routes return by given query
func (n *network) pruneRoutes(q router.Query) error {
routes, err := n.router.Table().Query(q)
if err != nil && err != router.ErrRouteNotFound {
return err
}
for _, route := range routes {
if err := n.router.Table().Delete(route); err != nil && err != router.ErrRouteNotFound {
return err
}
}
return nil
}
// pruneNodeRoutes prunes routes that were either originated by or routable via given node
func (n *network) prunePeerRoutes(peer *node) error {
// lookup all routes originated by router
q := router.NewQuery(
router.QueryRouter(peer.id),
)
if err := n.pruneRoutes(q); err != nil {
return err
}
// lookup all routes routable via gw
q = router.NewQuery(
router.QueryGateway(peer.id),
)
if err := n.pruneRoutes(q); err != nil {
return err
}
return nil
}
// prune deltes node peers that have not been seen for longer than PruneTime seconds
// prune also removes all the routes either originated by or routable by the stale nodes
func (n *network) prune() {
prune := time.NewTicker(PruneTime)
defer prune.Stop()
for {
select {
case <-n.closed:
return
case <-prune.C:
pruned := n.PruneStalePeerNodes(PruneTime)
for id, peer := range pruned {
log.Debugf("Network peer exceeded prune time: %s", id)
if err := n.prunePeerRoutes(peer); err != nil {
log.Debugf("Network failed pruning peer %s routes: %v", id, err)
}
}
}
}
}
// handleCtrlConn handles ControlChannel connections
func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Message) {
for {
m := new(transport.Message)
if err := sess.Recv(m); err != nil {
// TODO: should we bail here?
log.Debugf("Network tunnel advert receive error: %v", err)
return
}
select {
case msg <- m:
case <-n.closed:
return
}
}
}
// acceptCtrlConn accepts connections from ControlChannel
func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *transport.Message) {
for {
// accept a connection
conn, err := l.Accept()
if err != nil {
// TODO: handle this
log.Debugf("Network tunnel [%s] accept error: %v", ControlChannel, err)
return
}
select {
case <-n.closed:
return
default:
// go handle ControlChannel connection
go n.handleCtrlConn(conn, recv)
}
}
}
// setRouteMetric calculates metric of the route and updates it in place
// - Local route metric is 1
// - Routes with ID of adjacent nodes are 10
// - Routes by peers of the advertiser are 100
// - Routes beyond your neighbourhood are 1000
func (n *network) setRouteMetric(route *router.Route) {
// we are the origin of the route
if route.Router == n.options.Id {
route.Metric = 1
return
}
// check if the route origin is our peer
if _, ok := n.peers[route.Router]; ok {
route.Metric = 10
return
}
// check if the route origin is the peer of our peer
for _, peer := range n.peers {
for id := range peer.peers {
if route.Router == id {
route.Metric = 100
return
}
}
}
// the origin of the route is beyond our neighbourhood
route.Metric = 1000
}
// processCtrlChan processes messages received on ControlChannel
func (n *network) processCtrlChan(client transport.Client, listener tunnel.Listener) {
// receive control message queue
recv := make(chan *transport.Message, 128)
// accept ControlChannel cconnections
go n.acceptCtrlConn(listener, recv)
for {
select {
case m := <-recv:
// switch on type of message and take action
switch m.Header["Micro-Method"] {
case "advert":
pbRtrAdvert := &pbRtr.Advert{}
if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil {
log.Debugf("Network fail to unmarshal advert message: %v", err)
continue
}
// don't process your own messages
if pbRtrAdvert.Id == n.options.Id {
continue
}
log.Debugf("Network received advert message from: %s", pbRtrAdvert.Id)
// loookup advertising node in our peer topology
advertNode := n.node.GetPeerNode(pbRtrAdvert.Id)
if advertNode == nil {
// if we can't find the node in our topology (MaxDepth) we skipp prcessing adverts
log.Debugf("Network skipping advert message from unknown peer: %s", pbRtrAdvert.Id)
continue
}
var events []*router.Event
for _, event := range pbRtrAdvert.Events {
// we know the advertising node is not the origin of the route
if pbRtrAdvert.Id != event.Route.Router {
// if the origin router is not the advertising node peer
// we can't rule out potential routing loops so we bail here
if peer := advertNode.GetPeerNode(event.Route.Router); peer == nil {
log.Debugf("Network skipping advert message from peer: %s", pbRtrAdvert.Id)
continue
}
}
route := router.Route{
Service: event.Route.Service,
Address: event.Route.Address,
Gateway: event.Route.Gateway,
Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link,
Metric: int(event.Route.Metric),
}
// set the route metric
n.node.RLock()
n.setRouteMetric(&route)
n.node.RUnlock()
// throw away metric bigger than 1000
if route.Metric > 1000 {
log.Debugf("Network route metric %d dropping node: %s", route.Metric, route.Router)
continue
}
// create router event
e := &router.Event{
Type: router.EventType(event.Type),
Timestamp: time.Unix(0, pbRtrAdvert.Timestamp),
Route: route,
}
events = append(events, e)
}
// if no events are eligible for processing continue
if len(events) == 0 {
log.Debugf("Network no events to be processed by router: %s", n.options.Id)
continue
}
// create an advert and process it
advert := &router.Advert{
Id: pbRtrAdvert.Id,
Type: router.AdvertType(pbRtrAdvert.Type),
Timestamp: time.Unix(0, pbRtrAdvert.Timestamp),
TTL: time.Duration(pbRtrAdvert.Ttl),
Events: events,
}
log.Debugf("Network router %s processing advert: %s", n.Id(), advert.Id)
if err := n.router.Process(advert); err != nil {
log.Debugf("Network failed to process advert %s: %v", advert.Id, err)
}
case "solicit":
pbRtrSolicit := &pbRtr.Solicit{}
if err := proto.Unmarshal(m.Body, pbRtrSolicit); err != nil {
log.Debugf("Network fail to unmarshal solicit message: %v", err)
continue
}
log.Debugf("Network received solicit message from: %s", pbRtrSolicit.Id)
// ignore solicitation when requested by you
if pbRtrSolicit.Id == n.options.Id {
continue
}
log.Debugf("Network router flushing routes for: %s", pbRtrSolicit.Id)
// advertise all the routes when a new node has connected
if err := n.router.Solicit(); err != nil {
log.Debugf("Network failed to solicit routes: %s", err)
}
}
case <-n.closed:
return
}
}
}
// advertise advertises routes to the network
func (n *network) advertise(client transport.Client, advertChan <-chan *router.Advert) {
hasher := fnv.New64()
for {
select {
// process local adverts and randomly fire them at other nodes
case advert := <-advertChan:
// create a proto advert
var events []*pbRtr.Event
for _, event := range advert.Events {
// the routes service address
address := event.Route.Address
// only hash the address if we're advertising our own local routes
if event.Route.Router == advert.Id {
// hash the service before advertising it
hasher.Reset()
hasher.Write([]byte(event.Route.Address + n.node.id))
address = fmt.Sprintf("%d", hasher.Sum64())
}
// NOTE: we override Gateway, Link and Address here
// TODO: should we avoid overriding gateway?
route := &pbRtr.Route{
Service: event.Route.Service,
Address: address,
Gateway: n.node.id,
Network: event.Route.Network,
Router: event.Route.Router,
Link: DefaultLink,
Metric: int64(event.Route.Metric),
}
e := &pbRtr.Event{
Type: pbRtr.EventType(event.Type),
Timestamp: event.Timestamp.UnixNano(),
Route: route,
}
events = append(events, e)
}
msg := &pbRtr.Advert{
Id: advert.Id,
Type: pbRtr.AdvertType(advert.Type),
Timestamp: advert.Timestamp.UnixNano(),
Events: events,
}
if err := n.sendMsg("advert", msg, ControlChannel); err != nil {
log.Debugf("Network failed to advertise routes: %v", err)
continue
}
case <-n.closed:
return
}
}
}
// Connect connects the network
func (n *network) Connect() error {
n.Lock()
// return if already connected
if n.connected {
n.Unlock()
return nil
}
// try to resolve network nodes
nodes, err := n.resolveNodes()
if err != nil {
log.Debugf("Network failed to resolve nodes: %v", err)
}
// connect network tunnel
if err := n.tunnel.Connect(); err != nil {
n.Unlock()
return err
}
// set our internal node address
// if advertise address is not set
if len(n.options.Advertise) == 0 {
n.node.address = n.tunnel.Address()
n.server.Init(server.Advertise(n.tunnel.Address()))
}
// initialize the tunnel to resolved nodes
n.tunnel.Init(
tunnel.Nodes(nodes...),
)
// dial into ControlChannel to send route adverts
ctrlClient, err := n.tunnel.Dial(ControlChannel, tunnel.DialMulticast())
if err != nil {
n.Unlock()
return err
}
n.tunClient[ControlChannel] = ctrlClient
// listen on ControlChannel
ctrlListener, err := n.tunnel.Listen(ControlChannel)
if err != nil {
n.Unlock()
return err
}
// dial into NetworkChannel to send network messages
netClient, err := n.tunnel.Dial(NetworkChannel, tunnel.DialMulticast())
if err != nil {
n.Unlock()
return err
}
n.tunClient[NetworkChannel] = netClient
// listen on NetworkChannel
netListener, err := n.tunnel.Listen(NetworkChannel)
if err != nil {
n.Unlock()
return err
}
// create closed channel
n.closed = make(chan bool)
// start the router
if err := n.options.Router.Start(); err != nil {
n.Unlock()
return err
}
// start advertising routes
advertChan, err := n.options.Router.Advertise()
if err != nil {
n.Unlock()
return err
}
// start the server
if err := n.server.Start(); err != nil {
n.Unlock()
return err
}
n.Unlock()
// send connect message to NetworkChannel
// NOTE: in theory we could do this as soon as
// Dial to NetworkChannel succeeds, but instead
// we initialize all other node resources first
msg := &pbNet.Connect{
Node: &pbNet.Node{
Id: n.node.id,
Address: n.node.address,
},
}
if err := n.sendMsg("connect", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to send connect message: %s", err)
}
// go resolving network nodes
go n.resolve()
// broadcast peers
go n.announce(netClient)
// prune stale nodes
go n.prune()
// listen to network messages
go n.processNetChan(netClient, netListener)
// advertise service routes
go n.advertise(ctrlClient, advertChan)
// accept and process routes
go n.processCtrlChan(ctrlClient, ctrlListener)
n.Lock()
n.connected = true
n.Unlock()
return nil
}
func (n *network) close() error {
// stop the server
if err := n.server.Stop(); err != nil {
return err
}
// stop the router
if err := n.router.Stop(); err != nil {
return err
}
// close the tunnel
if err := n.tunnel.Close(); err != nil {
return err
}
return nil
}
// Close closes network connection
func (n *network) Close() error {
n.Lock()
if !n.connected {
n.Unlock()
return nil
}
select {
case <-n.closed:
n.Unlock()
return nil
default:
// TODO: send close message to the network channel
close(n.closed)
// set connected to false
n.connected = false
// unlock the lock otherwise we'll deadlock sending the close
n.Unlock()
msg := &pbNet.Close{
Node: &pbNet.Node{
Id: n.node.id,
Address: n.node.address,
},
}
if err := n.sendMsg("close", msg, NetworkChannel); err != nil {
log.Debugf("Network failed to send close message: %s", err)
}
}
return n.close()
}
// Client returns network client
func (n *network) Client() client.Client {
return n.client
}
// Server returns network server
func (n *network) Server() server.Server {
return n.server
}

145
network/handler/handler.go Normal file
View File

@ -0,0 +1,145 @@
// Package handler implements network RPC handler
package handler
import (
"context"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/network"
pbNet "github.com/micro/go-micro/network/proto"
pbRtr "github.com/micro/go-micro/router/proto"
)
// Network implements network handler
type Network struct {
Network network.Network
}
func flatten(n network.Node, visited map[string]bool) []network.Node {
// if node is nil runaway
if n == nil {
return nil
}
// set visisted
if visited == nil {
visited = make(map[string]bool)
}
// check if already visited
if visited[n.Id()] == true {
return nil
}
// create new list of nodes
var nodes []network.Node
// append the current node
nodes = append(nodes, n)
// set to visited
visited[n.Id()] = true
// visit the list of peers
for _, node := range n.Peers() {
nodes = append(nodes, flatten(node, visited)...)
}
return nodes
}
// Nodes returns the list of nodes
func (n *Network) Nodes(ctx context.Context, req *pbNet.NodesRequest, resp *pbNet.NodesResponse) error {
depth := uint(req.Depth)
if depth <= 0 || depth > network.MaxDepth {
depth = network.MaxDepth
}
// root node
nodes := map[string]network.Node{}
// get peers encoded into protobuf
peers := flatten(n.Network, nil)
// walk all the peers
for _, peer := range peers {
if peer == nil {
continue
}
if _, ok := nodes[peer.Id()]; ok {
continue
}
// add to visited list
nodes[n.Network.Id()] = peer
resp.Nodes = append(resp.Nodes, &pbNet.Node{
Id: peer.Id(),
Address: peer.Address(),
})
}
return nil
}
// Graph returns the network graph from this root node
func (n *Network) Graph(ctx context.Context, req *pbNet.GraphRequest, resp *pbNet.GraphResponse) error {
depth := uint(req.Depth)
if depth <= 0 || depth > network.MaxDepth {
depth = network.MaxDepth
}
// get peers encoded into protobuf
peers := network.PeersToProto(n.Network, depth)
// set the root node
resp.Root = peers
return nil
}
// Routes returns a list of routing table routes
func (n *Network) Routes(ctx context.Context, req *pbNet.RoutesRequest, resp *pbNet.RoutesResponse) error {
routes, err := n.Network.Options().Router.Table().List()
if err != nil {
return errors.InternalServerError("go.micro.network", "failed to list routes: %s", err)
}
var respRoutes []*pbRtr.Route
for _, route := range routes {
respRoute := &pbRtr.Route{
Service: route.Service,
Address: route.Address,
Gateway: route.Gateway,
Network: route.Network,
Router: route.Router,
Link: route.Link,
Metric: int64(route.Metric),
}
respRoutes = append(respRoutes, respRoute)
}
resp.Routes = respRoutes
return nil
}
// Services returns a list of services based on the routing table
func (n *Network) Services(ctx context.Context, req *pbNet.ServicesRequest, resp *pbNet.ServicesResponse) error {
routes, err := n.Network.Options().Router.Table().List()
if err != nil {
return errors.InternalServerError("go.micro.network", "failed to list services: %s", err)
}
services := make(map[string]bool)
for _, route := range routes {
if _, ok := services[route.Service]; ok {
continue
}
services[route.Service] = true
resp.Services = append(resp.Services, route.Service)
}
return nil
}

View File

@ -1,288 +0,0 @@
// Package link provides a measured transport.Socket link
package link
import (
"io"
"sync"
"time"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/transport"
)
type link struct {
sync.RWMutex
// the link id
id string
// the remote end to dial
addr string
// channel used to close the link
closed chan bool
// if its connected
connected bool
// the transport to use
transport transport.Transport
// the send queue to the socket
sendQueue chan *transport.Message
// the recv queue to the socket
recvQueue chan *transport.Message
// the socket for this link
socket transport.Socket
// determines the cost of the link
// based on queue length and roundtrip
length int
weight int
}
func newLink(options options.Options) *link {
// default values
var sock transport.Socket
var addr string
id := "local"
tr := transport.DefaultTransport
lid, ok := options.Values().Get("link.id")
if ok {
id = lid.(string)
}
laddr, ok := options.Values().Get("link.address")
if ok {
addr = laddr.(string)
}
ltr, ok := options.Values().Get("link.transport")
if ok {
tr = ltr.(transport.Transport)
}
lsock, ok := options.Values().Get("link.socket")
if ok {
sock = lsock.(transport.Socket)
}
l := &link{
// the remote end to dial
addr: addr,
// transport to dial link
transport: tr,
// the socket to use
// this is nil if not specified
socket: sock,
// unique id assigned to the link
id: id,
// the closed channel used to close the conn
closed: make(chan bool),
// then send queue
sendQueue: make(chan *transport.Message, 128),
// the receive queue
recvQueue: make(chan *transport.Message, 128),
}
// return the link
return l
}
// link methods
// process processes messages on the send and receive queues.
func (l *link) process() {
go func() {
for {
m := new(transport.Message)
if err := l.recv(m); err != nil {
return
}
select {
case l.recvQueue <- m:
case <-l.closed:
return
}
}
}()
// messages sent
i := 0
length := 0
for {
select {
case m := <-l.sendQueue:
t := time.Now()
// send the message
if err := l.send(m); err != nil {
return
}
// get header size, body size and time taken
hl := len(m.Header)
bl := len(m.Body)
d := time.Since(t)
// don't calculate on empty messages
if hl == 0 && bl == 0 {
continue
}
// increment sent
i++
// time take to send some bits and bytes
td := float64(hl+bl) / float64(d.Nanoseconds())
// increase the scale
td += 1
// judge the length
length = int(td) / (length + int(td))
// every 10 messages update length
if (i % 10) == 1 {
// cost average the length
// save it
l.Lock()
l.length = length
l.Unlock()
}
case <-l.closed:
return
}
}
}
// send a message over the link
func (l *link) send(m *transport.Message) error {
// TODO: measure time taken and calculate length/rate
// send via the transport socket
return l.socket.Send(m)
}
// recv a message on the link
func (l *link) recv(m *transport.Message) error {
if m.Header == nil {
m.Header = make(map[string]string)
}
// receive the transport message
return l.socket.Recv(m)
}
// Connect attempts to connect to an address and sets the socket
func (l *link) Connect() error {
l.Lock()
if l.connected {
l.Unlock()
return nil
}
defer l.Unlock()
// replace closed
l.closed = make(chan bool)
// assume existing socket
if len(l.addr) == 0 {
go l.process()
return nil
}
// dial the endpoint
c, err := l.transport.Dial(l.addr)
if err != nil {
return err
}
// set the socket
l.socket = c
// kick start the processing
go l.process()
return nil
}
// Close the link
func (l *link) Close() error {
select {
case <-l.closed:
return nil
default:
close(l.closed)
l.Lock()
l.connected = false
l.Unlock()
return l.socket.Close()
}
}
// returns the node id
func (l *link) Id() string {
l.RLock()
defer l.RUnlock()
return l.id
}
// the remote ip of the link
func (l *link) Remote() string {
l.RLock()
defer l.RUnlock()
return l.socket.Remote()
}
// the local ip of the link
func (l *link) Local() string {
l.RLock()
defer l.RUnlock()
return l.socket.Local()
}
// length/rate of the link
func (l *link) Length() int {
l.RLock()
defer l.RUnlock()
return l.length
}
// weight checks the size of the queues
func (l *link) Weight() int {
return len(l.sendQueue) + len(l.recvQueue)
}
// Accept accepts a message on the socket
func (l *link) Recv(m *transport.Message) error {
select {
case <-l.closed:
return io.EOF
case rm := <-l.recvQueue:
*m = *rm
return nil
}
// never reach
return nil
}
// Send sends a message on the socket immediately
func (l *link) Send(m *transport.Message) error {
select {
case <-l.closed:
return io.EOF
case l.sendQueue <- m:
}
return nil
}
func (l *link) Status() string {
select {
case <-l.closed:
return "closed"
default:
return "connected"
}
}

View File

@ -1,60 +0,0 @@
// Package link provides a measured link on top of a transport.Socket
package link
import (
"errors"
"github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/transport"
)
// Link is a layer on top of a transport socket with the
// buffering send and recv queue's with the ability to
// measure the actual transport link and reconnect if
// an address is specified.
type Link interface {
// provides the transport.Socket interface
transport.Socket
// Connect connects the link. It must be called first
// if there's an expectation to create a new socket.
Connect() error
// Id of the link is "local" if not specified
Id() string
// Status of the link
Status() string
// Depth of the buffers
Weight() int
// Rate of the link
Length() int
}
var (
ErrLinkClosed = errors.New("link closed")
)
// NewLink creates a new link on top of a socket
func NewLink(opts ...options.Option) Link {
return newLink(options.NewOptions(opts...))
}
// Sets the link id which otherwise defaults to "local"
func Id(id string) options.Option {
return options.WithValue("link.id", id)
}
// The address to use for the link. Connect must be
// called for this to be used, its otherwise unused.
func Address(a string) options.Option {
return options.WithValue("link.address", a)
}
// The transport to use for the link where we
// want to dial the connection first.
func Transport(t transport.Transport) options.Option {
return options.WithValue("link.transport", t)
}
// Socket sets the socket to use instead of dialing.
func Socket(s transport.Socket) options.Option {
return options.WithValue("link.socket", s)
}

View File

@ -1,2 +1,58 @@
// Package network is for creating internetworks // Package network is for creating internetworks
package network package network
import (
"time"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/server"
)
var (
// DefaultName is default network name
DefaultName = "go.micro"
// DefaultAddress is default network address
DefaultAddress = ":0"
// ResolveTime defines time interval to periodically resolve network nodes
ResolveTime = 1 * time.Minute
// AnnounceTime defines time interval to periodically announce node neighbours
AnnounceTime = 30 * time.Second
// PruneTime defines time interval to periodically check nodes that need to be pruned
// due to their not announcing their presence within this time interval
PruneTime = 90 * time.Second
)
// Node is network node
type Node interface {
// Id is node id
Id() string
// Address is node bind address
Address() string
// Peers returns node peers
Peers() []Node
// Network is the network node is in
Network() Network
}
// Network is micro network
type Network interface {
// Node is network node
Node
// Options returns the network options
Options() Options
// Name of the network
Name() string
// Connect starts the resolver and tunnel server
Connect() error
// Close stops the tunnel and resolving
Close() error
// Client is micro client
Client() client.Client
// Server is micro server
Server() server.Server
}
// NewNetwork returns a new network interface
func NewNetwork(opts ...Option) Network {
return newNetwork(opts...)
}

366
network/node.go Normal file
View File

@ -0,0 +1,366 @@
package network
import (
"container/list"
"errors"
"sync"
"time"
pb "github.com/micro/go-micro/network/proto"
)
var (
// MaxDepth defines max depth of peer topology
MaxDepth uint = 3
)
var (
// ErrPeerExists is returned when adding a peer which already exists
ErrPeerExists = errors.New("peer already exists")
// ErrPeerNotFound is returned when a peer could not be found in node topology
ErrPeerNotFound = errors.New("peer not found")
)
// node is network node
type node struct {
sync.RWMutex
// id is node id
id string
// address is node address
address string
// peers are nodes with direct link to this node
peers map[string]*node
// network returns the node network
network Network
// lastSeen keeps track of node lifetime and updates
lastSeen time.Time
}
// Id is node ide
func (n *node) Id() string {
return n.id
}
// Address returns node address
func (n *node) Address() string {
return n.address
}
// Network returns node network
func (n *node) Network() Network {
return n.network
}
// walk walks the node graph until some condition is met
func (n *node) walk(until func(peer *node) bool, action func(parent, peer *node)) map[string]*node {
// track the visited nodes
visited := make(map[string]*node)
// queue of the nodes to visit
queue := list.New()
// push node to the back of queue
queue.PushBack(n)
// mark the node as visited
visited[n.id] = n
// keep iterating over the queue until its empty
for queue.Len() > 0 {
// pop the node from the front of the queue
qnode := queue.Front()
if until(qnode.Value.(*node)) {
return visited
}
// iterate through all of the node peers
// mark the visited nodes; enqueue the non-visted
for id, peer := range qnode.Value.(*node).peers {
if _, ok := visited[id]; !ok {
visited[id] = peer
action(qnode.Value.(*node), peer)
queue.PushBack(peer)
}
}
// remove the node from the queue
queue.Remove(qnode)
}
return visited
}
// AddPeer adds a new peer to node topology
// It returns false if the peer already exists
func (n *node) AddPeer(peer *node) error {
n.Lock()
defer n.Unlock()
if _, ok := n.peers[peer.id]; !ok {
n.peers[peer.id] = peer
return nil
}
return ErrPeerExists
}
// DeletePeer deletes a peer from node peers
// It returns true if the peer has been deleted
func (n *node) DeletePeer(id string) bool {
n.Lock()
defer n.Unlock()
delete(n.peers, id)
return true
}
// UpdatePeer updates a peer if it already exists
// It returns error if the peer does not exist
func (n *node) UpdatePeer(peer *node) error {
n.Lock()
defer n.Unlock()
if _, ok := n.peers[peer.id]; ok {
n.peers[peer.id] = peer
return nil
}
return ErrPeerNotFound
}
// RefreshPeer updates node timestamp
// It returns false if the peer has not been found.
func (n *node) RefreshPeer(id string, now time.Time) error {
n.Lock()
defer n.Unlock()
peer, ok := n.peers[id]
if !ok {
return ErrPeerNotFound
}
if peer.lastSeen.Before(now) {
peer.lastSeen = now
}
return nil
}
// Nodes returns a slice of all nodes in the whole node topology
func (n *node) Nodes() []Node {
// we need to freeze the network graph here
// otherwise we might get inconsisten results
n.RLock()
defer n.RUnlock()
// NOTE: this should never be true
untilNoMorePeers := func(node *node) bool {
return node == nil
}
justWalk := func(parent, node *node) {}
visited := n.walk(untilNoMorePeers, justWalk)
var nodes []Node
// collect all the nodes and return them
for _, node := range visited {
nodes = append(nodes, node)
}
return nodes
}
// GetPeerNode returns a node from node MaxDepth topology
// It returns nil if the peer was not found
func (n *node) GetPeerNode(id string) *node {
n.RLock()
defer n.RUnlock()
// get node topology up to MaxDepth
top := n.Topology(MaxDepth)
untilFoundPeer := func(n *node) bool {
return n.id == id
}
justWalk := func(paent, node *node) {}
visited := top.walk(untilFoundPeer, justWalk)
peerNode, ok := visited[id]
if !ok {
return nil
}
return peerNode
}
// DeletePeerNode removes peer node from node topology
func (n *node) DeletePeerNode(id string) error {
n.Lock()
defer n.Unlock()
untilNoMorePeers := func(node *node) bool {
return node == nil
}
deleted := make(map[string]*node)
deletePeer := func(parent, node *node) {
if node.id != n.id && node.id == id {
delete(parent.peers, node.id)
deleted[node.id] = node
}
}
n.walk(untilNoMorePeers, deletePeer)
if _, ok := deleted[id]; !ok {
return ErrPeerNotFound
}
return nil
}
// PruneStalePeerNodes prune the peers that have not been seen for longer than given time
// It returns a map of the the nodes that got pruned
func (n *node) PruneStalePeerNodes(pruneTime time.Duration) map[string]*node {
n.Lock()
defer n.Unlock()
untilNoMorePeers := func(node *node) bool {
return node == nil
}
pruned := make(map[string]*node)
pruneStalePeer := func(parent, node *node) {
if node.id != n.id && time.Since(node.lastSeen) > PruneTime {
delete(parent.peers, node.id)
pruned[node.id] = node
}
}
n.walk(untilNoMorePeers, pruneStalePeer)
return pruned
}
// Topology returns a copy of the node topology down to given depth
// NOTE: the returned node is a node graph - not a single node
func (n *node) Topology(depth uint) *node {
n.RLock()
defer n.RUnlock()
// make a copy of yourself
node := &node{
id: n.id,
address: n.address,
peers: make(map[string]*node),
network: n.network,
lastSeen: n.lastSeen,
}
// return if we reach requested depth or we have no more peers
if depth == 0 || len(n.peers) == 0 {
return node
}
// decrement the depth
depth--
// iterate through our peers and update the node peers
for _, peer := range n.peers {
nodePeer := peer.Topology(depth)
if _, ok := node.peers[nodePeer.id]; !ok {
node.peers[nodePeer.id] = nodePeer
}
}
return node
}
// Peers returns node peers up to MaxDepth
func (n *node) Peers() []Node {
n.RLock()
defer n.RUnlock()
var peers []Node
for _, nodePeer := range n.peers {
peer := nodePeer.Topology(MaxDepth)
peers = append(peers, peer)
}
return peers
}
// UnpackPeerTopology unpacks pb.Peer into node topology of given depth
func UnpackPeerTopology(pbPeer *pb.Peer, lastSeen time.Time, depth uint) *node {
peerNode := &node{
id: pbPeer.Node.Id,
address: pbPeer.Node.Address,
peers: make(map[string]*node),
lastSeen: lastSeen,
}
// return if have either reached the depth or have no more peers
if depth == 0 || len(pbPeer.Peers) == 0 {
return peerNode
}
// decrement the depth
depth--
peers := make(map[string]*node)
for _, pbPeer := range pbPeer.Peers {
peer := UnpackPeerTopology(pbPeer, lastSeen, depth)
peers[pbPeer.Node.Id] = peer
}
peerNode.peers = peers
return peerNode
}
func peerProtoTopology(peer Node, depth uint) *pb.Peer {
node := &pb.Node{
Id: peer.Id(),
Address: peer.Address(),
}
pbPeers := &pb.Peer{
Node: node,
Peers: make([]*pb.Peer, 0),
}
// return if we reached the end of topology or depth
if depth == 0 || len(peer.Peers()) == 0 {
return pbPeers
}
// decrement the depth
depth--
// iterate through peers of peers aka pops
for _, pop := range peer.Peers() {
peer := peerProtoTopology(pop, depth)
pbPeers.Peers = append(pbPeers.Peers, peer)
}
return pbPeers
}
// PeersToProto returns node peers graph encoded into protobuf
func PeersToProto(node Node, depth uint) *pb.Peer {
// network node aka root node
pbNode := &pb.Node{
Id: node.Id(),
Address: node.Address(),
}
// we will build proto topology into this
pbPeers := &pb.Peer{
Node: pbNode,
Peers: make([]*pb.Peer, 0),
}
for _, peer := range node.Peers() {
pbPeer := peerProtoTopology(peer, depth)
pbPeers.Peers = append(pbPeers.Peers, pbPeer)
}
return pbPeers
}

319
network/node_test.go Normal file
View File

@ -0,0 +1,319 @@
package network
import (
"testing"
"time"
pb "github.com/micro/go-micro/network/proto"
)
var (
testNodeId = "testNode"
testNodeAddress = "testAddress"
testNodeNetName = "testNetwork"
testNodePeerIds = []string{"peer1", "peer2", "peer3"}
testPeerOfPeerIds = []string{"peer11", "peer12"}
)
func testSetup() *node {
testNode := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
// add some peers to the node
for _, id := range testNodePeerIds {
testNode.peers[id] = &node{
id: id,
address: testNode.address + "-" + id,
peers: make(map[string]*node),
network: testNode.network,
}
}
// add peers to peer1
// NOTE: these are peers of peers!
for _, id := range testPeerOfPeerIds {
testNode.peers["peer1"].peers[id] = &node{
id: id,
address: testNode.address + "-" + id,
peers: make(map[string]*node),
network: testNode.network,
}
}
// connect peer1 with peer2
testNode.peers["peer1"].peers["peer2"] = testNode.peers["peer2"]
// connect peer2 with peer3
testNode.peers["peer2"].peers["peer3"] = testNode.peers["peer3"]
return testNode
}
func TestNodeId(t *testing.T) {
node := testSetup()
if node.Id() != testNodeId {
t.Errorf("Expected id: %s, found: %s", testNodeId, node.Id())
}
}
func TestNodeAddress(t *testing.T) {
node := testSetup()
if node.Address() != testNodeAddress {
t.Errorf("Expected address: %s, found: %s", testNodeAddress, node.Address())
}
}
func TestNodeNetwork(t *testing.T) {
node := testSetup()
if node.Network().Name() != testNodeNetName {
t.Errorf("Expected network: %s, found: %s", testNodeNetName, node.Network().Name())
}
}
func TestNodes(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
// get all the nodes including yourself
nodes := single.Nodes()
nodeCount := 1
if len(nodes) != nodeCount {
t.Errorf("Expected to find %d nodes, found: %d", nodeCount, len(nodes))
}
// complicated node graph
node := testSetup()
// get all the nodes including yourself
nodes = node.Nodes()
// compile a list of ids of all nodes in the network into map for easy indexing
nodeIds := make(map[string]bool)
// add yourself
nodeIds[node.id] = true
// add peer Ids
for _, id := range testNodePeerIds {
nodeIds[id] = true
}
// add peer1 peers i.e. peers of peer
for _, id := range testPeerOfPeerIds {
nodeIds[id] = true
}
// we should return the correct number of nodes
if len(nodes) != len(nodeIds) {
t.Errorf("Expected %d nodes, found: %d", len(nodeIds), len(nodes))
}
// iterate through the list of nodes and makes sure all have been returned
for _, node := range nodes {
if _, ok := nodeIds[node.Id()]; !ok {
t.Errorf("Expected to find %s node", node.Id())
}
}
// this is a leaf node
id := "peer11"
if nodePeer := node.GetPeerNode(id); nodePeer == nil {
t.Errorf("Expected to find %s node", id)
}
}
func collectPeerIds(peer Node, ids map[string]bool) map[string]bool {
if len(peer.Peers()) == 0 {
return ids
}
// iterate through the whole graph
for _, peer := range peer.Peers() {
ids = collectPeerIds(peer, ids)
if _, ok := ids[peer.Id()]; !ok {
ids[peer.Id()] = true
}
}
return ids
}
func TestPeers(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
// get node peers
peers := single.Peers()
// there should be no peers
peerCount := 0
if len(peers) != peerCount {
t.Errorf("Expected to find %d nodes, found: %d", peerCount, len(peers))
}
// complicated node graph
node := testSetup()
// list of ids of nodes of MaxDepth
peerIds := make(map[string]bool)
// add peer Ids
for _, id := range testNodePeerIds {
peerIds[id] = true
}
// add peers of peers to peerIds
for _, id := range testPeerOfPeerIds {
peerIds[id] = true
}
// get node peers
peers = node.Peers()
// we will collect all returned Peer Ids into this map
resPeerIds := make(map[string]bool)
for _, peer := range peers {
resPeerIds[peer.Id()] = true
resPeerIds = collectPeerIds(peer, resPeerIds)
}
// if correct, we must collect all peerIds
if len(resPeerIds) != len(peerIds) {
t.Errorf("Expected to find %d peers, found: %d", len(peerIds), len(resPeerIds))
}
for id := range resPeerIds {
if _, ok := peerIds[id]; !ok {
t.Errorf("Expected to find %s peer", id)
}
}
}
func TestDeletePeerNode(t *testing.T) {
// complicated node graph
node := testSetup()
nodeCount := len(node.Nodes())
// should not find non-existent peer node
if err := node.DeletePeerNode("foobar"); err != ErrPeerNotFound {
t.Errorf("Expected: %v, got: %v", ErrPeerNotFound, err)
}
// lets pick one of the peer1 peers
if err := node.DeletePeerNode(testPeerOfPeerIds[0]); err != nil {
t.Errorf("Error deleting peer node: %v", err)
}
nodeDelCount := len(node.Nodes())
if nodeDelCount != nodeCount-1 {
t.Errorf("Expected node count: %d, got: %d", nodeCount-1, nodeDelCount)
}
}
func TestPruneStalePeerNodes(t *testing.T) {
// complicated node graph
node := testSetup()
nodes := node.Nodes()
pruneTime := 10 * time.Millisecond
time.Sleep(pruneTime)
// should delete all nodes besides node
pruned := node.PruneStalePeerNodes(pruneTime)
if len(pruned) != len(nodes)-1 {
t.Errorf("Expected pruned node count: %d, got: %d", len(nodes)-1, len(pruned))
}
}
func TestUnpackPeerTopology(t *testing.T) {
pbPeer := &pb.Peer{
Node: &pb.Node{
Id: "newPeer",
Address: "newPeerAddress",
},
Peers: make([]*pb.Peer, 0),
}
// it should add pbPeer to the single node peers
peer := UnpackPeerTopology(pbPeer, time.Now(), 5)
if peer.id != pbPeer.Node.Id {
t.Errorf("Expected peer id %s, found: %s", pbPeer.Node.Id, peer.id)
}
node := testSetup()
// build a simple topology to update node peer1
peer1 := node.peers["peer1"]
pbPeer1Node := &pb.Node{
Id: peer1.id,
Address: peer1.address,
}
pbPeer111 := &pb.Peer{
Node: &pb.Node{
Id: "peer111",
Address: "peer111Address",
},
Peers: make([]*pb.Peer, 0),
}
pbPeer121 := &pb.Peer{
Node: &pb.Node{
Id: "peer121",
Address: "peer121Address",
},
Peers: make([]*pb.Peer, 0),
}
// topology to update
pbPeer1 := &pb.Peer{
Node: pbPeer1Node,
Peers: []*pb.Peer{pbPeer111, pbPeer121},
}
// unpack peer1 topology
peer = UnpackPeerTopology(pbPeer1, time.Now(), 5)
// make sure peer1 topology has been correctly updated
newPeerIds := []string{pbPeer111.Node.Id, pbPeer121.Node.Id}
for _, id := range newPeerIds {
if _, ok := peer.peers[id]; !ok {
t.Errorf("Expected %s to be a peer of %s", id, "peer1")
}
}
}
func TestPeersToProto(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: newNetwork(Name(testNodeNetName)),
}
topCount := 0
protoPeers := PeersToProto(single, 0)
if len(protoPeers.Peers) != topCount {
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
}
// complicated node graph
node := testSetup()
topCount = 3
// list of ids of nodes of depth 1 i.e. node peers
peerIds := make(map[string]bool)
// add peer Ids
for _, id := range testNodePeerIds {
peerIds[id] = true
}
// depth 1 should give us immmediate neighbours only
protoPeers = PeersToProto(node, 1)
if len(protoPeers.Peers) != topCount {
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
}
}

111
network/options.go Normal file
View File

@ -0,0 +1,111 @@
package network
import (
"github.com/google/uuid"
"github.com/micro/go-micro/network/resolver"
"github.com/micro/go-micro/network/resolver/registry"
"github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/proxy/mucp"
"github.com/micro/go-micro/router"
"github.com/micro/go-micro/tunnel"
)
type Option func(*Options)
// Options configure network
type Options struct {
// Id of the node
Id string
// Name of the network
Name string
// Address to bind to
Address string
// Advertise sets the address to advertise
Advertise string
// Peers is a list of peers to connect to
Peers []string
// Tunnel is network tunnel
Tunnel tunnel.Tunnel
// Router is network router
Router router.Router
// Proxy is network proxy
Proxy proxy.Proxy
// Resolver is network resolver
Resolver resolver.Resolver
}
// Id sets the id of the network node
func Id(id string) Option {
return func(o *Options) {
o.Id = id
}
}
// Name sets the network name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Address sets the network address
func Address(a string) Option {
return func(o *Options) {
o.Address = a
}
}
// Advertise sets the address to advertise
func Advertise(a string) Option {
return func(o *Options) {
o.Advertise = a
}
}
// Peers is a list of peers to connect to
func Peers(n ...string) Option {
return func(o *Options) {
o.Peers = n
}
}
// Tunnel sets the network tunnel
func Tunnel(t tunnel.Tunnel) Option {
return func(o *Options) {
o.Tunnel = t
}
}
// Router sets the network router
func Router(r router.Router) Option {
return func(o *Options) {
o.Router = r
}
}
// Proxy sets the network proxy
func Proxy(p proxy.Proxy) Option {
return func(o *Options) {
o.Proxy = p
}
}
// Resolver is the network resolver
func Resolver(r resolver.Resolver) Option {
return func(o *Options) {
o.Resolver = r
}
}
// DefaultOptions returns network default options
func DefaultOptions() Options {
return Options{
Id: uuid.New().String(),
Name: DefaultName,
Address: DefaultAddress,
Tunnel: tunnel.NewTunnel(),
Router: router.DefaultRouter,
Proxy: mucp.NewProxy(),
Resolver: &registry.Resolver{},
}
}

View File

@ -0,0 +1,151 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: github.com/micro/go-micro/network/proto/network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
_ "github.com/micro/go-micro/router/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 Network service
type NetworkService interface {
// Returns the entire network graph
Graph(ctx context.Context, in *GraphRequest, opts ...client.CallOption) (*GraphResponse, error)
// Returns a list of known nodes in the network
Nodes(ctx context.Context, in *NodesRequest, opts ...client.CallOption) (*NodesResponse, error)
// Returns a list of known routes in the network
Routes(ctx context.Context, in *RoutesRequest, opts ...client.CallOption) (*RoutesResponse, error)
// Returns a list of known services based on routes
Services(ctx context.Context, in *ServicesRequest, opts ...client.CallOption) (*ServicesResponse, error)
}
type networkService struct {
c client.Client
name string
}
func NewNetworkService(name string, c client.Client) NetworkService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.network"
}
return &networkService{
c: c,
name: name,
}
}
func (c *networkService) Graph(ctx context.Context, in *GraphRequest, opts ...client.CallOption) (*GraphResponse, error) {
req := c.c.NewRequest(c.name, "Network.Graph", in)
out := new(GraphResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Nodes(ctx context.Context, in *NodesRequest, opts ...client.CallOption) (*NodesResponse, error) {
req := c.c.NewRequest(c.name, "Network.Nodes", in)
out := new(NodesResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Routes(ctx context.Context, in *RoutesRequest, opts ...client.CallOption) (*RoutesResponse, error) {
req := c.c.NewRequest(c.name, "Network.Routes", in)
out := new(RoutesResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Services(ctx context.Context, in *ServicesRequest, opts ...client.CallOption) (*ServicesResponse, error) {
req := c.c.NewRequest(c.name, "Network.Services", in)
out := new(ServicesResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Network service
type NetworkHandler interface {
// Returns the entire network graph
Graph(context.Context, *GraphRequest, *GraphResponse) error
// Returns a list of known nodes in the network
Nodes(context.Context, *NodesRequest, *NodesResponse) error
// Returns a list of known routes in the network
Routes(context.Context, *RoutesRequest, *RoutesResponse) error
// Returns a list of known services based on routes
Services(context.Context, *ServicesRequest, *ServicesResponse) error
}
func RegisterNetworkHandler(s server.Server, hdlr NetworkHandler, opts ...server.HandlerOption) error {
type network interface {
Graph(ctx context.Context, in *GraphRequest, out *GraphResponse) error
Nodes(ctx context.Context, in *NodesRequest, out *NodesResponse) error
Routes(ctx context.Context, in *RoutesRequest, out *RoutesResponse) error
Services(ctx context.Context, in *ServicesRequest, out *ServicesResponse) error
}
type Network struct {
network
}
h := &networkHandler{hdlr}
return s.Handle(s.NewHandler(&Network{h}, opts...))
}
type networkHandler struct {
NetworkHandler
}
func (h *networkHandler) Graph(ctx context.Context, in *GraphRequest, out *GraphResponse) error {
return h.NetworkHandler.Graph(ctx, in, out)
}
func (h *networkHandler) Nodes(ctx context.Context, in *NodesRequest, out *NodesResponse) error {
return h.NetworkHandler.Nodes(ctx, in, out)
}
func (h *networkHandler) Routes(ctx context.Context, in *RoutesRequest, out *RoutesResponse) error {
return h.NetworkHandler.Routes(ctx, in, out)
}
func (h *networkHandler) Services(ctx context.Context, in *ServicesRequest, out *ServicesResponse) error {
return h.NetworkHandler.Services(ctx, in, out)
}

735
network/proto/network.pb.go Normal file
View File

@ -0,0 +1,735 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: github.com/micro/go-micro/network/proto/network.proto
package go_micro_network
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
proto1 "github.com/micro/go-micro/router/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
// PeerRequest requests list of peers
type NodesRequest struct {
// node topology depth
Depth uint32 `protobuf:"varint,1,opt,name=depth,proto3" json:"depth,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NodesRequest) Reset() { *m = NodesRequest{} }
func (m *NodesRequest) String() string { return proto.CompactTextString(m) }
func (*NodesRequest) ProtoMessage() {}
func (*NodesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{0}
}
func (m *NodesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodesRequest.Unmarshal(m, b)
}
func (m *NodesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NodesRequest.Marshal(b, m, deterministic)
}
func (m *NodesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_NodesRequest.Merge(m, src)
}
func (m *NodesRequest) XXX_Size() int {
return xxx_messageInfo_NodesRequest.Size(m)
}
func (m *NodesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_NodesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_NodesRequest proto.InternalMessageInfo
func (m *NodesRequest) GetDepth() uint32 {
if m != nil {
return m.Depth
}
return 0
}
// PeerResponse is returned by ListPeers
type NodesResponse struct {
// return peer topology
Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NodesResponse) Reset() { *m = NodesResponse{} }
func (m *NodesResponse) String() string { return proto.CompactTextString(m) }
func (*NodesResponse) ProtoMessage() {}
func (*NodesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{1}
}
func (m *NodesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodesResponse.Unmarshal(m, b)
}
func (m *NodesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NodesResponse.Marshal(b, m, deterministic)
}
func (m *NodesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_NodesResponse.Merge(m, src)
}
func (m *NodesResponse) XXX_Size() int {
return xxx_messageInfo_NodesResponse.Size(m)
}
func (m *NodesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_NodesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_NodesResponse proto.InternalMessageInfo
func (m *NodesResponse) GetNodes() []*Node {
if m != nil {
return m.Nodes
}
return nil
}
type GraphRequest struct {
// node topology depth
Depth uint32 `protobuf:"varint,1,opt,name=depth,proto3" json:"depth,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GraphRequest) Reset() { *m = GraphRequest{} }
func (m *GraphRequest) String() string { return proto.CompactTextString(m) }
func (*GraphRequest) ProtoMessage() {}
func (*GraphRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{2}
}
func (m *GraphRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphRequest.Unmarshal(m, b)
}
func (m *GraphRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GraphRequest.Marshal(b, m, deterministic)
}
func (m *GraphRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GraphRequest.Merge(m, src)
}
func (m *GraphRequest) XXX_Size() int {
return xxx_messageInfo_GraphRequest.Size(m)
}
func (m *GraphRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GraphRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GraphRequest proto.InternalMessageInfo
func (m *GraphRequest) GetDepth() uint32 {
if m != nil {
return m.Depth
}
return 0
}
type GraphResponse struct {
Root *Peer `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GraphResponse) Reset() { *m = GraphResponse{} }
func (m *GraphResponse) String() string { return proto.CompactTextString(m) }
func (*GraphResponse) ProtoMessage() {}
func (*GraphResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{3}
}
func (m *GraphResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphResponse.Unmarshal(m, b)
}
func (m *GraphResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GraphResponse.Marshal(b, m, deterministic)
}
func (m *GraphResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GraphResponse.Merge(m, src)
}
func (m *GraphResponse) XXX_Size() int {
return xxx_messageInfo_GraphResponse.Size(m)
}
func (m *GraphResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GraphResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GraphResponse proto.InternalMessageInfo
func (m *GraphResponse) GetRoot() *Peer {
if m != nil {
return m.Root
}
return nil
}
type RoutesRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RoutesRequest) Reset() { *m = RoutesRequest{} }
func (m *RoutesRequest) String() string { return proto.CompactTextString(m) }
func (*RoutesRequest) ProtoMessage() {}
func (*RoutesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{4}
}
func (m *RoutesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoutesRequest.Unmarshal(m, b)
}
func (m *RoutesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RoutesRequest.Marshal(b, m, deterministic)
}
func (m *RoutesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RoutesRequest.Merge(m, src)
}
func (m *RoutesRequest) XXX_Size() int {
return xxx_messageInfo_RoutesRequest.Size(m)
}
func (m *RoutesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RoutesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RoutesRequest proto.InternalMessageInfo
type RoutesResponse struct {
Routes []*proto1.Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RoutesResponse) Reset() { *m = RoutesResponse{} }
func (m *RoutesResponse) String() string { return proto.CompactTextString(m) }
func (*RoutesResponse) ProtoMessage() {}
func (*RoutesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{5}
}
func (m *RoutesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoutesResponse.Unmarshal(m, b)
}
func (m *RoutesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RoutesResponse.Marshal(b, m, deterministic)
}
func (m *RoutesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RoutesResponse.Merge(m, src)
}
func (m *RoutesResponse) XXX_Size() int {
return xxx_messageInfo_RoutesResponse.Size(m)
}
func (m *RoutesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RoutesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RoutesResponse proto.InternalMessageInfo
func (m *RoutesResponse) GetRoutes() []*proto1.Route {
if m != nil {
return m.Routes
}
return nil
}
type ServicesRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ServicesRequest) Reset() { *m = ServicesRequest{} }
func (m *ServicesRequest) String() string { return proto.CompactTextString(m) }
func (*ServicesRequest) ProtoMessage() {}
func (*ServicesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{6}
}
func (m *ServicesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ServicesRequest.Unmarshal(m, b)
}
func (m *ServicesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ServicesRequest.Marshal(b, m, deterministic)
}
func (m *ServicesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ServicesRequest.Merge(m, src)
}
func (m *ServicesRequest) XXX_Size() int {
return xxx_messageInfo_ServicesRequest.Size(m)
}
func (m *ServicesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ServicesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ServicesRequest proto.InternalMessageInfo
type ServicesResponse struct {
Services []string `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ServicesResponse) Reset() { *m = ServicesResponse{} }
func (m *ServicesResponse) String() string { return proto.CompactTextString(m) }
func (*ServicesResponse) ProtoMessage() {}
func (*ServicesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{7}
}
func (m *ServicesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ServicesResponse.Unmarshal(m, b)
}
func (m *ServicesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ServicesResponse.Marshal(b, m, deterministic)
}
func (m *ServicesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ServicesResponse.Merge(m, src)
}
func (m *ServicesResponse) XXX_Size() int {
return xxx_messageInfo_ServicesResponse.Size(m)
}
func (m *ServicesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ServicesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ServicesResponse proto.InternalMessageInfo
func (m *ServicesResponse) GetServices() []string {
if m != nil {
return m.Services
}
return nil
}
// Node is network node
type Node struct {
// node id
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// node address
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Node) Reset() { *m = Node{} }
func (m *Node) String() string { return proto.CompactTextString(m) }
func (*Node) ProtoMessage() {}
func (*Node) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{8}
}
func (m *Node) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Node.Unmarshal(m, b)
}
func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Node.Marshal(b, m, deterministic)
}
func (m *Node) XXX_Merge(src proto.Message) {
xxx_messageInfo_Node.Merge(m, src)
}
func (m *Node) XXX_Size() int {
return xxx_messageInfo_Node.Size(m)
}
func (m *Node) XXX_DiscardUnknown() {
xxx_messageInfo_Node.DiscardUnknown(m)
}
var xxx_messageInfo_Node proto.InternalMessageInfo
func (m *Node) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Node) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
// Connect is sent when the node connects to the network
type Connect struct {
// network mode
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Connect) Reset() { *m = Connect{} }
func (m *Connect) String() string { return proto.CompactTextString(m) }
func (*Connect) ProtoMessage() {}
func (*Connect) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{9}
}
func (m *Connect) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Connect.Unmarshal(m, b)
}
func (m *Connect) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Connect.Marshal(b, m, deterministic)
}
func (m *Connect) XXX_Merge(src proto.Message) {
xxx_messageInfo_Connect.Merge(m, src)
}
func (m *Connect) XXX_Size() int {
return xxx_messageInfo_Connect.Size(m)
}
func (m *Connect) XXX_DiscardUnknown() {
xxx_messageInfo_Connect.DiscardUnknown(m)
}
var xxx_messageInfo_Connect proto.InternalMessageInfo
func (m *Connect) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Close is sent when the node disconnects from the network
type Close struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Close) Reset() { *m = Close{} }
func (m *Close) String() string { return proto.CompactTextString(m) }
func (*Close) ProtoMessage() {}
func (*Close) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{10}
}
func (m *Close) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Close.Unmarshal(m, b)
}
func (m *Close) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Close.Marshal(b, m, deterministic)
}
func (m *Close) XXX_Merge(src proto.Message) {
xxx_messageInfo_Close.Merge(m, src)
}
func (m *Close) XXX_Size() int {
return xxx_messageInfo_Close.Size(m)
}
func (m *Close) XXX_DiscardUnknown() {
xxx_messageInfo_Close.DiscardUnknown(m)
}
var xxx_messageInfo_Close proto.InternalMessageInfo
func (m *Close) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Peer is used to advertise node peers
type Peer struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
// node peers
Peers []*Peer `protobuf:"bytes,2,rep,name=peers,proto3" json:"peers,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Peer) Reset() { *m = Peer{} }
func (m *Peer) String() string { return proto.CompactTextString(m) }
func (*Peer) ProtoMessage() {}
func (*Peer) Descriptor() ([]byte, []int) {
return fileDescriptor_0b7953b26a7c4730, []int{11}
}
func (m *Peer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Peer.Unmarshal(m, b)
}
func (m *Peer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Peer.Marshal(b, m, deterministic)
}
func (m *Peer) XXX_Merge(src proto.Message) {
xxx_messageInfo_Peer.Merge(m, src)
}
func (m *Peer) XXX_Size() int {
return xxx_messageInfo_Peer.Size(m)
}
func (m *Peer) XXX_DiscardUnknown() {
xxx_messageInfo_Peer.DiscardUnknown(m)
}
var xxx_messageInfo_Peer proto.InternalMessageInfo
func (m *Peer) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
func (m *Peer) GetPeers() []*Peer {
if m != nil {
return m.Peers
}
return nil
}
func init() {
proto.RegisterType((*NodesRequest)(nil), "go.micro.network.NodesRequest")
proto.RegisterType((*NodesResponse)(nil), "go.micro.network.NodesResponse")
proto.RegisterType((*GraphRequest)(nil), "go.micro.network.GraphRequest")
proto.RegisterType((*GraphResponse)(nil), "go.micro.network.GraphResponse")
proto.RegisterType((*RoutesRequest)(nil), "go.micro.network.RoutesRequest")
proto.RegisterType((*RoutesResponse)(nil), "go.micro.network.RoutesResponse")
proto.RegisterType((*ServicesRequest)(nil), "go.micro.network.ServicesRequest")
proto.RegisterType((*ServicesResponse)(nil), "go.micro.network.ServicesResponse")
proto.RegisterType((*Node)(nil), "go.micro.network.Node")
proto.RegisterType((*Connect)(nil), "go.micro.network.Connect")
proto.RegisterType((*Close)(nil), "go.micro.network.Close")
proto.RegisterType((*Peer)(nil), "go.micro.network.Peer")
}
func init() {
proto.RegisterFile("github.com/micro/go-micro/network/proto/network.proto", fileDescriptor_0b7953b26a7c4730)
}
var fileDescriptor_0b7953b26a7c4730 = []byte{
// 416 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x5d, 0x6f, 0xda, 0x30,
0x14, 0x86, 0x21, 0x10, 0x3e, 0xce, 0x16, 0x60, 0xd6, 0x34, 0x45, 0xb9, 0xd8, 0x98, 0xb5, 0x0b,
0x34, 0x6d, 0x66, 0x02, 0x71, 0x35, 0x4d, 0x9a, 0xc4, 0x45, 0xa5, 0x4a, 0x45, 0x95, 0xf9, 0x03,
0x85, 0xc4, 0x82, 0xa8, 0x25, 0x4e, 0x1d, 0xd3, 0xfe, 0xc2, 0xfe, 0xaf, 0xca, 0x1f, 0xe1, 0x33,
0x41, 0xed, 0x1d, 0xe7, 0xf0, 0xf8, 0x3d, 0x3e, 0xaf, 0xdf, 0xc0, 0x64, 0x15, 0xcb, 0xf5, 0x76,
0x49, 0x42, 0xbe, 0x19, 0x6e, 0xe2, 0x50, 0xf0, 0xe1, 0x8a, 0xff, 0x36, 0x3f, 0x12, 0x26, 0x9f,
0xb9, 0xb8, 0x1f, 0xa6, 0x82, 0xcb, 0x5d, 0x45, 0x74, 0x85, 0x7a, 0x2b, 0x4e, 0x34, 0x45, 0x6c,
0x3f, 0x18, 0x97, 0x0b, 0x09, 0xbe, 0x95, 0x4c, 0x58, 0x1d, 0x53, 0x18, 0x19, 0xfc, 0x03, 0x3e,
0xce, 0x78, 0xc4, 0x32, 0xca, 0x1e, 0xb7, 0x2c, 0x93, 0xe8, 0x33, 0xb8, 0x11, 0x4b, 0xe5, 0xda,
0xaf, 0xf6, 0xab, 0x03, 0x8f, 0x9a, 0x02, 0xff, 0x03, 0xcf, 0x52, 0x59, 0xca, 0x93, 0x8c, 0xa1,
0x5f, 0xe0, 0x26, 0xaa, 0xe1, 0x57, 0xfb, 0xb5, 0xc1, 0x87, 0xd1, 0x17, 0x72, 0x7a, 0x1b, 0xa2,
0x78, 0x6a, 0x20, 0x35, 0xe4, 0x4a, 0x2c, 0xd2, 0xf5, 0xe5, 0x21, 0x7f, 0xc1, 0xb3, 0x94, 0x1d,
0xf2, 0x13, 0xea, 0x82, 0x73, 0xa9, 0xa9, 0xc2, 0x19, 0xb7, 0x8c, 0x09, 0xaa, 0x19, 0xdc, 0x05,
0x8f, 0xaa, 0xbd, 0xf2, 0x45, 0xf0, 0x7f, 0xe8, 0xe4, 0x0d, 0x2b, 0x47, 0xa0, 0xa1, 0x57, 0x2f,
0xb8, 0xb4, 0xb5, 0x44, 0x1f, 0xa0, 0x96, 0xc2, 0x9f, 0xa0, 0x3b, 0x67, 0xe2, 0x29, 0x0e, 0xf7,
0xa2, 0x04, 0x7a, 0xfb, 0x96, 0x95, 0x0d, 0xa0, 0x95, 0xd9, 0x9e, 0x16, 0x6e, 0xd3, 0x5d, 0x8d,
0xff, 0x40, 0x5d, 0xf9, 0x80, 0x3a, 0xe0, 0xc4, 0x91, 0xde, 0xa3, 0x4d, 0x9d, 0x38, 0x42, 0x3e,
0x34, 0x17, 0x51, 0x24, 0x58, 0x96, 0xf9, 0x8e, 0x6e, 0xe6, 0x25, 0x9e, 0x40, 0x73, 0xca, 0x93,
0x84, 0x85, 0x52, 0xad, 0xaf, 0xec, 0x2b, 0x5f, 0x5f, 0x5b, 0xac, 0x19, 0x3c, 0x06, 0x77, 0xfa,
0xc0, 0x8d, 0x67, 0x6f, 0x3e, 0x74, 0x07, 0x75, 0xe5, 0xe0, 0x7b, 0xce, 0xa8, 0x87, 0x4f, 0x19,
0x13, 0xea, 0xde, 0xb5, 0x0b, 0x8f, 0x62, 0xa0, 0xd1, 0x8b, 0x03, 0xcd, 0x99, 0xe9, 0xa3, 0x6b,
0x70, 0xf5, 0xf3, 0xa2, 0xaf, 0xe7, 0x67, 0x0e, 0xd3, 0x11, 0x7c, 0x2b, 0xfd, 0xdf, 0x38, 0x8e,
0x2b, 0x4a, 0x4b, 0xe7, 0xb1, 0x48, 0xeb, 0x30, 0xce, 0x45, 0x5a, 0x47, 0x41, 0xc6, 0x15, 0x74,
0x03, 0x0d, 0x13, 0x14, 0x54, 0x00, 0x1f, 0x65, 0x2a, 0xe8, 0x97, 0x03, 0x3b, 0xb9, 0x39, 0xb4,
0xf2, 0x88, 0xa0, 0xef, 0xe7, 0xfc, 0x49, 0xa2, 0x02, 0x7c, 0x09, 0xc9, 0x45, 0x97, 0x0d, 0xfd,
0xb1, 0x8e, 0x5f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x3a, 0x63, 0x7a, 0x7b, 0x2c, 0x04, 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
// NetworkClient is the client API for Network service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type NetworkClient interface {
// Returns the entire network graph
Graph(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error)
// Returns a list of known nodes in the network
Nodes(ctx context.Context, in *NodesRequest, opts ...grpc.CallOption) (*NodesResponse, error)
// Returns a list of known routes in the network
Routes(ctx context.Context, in *RoutesRequest, opts ...grpc.CallOption) (*RoutesResponse, error)
// Returns a list of known services based on routes
Services(ctx context.Context, in *ServicesRequest, opts ...grpc.CallOption) (*ServicesResponse, error)
}
type networkClient struct {
cc *grpc.ClientConn
}
func NewNetworkClient(cc *grpc.ClientConn) NetworkClient {
return &networkClient{cc}
}
func (c *networkClient) Graph(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error) {
out := new(GraphResponse)
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Graph", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkClient) Nodes(ctx context.Context, in *NodesRequest, opts ...grpc.CallOption) (*NodesResponse, error) {
out := new(NodesResponse)
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Nodes", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkClient) Routes(ctx context.Context, in *RoutesRequest, opts ...grpc.CallOption) (*RoutesResponse, error) {
out := new(RoutesResponse)
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Routes", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkClient) Services(ctx context.Context, in *ServicesRequest, opts ...grpc.CallOption) (*ServicesResponse, error) {
out := new(ServicesResponse)
err := c.cc.Invoke(ctx, "/go.micro.network.Network/Services", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// NetworkServer is the server API for Network service.
type NetworkServer interface {
// Returns the entire network graph
Graph(context.Context, *GraphRequest) (*GraphResponse, error)
// Returns a list of known nodes in the network
Nodes(context.Context, *NodesRequest) (*NodesResponse, error)
// Returns a list of known routes in the network
Routes(context.Context, *RoutesRequest) (*RoutesResponse, error)
// Returns a list of known services based on routes
Services(context.Context, *ServicesRequest) (*ServicesResponse, error)
}
func RegisterNetworkServer(s *grpc.Server, srv NetworkServer) {
s.RegisterService(&_Network_serviceDesc, srv)
}
func _Network_Graph_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GraphRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NetworkServer).Graph(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.network.Network/Graph",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NetworkServer).Graph(ctx, req.(*GraphRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Network_Nodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(NodesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NetworkServer).Nodes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.network.Network/Nodes",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NetworkServer).Nodes(ctx, req.(*NodesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Network_Routes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RoutesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NetworkServer).Routes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.network.Network/Routes",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NetworkServer).Routes(ctx, req.(*RoutesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Network_Services_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ServicesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NetworkServer).Services(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.network.Network/Services",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NetworkServer).Services(ctx, req.(*ServicesRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Network_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.network.Network",
HandlerType: (*NetworkServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Graph",
Handler: _Network_Graph_Handler,
},
{
MethodName: "Nodes",
Handler: _Network_Nodes_Handler,
},
{
MethodName: "Routes",
Handler: _Network_Routes_Handler,
},
{
MethodName: "Services",
Handler: _Network_Services_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "github.com/micro/go-micro/network/proto/network.proto",
}

View File

@ -0,0 +1,79 @@
syntax = "proto3";
package go.micro.network;
import "github.com/micro/go-micro/router/proto/router.proto";
// Network service is usesd to gain visibility into networks
service Network {
// Returns the entire network graph
rpc Graph(GraphRequest) returns (GraphResponse) {};
// Returns a list of known nodes in the network
rpc Nodes(NodesRequest) returns (NodesResponse) {};
// Returns a list of known routes in the network
rpc Routes(RoutesRequest) returns (RoutesResponse) {};
// Returns a list of known services based on routes
rpc Services(ServicesRequest) returns (ServicesResponse) {};
}
// PeerRequest requests list of peers
message NodesRequest {
// node topology depth
uint32 depth = 1;
}
// PeerResponse is returned by ListPeers
message NodesResponse {
// return peer topology
repeated Node nodes = 1;
}
message GraphRequest {
// node topology depth
uint32 depth = 1;
}
message GraphResponse {
Peer root = 1;
}
message RoutesRequest {
}
message RoutesResponse {
repeated go.micro.router.Route routes = 1;
}
message ServicesRequest {}
message ServicesResponse {
repeated string services = 1;
}
// Node is network node
message Node {
// node id
string id = 1;
// node address
string address = 2;
}
// Connect is sent when the node connects to the network
message Connect {
// network mode
Node node = 1;
}
// Close is sent when the node disconnects from the network
message Close {
// network node
Node node = 1;
}
// Peer is used to advertise node peers
message Peer {
// network node
Node node = 1;
// node peers
repeated Peer peers = 2;
}

View File

@ -8,6 +8,7 @@ import (
"github.com/micro/go-micro/network/resolver" "github.com/micro/go-micro/network/resolver"
) )
// Resolver is a DNS network resolve
type Resolver struct{} type Resolver struct{}
// Resolve assumes ID is a domain name e.g micro.mu // Resolve assumes ID is a domain name e.g micro.mu

View File

@ -3,6 +3,7 @@ package http
import ( import (
"encoding/json" "encoding/json"
"errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -10,17 +11,26 @@ import (
"github.com/micro/go-micro/network/resolver" "github.com/micro/go-micro/network/resolver"
) )
// Resolver is a HTTP network resolver
type Resolver struct { type Resolver struct {
// If not set, defaults to http // If not set, defaults to http
Proto string Proto string
// Path sets the path to lookup. Defaults to /network // Path sets the path to lookup. Defaults to /network
Path string Path string
// Host url to use for the query
Host string
}
type Response struct {
Nodes []*resolver.Record `json:"nodes,omitempty"`
} }
// Resolve assumes ID is a domain which can be converted to a http://name/network request // Resolve assumes ID is a domain which can be converted to a http://name/network request
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) { func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
proto := "http" proto := "https"
host := "micro.mu"
path := "/network" path := "/network"
if len(r.Proto) > 0 { if len(r.Proto) > 0 {
@ -31,29 +41,38 @@ func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
path = r.Path path = r.Path
} }
if len(r.Host) > 0 {
host = r.Host
}
uri := &url.URL{ uri := &url.URL{
Scheme: proto, Scheme: proto,
Path: path, Path: path,
Host: name, Host: host,
} }
q := uri.Query()
q.Set("name", name)
uri.RawQuery = q.Encode()
rsp, err := http.Get(uri.String()) rsp, err := http.Get(uri.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rsp.Body.Close() defer rsp.Body.Close()
if rsp.StatusCode != 200 {
return nil, errors.New("non 200 response")
}
b, err := ioutil.ReadAll(rsp.Body) b, err := ioutil.ReadAll(rsp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// encoding format is assumed to be json // encoding format is assumed to be json
var records []*resolver.Record var response *Response
if err := json.Unmarshal(b, &records); err != nil { if err := json.Unmarshal(b, &response); err != nil {
return nil, err return nil, err
} }
return records, nil return response.Nodes, nil
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
) )
// Resolver is a registry network resolver
type Resolver struct { type Resolver struct {
// Registry is the registry to use otherwise we use the defaul // Registry is the registry to use otherwise we use the defaul
Registry registry.Registry Registry registry.Registry

View File

@ -5,7 +5,7 @@ package resolver
// via the name to connect to. This is done based on Network.Name(). // via the name to connect to. This is done based on Network.Name().
// Before we can be part of any network, we have to connect to it. // Before we can be part of any network, we have to connect to it.
type Resolver interface { type Resolver interface {
// Resolve returns a list of addresses for an name // Resolve returns a list of addresses for a name
Resolve(name string) ([]*Record, error) Resolve(name string) ([]*Record, error)
} }

View File

@ -0,0 +1,33 @@
// Package static is a static resolver
package registry
import (
"github.com/micro/go-micro/network/resolver"
)
// Resolver returns a static list of nodes. In the event the node list
// is not present it will return the name of the network passed in.
type Resolver struct {
// A static list of nodes
Nodes []string
}
// Resolve returns the list of nodes
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
// if there are no nodes just return the name
if len(r.Nodes) == 0 {
return []*resolver.Record{
{Address: name},
}, nil
}
var records []*resolver.Record
for _, node := range r.Nodes {
records = append(records, &resolver.Record{
Address: node,
})
}
return records, nil
}

125
plugin/default.go Normal file
View File

@ -0,0 +1,125 @@
// Package plugin provides the ability to load plugins
package plugin
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
pg "plugin"
"strings"
"text/template"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/client/selector"
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport"
)
type plugin struct{}
// Init sets up the plugin
func (p *plugin) Init(c *Config) error {
switch c.Type {
case "broker":
pg, ok := c.NewFunc.(func(...broker.Option) broker.Broker)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultBrokers[c.Name] = pg
case "client":
pg, ok := c.NewFunc.(func(...client.Option) client.Client)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultClients[c.Name] = pg
case "registry":
pg, ok := c.NewFunc.(func(...registry.Option) registry.Registry)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultRegistries[c.Name] = pg
case "selector":
pg, ok := c.NewFunc.(func(...selector.Option) selector.Selector)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultSelectors[c.Name] = pg
case "server":
pg, ok := c.NewFunc.(func(...server.Option) server.Server)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultServers[c.Name] = pg
case "transport":
pg, ok := c.NewFunc.(func(...transport.Option) transport.Transport)
if !ok {
return fmt.Errorf("Invalid plugin %s", c.Name)
}
cmd.DefaultTransports[c.Name] = pg
default:
return fmt.Errorf("Unknown plugin type: %s for %s", c.Type, c.Name)
}
return nil
}
// Load loads a plugin created with `go build -buildmode=plugin`
func (p *plugin) Load(path string) (*Config, error) {
plugin, err := pg.Open(path)
if err != nil {
return nil, err
}
s, err := plugin.Lookup("Plugin")
if err != nil {
return nil, err
}
pl, ok := s.(*Config)
if !ok {
return nil, errors.New("could not cast Plugin object")
}
return pl, nil
}
// Generate creates a go file at the specified path.
// You must use `go build -buildmode=plugin`to build it.
func (p *plugin) Generate(path string, c *Config) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
t, err := template.New(c.Name).Parse(tmpl)
if err != nil {
return err
}
return t.Execute(f, c)
}
// Build generates a dso plugin using the go command `go build -buildmode=plugin`
func (p *plugin) Build(path string, c *Config) error {
path = strings.TrimSuffix(path, ".so")
// create go file in tmp path
temp := os.TempDir()
base := filepath.Base(path)
goFile := filepath.Join(temp, base+".go")
// generate .go file
if err := p.Generate(goFile, c); err != nil {
return err
}
// remove .go file
defer os.Remove(goFile)
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !os.IsExist(err) {
return fmt.Errorf("Failed to create dir %s: %v", filepath.Dir(path), err)
}
cmd := exec.Command("go", "build", "-buildmode=plugin", "-o", path+".so", goFile)
return cmd.Run()
}

46
plugin/plugin.go Normal file
View File

@ -0,0 +1,46 @@
// Package plugin provides the ability to load plugins
package plugin
// Plugin is a plugin loaded from a file
type Plugin interface {
// Initialise a plugin with the config
Init(c *Config) error
// Load loads a .so plugin at the given path
Load(path string) (*Config, error)
// Build a .so plugin with config at the path specified
Build(path string, c *Config) error
}
// Config is the plugin config
type Config struct {
// Name of the plugin e.g rabbitmq
Name string
// Type of the plugin e.g broker
Type string
// Path specifies the import path
Path string
// NewFunc creates an instance of the plugin
NewFunc interface{}
}
var (
// Default plugin loader
DefaultPlugin = NewPlugin()
)
// NewPlugin creates a new plugin interface
func NewPlugin() Plugin {
return &plugin{}
}
func Build(path string, c *Config) error {
return DefaultPlugin.Build(path, c)
}
func Load(path string) (*Config, error) {
return DefaultPlugin.Load(path)
}
func Init(c *Config) error {
return DefaultPlugin.Init(c)
}

20
plugin/template.go Normal file
View File

@ -0,0 +1,20 @@
package plugin
var (
tmpl = `
package main
import (
"github.com/micro/go-micro/plugin"
"{{.Path}}"
)
var Plugin = plugin.Config{
Name: "{{.Name}}",
Type: "{{.Type}}",
Path: "{{.Path}}",
NewFunc: {{.Name}}.{{.NewFunc}},
}
`
)

View File

@ -10,6 +10,7 @@ import (
"github.com/micro/go-micro/client/grpc" "github.com/micro/go-micro/client/grpc"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/config/options" "github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy" "github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
) )
@ -61,6 +62,10 @@ func readLoop(r server.Request, s client.Stream) error {
} }
} }
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
return errors.InternalServerError("go.micro.proxy.grpc", "SendRequest is unsupported")
}
// ServeRequest honours the server.Proxy interface // ServeRequest honours the server.Proxy interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
// set default client // set default client

View File

@ -10,6 +10,7 @@ import (
"net/url" "net/url"
"path" "path"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/config/options" "github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/errors" "github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy" "github.com/micro/go-micro/proxy"
@ -44,6 +45,10 @@ func getEndpoint(hdr map[string]string) string {
return "" return ""
} }
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
return errors.InternalServerError("go.micro.proxy.http", "SendRequest is unsupported")
}
// ServeRequest honours the server.Router interface // ServeRequest honours the server.Router interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
if p.Endpoint == "" { if p.Endpoint == "" {

View File

@ -5,13 +5,16 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"sort"
"strings" "strings"
"sync" "sync"
"time"
"github.com/micro/go-micro/client" "github.com/micro/go-micro/client"
"github.com/micro/go-micro/codec" "github.com/micro/go-micro/codec"
"github.com/micro/go-micro/codec/bytes" "github.com/micro/go-micro/codec/bytes"
"github.com/micro/go-micro/config/options" "github.com/micro/go-micro/config/options"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/proxy" "github.com/micro/go-micro/proxy"
"github.com/micro/go-micro/router" "github.com/micro/go-micro/router"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
@ -26,9 +29,12 @@ type Proxy struct {
// Endpoint specifies the fixed service endpoint to call. // Endpoint specifies the fixed service endpoint to call.
Endpoint string Endpoint string
// The client to use for outbound requests // The client to use for outbound requests in the local network
Client client.Client Client client.Client
// Links are used for outbound requests not in the local network
Links map[string]client.Client
// The router for routes // The router for routes
Router router.Router Router router.Router
@ -76,7 +82,7 @@ func readLoop(r server.Request, s client.Stream) error {
} }
// toNodes returns a list of node addresses from given routes // toNodes returns a list of node addresses from given routes
func toNodes(routes map[uint64]router.Route) []string { func toNodes(routes []router.Route) []string {
var nodes []string var nodes []string
for _, node := range routes { for _, node := range routes {
address := node.Address address := node.Address
@ -88,37 +94,63 @@ func toNodes(routes map[uint64]router.Route) []string {
return nodes return nodes
} }
func (p *Proxy) getRoute(service string) ([]string, error) { func (p *Proxy) getLink(r router.Route) (client.Client, error) {
if r.Link == "local" || len(p.Links) == 0 {
return p.Client, nil
}
l, ok := p.Links[r.Link]
if !ok {
return nil, errors.InternalServerError("go.micro.proxy", "link not found")
}
return l, nil
}
func (p *Proxy) getRoute(service string) ([]router.Route, error) {
toSlice := func(r map[uint64]router.Route) []router.Route {
var routes []router.Route
for _, v := range r {
routes = append(routes, v)
}
// sort the routes in order of metric
sort.Slice(routes, func(i, j int) bool { return routes[i].Metric < routes[j].Metric })
return routes
}
// lookup the route cache first // lookup the route cache first
p.Lock() p.Lock()
routes, ok := p.Routes[service] routes, ok := p.Routes[service]
if ok { if ok {
p.Unlock() p.Unlock()
return toNodes(routes), nil return toSlice(routes), nil
} }
p.Routes[service] = make(map[uint64]router.Route)
p.Unlock() 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 // lookup the routes in the router
results, err := p.Router.Lookup(router.NewQuery(router.QueryService(service))) results, err := p.Router.Lookup(router.NewQuery(router.QueryService(service)))
if err != nil { 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 return nil, err
} }
// update the proxy cache // update the proxy cache
p.Lock() p.Lock()
for _, route := range results { for _, route := range results {
// create if does not exist
if _, ok := p.Routes[service]; !ok {
p.Routes[service] = make(map[uint64]router.Route)
}
p.Routes[service][route.Hash()] = route p.Routes[service][route.Hash()] = route
} }
routes = p.Routes[service] routes = p.Routes[service]
p.Unlock() p.Unlock()
return toNodes(routes), nil return toSlice(routes), nil
} }
// manageRouteCache applies action on a given route to Proxy route cache // manageRouteCache applies action on a given route to Proxy route cache
@ -130,9 +162,6 @@ func (p *Proxy) manageRouteCache(route router.Route, action string) error {
} }
p.Routes[route.Service][route.Hash()] = route p.Routes[route.Service][route.Hash()] = route
case "delete": case "delete":
if _, ok := p.Routes[route.Service]; !ok {
return fmt.Errorf("route not found")
}
delete(p.Routes[route.Service], route.Hash()) delete(p.Routes[route.Service], route.Hash())
default: default:
return fmt.Errorf("unknown action: %s", action) return fmt.Errorf("unknown action: %s", action)
@ -171,12 +200,31 @@ func (p *Proxy) watchRoutes() {
} }
} }
func (p *Proxy) SendRequest(ctx context.Context, req client.Request, rsp client.Response) error {
return errors.InternalServerError("go.micro.proxy", "SendRequest is unsupported")
}
// ServeRequest honours the server.Router interface // ServeRequest honours the server.Router interface
func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
// service name // determine if its local routing
service := req.Service() var local bool
endpoint := req.Endpoint() // address to call
var addresses []string var addresses []string
// routes
var routes []router.Route
// service name to call
service := req.Service()
// endpoint to call
endpoint := req.Endpoint()
if len(service) == 0 {
return errors.BadRequest("go.micro.proxy", "service name is blank")
}
// are we network routing or local routing
if len(p.Links) == 0 {
local = true
}
// call a specific backend endpoint either by name or address // call a specific backend endpoint either by name or address
if len(p.Endpoint) > 0 { if len(p.Endpoint) > 0 {
@ -190,7 +238,7 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
return err return err
} }
// set the address // set the address
addresses = addr routes = addr
// set the name // set the name
service = p.Endpoint service = p.Endpoint
} }
@ -201,16 +249,66 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
if err != nil { if err != nil {
return err return err
} }
addresses = addr routes = addr
} }
// if the address is already set just serve it
// TODO: figure it out if we should know to pick a link
if len(addresses) > 0 {
// serve the normal way
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, client.WithAddress(addresses...))
}
// there's no links e.g we're local routing then just serve it with addresses
if local {
var opts []client.CallOption var opts []client.CallOption
// set address if available // set address if available via routes or specific endpoint
if len(addresses) > 0 { if len(routes) > 0 {
addresses := toNodes(routes)
opts = append(opts, client.WithAddress(addresses...)) opts = append(opts, client.WithAddress(addresses...))
} }
// serve the normal way
return p.serveRequest(ctx, p.Client, service, endpoint, req, rsp, opts...)
}
var gerr error
// we're routing globally with multiple links
// so we need to pick a link per route
for _, route := range routes {
// pick the link or error out
link, err := p.getLink(route)
if err != nil {
// ok let's try again
gerr = err
continue
}
// set the address to call
addresses := toNodes([]router.Route{route})
// do the request with the link
gerr = p.serveRequest(ctx, link, service, endpoint, req, rsp, client.WithAddress(addresses...))
// return on no error since we succeeded
if gerr == nil {
return nil
}
// return where the context deadline was exceeded
if gerr == context.Canceled || gerr == context.DeadlineExceeded {
return err
}
// otherwise attempt to do it all over again
}
// if we got here something went really badly wrong
return gerr
}
func (p *Proxy) serveRequest(ctx context.Context, link client.Client, service, endpoint string, req server.Request, rsp server.Response, opts ...client.CallOption) error {
// read initial request // read initial request
body, err := req.Read() body, err := req.Read()
if err != nil { if err != nil {
@ -218,16 +316,33 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
} }
// create new request with raw bytes body // create new request with raw bytes body
creq := p.Client.NewRequest(service, endpoint, &bytes.Frame{body}, client.WithContentType(req.ContentType())) creq := link.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 := link.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 // create new stream
stream, err := p.Client.Stream(ctx, creq, opts...) stream, err := link.Stream(ctx, creq, opts...)
if err != nil { if err != nil {
return err return err
} }
defer stream.Close() defer stream.Close()
// create client request read loop // create client request read loop if streaming
go readLoop(req, stream) go readLoop(req, stream)
// get raw response // get raw response
@ -283,6 +398,7 @@ func NewSingleHostProxy(endpoint string) *Proxy {
// NewProxy returns a new proxy which will route based on mucp headers // NewProxy returns a new proxy which will route based on mucp headers
func NewProxy(opts ...options.Option) proxy.Proxy { func NewProxy(opts ...options.Option) proxy.Proxy {
p := new(Proxy) p := new(Proxy)
p.Links = map[string]client.Client{}
p.Options = options.NewOptions(opts...) p.Options = options.NewOptions(opts...)
p.Options.Init(options.WithString("mucp")) p.Options.Init(options.WithString("mucp"))
@ -303,6 +419,12 @@ func NewProxy(opts ...options.Option) proxy.Proxy {
p.Client = client.DefaultClient p.Client = client.DefaultClient
} }
// get client
links, ok := p.Options.Values().Get("proxy.links")
if ok {
p.Links = links.(map[string]client.Client)
}
// get router // get router
r, ok := p.Options.Values().Get("proxy.router") r, ok := p.Options.Values().Get("proxy.router")
if ok { if ok {
@ -319,7 +441,16 @@ func NewProxy(opts ...options.Option) proxy.Proxy {
// watch router service routes // watch router service routes
p.errChan = make(chan error, 1) p.errChan = make(chan error, 1)
go p.watchRoutes()
go func() {
// continuously attempt to watch routes
for {
// watch the routes
p.watchRoutes()
// in case of failure just wait a second
time.Sleep(time.Second)
}
}()
return p return p
} }

View File

@ -13,6 +13,8 @@ import (
// Proxy can be used as a proxy server for go-micro services // Proxy can be used as a proxy server for go-micro services
type Proxy interface { type Proxy interface {
options.Options options.Options
// SendRequest honours the client.Router interface
SendRequest(context.Context, client.Request, client.Response) error
// ServeRequest honours the server.Router interface // ServeRequest honours the server.Router interface
ServeRequest(context.Context, server.Request, server.Response) error ServeRequest(context.Context, server.Request, server.Response) error
} }
@ -35,3 +37,20 @@ func WithClient(c client.Client) options.Option {
func WithRouter(r router.Router) options.Option { func WithRouter(r router.Router) options.Option {
return options.WithValue("proxy.router", r) return options.WithValue("proxy.router", r)
} }
// WithLink sets a link for outbound requests
func WithLink(name string, c client.Client) options.Option {
return func(o *options.Values) error {
var links map[string]client.Client
v, ok := o.Get("proxy.links")
if ok {
links = v.(map[string]client.Client)
} else {
links = map[string]client.Client{}
}
links[name] = c
// save the links
o.Set("proxy.links", links)
return nil
}
}

View File

@ -36,7 +36,13 @@ type cache struct {
ttls map[string]time.Time ttls map[string]time.Time
watched map[string]bool watched map[string]bool
// used to stop the cache
exit chan bool exit chan bool
// status of the registry
// used to hold onto the cache
// in failure state
status error
} }
var ( var (
@ -50,6 +56,18 @@ func backoff(attempts int) time.Duration {
return time.Duration(math.Pow(10, float64(attempts))) * time.Millisecond return time.Duration(math.Pow(10, float64(attempts))) * time.Millisecond
} }
func (c *cache) getStatus() error {
c.RLock()
defer c.RUnlock()
return c.status
}
func (c *cache) setStatus(err error) {
c.Lock()
c.status = err
c.Unlock()
}
// isValid checks if the service is valid // isValid checks if the service is valid
func (c *cache) isValid(services []*registry.Service, ttl time.Time) bool { func (c *cache) isValid(services []*registry.Service, ttl time.Time) bool {
// no services exist // no services exist
@ -81,6 +99,11 @@ func (c *cache) quit() bool {
} }
func (c *cache) del(service string) { func (c *cache) del(service string) {
// don't blow away cache in error state
if err := c.getStatus(); err != nil {
return
}
// otherwise delete entries
delete(c.cache, service) delete(c.cache, service)
delete(c.ttls, service) delete(c.ttls, service)
} }
@ -105,13 +128,26 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
} }
// get does the actual request for a service and cache it // get does the actual request for a service and cache it
get := func(service string) ([]*registry.Service, error) { get := func(service string, cached []*registry.Service) ([]*registry.Service, error) {
// ask the registry // ask the registry
services, err := c.Registry.GetService(service) services, err := c.Registry.GetService(service)
if err != nil { if err != nil {
// check the cache
if len(cached) > 0 {
// set the error status
c.setStatus(err)
// return the stale cache
return registry.Copy(cached), nil
}
// otherwise return error
return nil, err return nil, err
} }
// reset the status
if c.getStatus(); err != nil {
c.setStatus(nil)
}
// cache results // cache results
c.Lock() c.Lock()
c.set(service, registry.Copy(services)) c.set(service, registry.Copy(services))
@ -129,7 +165,7 @@ func (c *cache) get(service string) ([]*registry.Service, error) {
c.RUnlock() c.RUnlock()
// get and return services // get and return services
return get(service) return get(service, services)
} }
func (c *cache) set(service string, services []*registry.Service) { func (c *cache) set(service string, services []*registry.Service) {
@ -283,6 +319,7 @@ func (c *cache) run(service string) {
} }
d := backoff(a) d := backoff(a)
c.setStatus(err)
if a > 3 { if a > 3 {
log.Log("rcache: ", err, " backing off ", d) log.Log("rcache: ", err, " backing off ", d)
@ -305,6 +342,7 @@ func (c *cache) run(service string) {
} }
d := backoff(b) d := backoff(b)
c.setStatus(err)
if b > 3 { if b > 3 {
log.Log("rcache: ", err, " backing off ", d) log.Log("rcache: ", err, " backing off ", d)
@ -348,6 +386,13 @@ func (c *cache) watch(w registry.Watcher) error {
close(stop) close(stop)
return err return err
} }
// reset the error status since we succeeded
if err := c.getStatus(); err != nil {
// reset status
c.setStatus(nil)
}
c.update(res) c.update(res)
} }
} }

View File

@ -13,6 +13,7 @@ import (
consul "github.com/hashicorp/consul/api" consul "github.com/hashicorp/consul/api"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
mnet "github.com/micro/go-micro/util/net"
hash "github.com/mitchellh/hashstructure" hash "github.com/mitchellh/hashstructure"
) )
@ -250,6 +251,9 @@ func (c *consulRegistry) Register(s *registry.Service, opts ...registry.Register
} }
host, pt, _ := net.SplitHostPort(node.Address) host, pt, _ := net.SplitHostPort(node.Address)
if host == "" {
host = node.Address
}
port, _ := strconv.Atoi(pt) port, _ := strconv.Atoi(pt)
// register the service // register the service
@ -351,7 +355,7 @@ func (c *consulRegistry) GetService(name string) ([]*registry.Service, error) {
svc.Nodes = append(svc.Nodes, &registry.Node{ svc.Nodes = append(svc.Nodes, &registry.Node{
Id: id, Id: id,
Address: fmt.Sprintf("%s:%d", address, s.Service.Port), Address: mnet.HostPort(address, s.Service.Port),
Metadata: decodeMetadata(s.Service.Tags), Metadata: decodeMetadata(s.Service.Tags),
}) })
} }

View File

@ -163,7 +163,7 @@ func (cw *consulWatcher) serviceHandler(idx uint64, data interface{}) {
// it's an update rather than creation // it's an update rather than creation
if len(nodes) > 0 { if len(nodes) > 0 {
delService := oldService delService := registry.CopyService(oldService)
delService.Nodes = nodes delService.Nodes = nodes
cw.next <- &registry.Result{Action: "delete", Service: delService} cw.next <- &registry.Result{Action: "delete", Service: delService}
} }

View File

@ -40,7 +40,7 @@ func TestGossipRegistryBroadcast(t *testing.T) {
r1 := newRegistry(Config(mc1), Address("127.0.0.1:54321")) r1 := newRegistry(Config(mc1), Address("127.0.0.1:54321"))
mc2 := newMemberlistConfig() mc2 := newMemberlistConfig()
r2 := newRegistry(Config(mc2), Address("127.0.0.2:54321"), registry.Addrs("127.0.0.1:54321")) r2 := newRegistry(Config(mc2), Address("127.0.0.1:54322"), registry.Addrs("127.0.0.1:54321"))
defer r1.(*gossipRegistry).Stop() defer r1.(*gossipRegistry).Stop()
defer r2.(*gossipRegistry).Stop() defer r2.(*gossipRegistry).Stop()
@ -100,7 +100,7 @@ func TestGossipRegistryRetry(t *testing.T) {
r1 := newRegistry(Config(mc1), Address("127.0.0.1:54321")) r1 := newRegistry(Config(mc1), Address("127.0.0.1:54321"))
mc2 := newMemberlistConfig() mc2 := newMemberlistConfig()
r2 := newRegistry(Config(mc2), Address("127.0.0.2:54321"), registry.Addrs("127.0.0.1:54321")) r2 := newRegistry(Config(mc2), Address("127.0.0.1:54322"), registry.Addrs("127.0.0.1:54321"))
defer r1.(*gossipRegistry).Stop() defer r1.(*gossipRegistry).Stop()
defer r2.(*gossipRegistry).Stop() defer r2.(*gossipRegistry).Stop()

View File

@ -0,0 +1,76 @@
package handler
import (
"context"
"github.com/micro/go-micro/errors"
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
"github.com/micro/go-micro/registry/service"
)
type Registry struct {
// internal registry
Registry registry.Registry
}
func (r *Registry) GetService(ctx context.Context, req *pb.GetRequest, rsp *pb.GetResponse) error {
services, err := r.Registry.GetService(req.Service)
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
for _, srv := range services {
rsp.Services = append(rsp.Services, service.ToProto(srv))
}
return nil
}
func (r *Registry) Register(ctx context.Context, req *pb.Service, rsp *pb.EmptyResponse) error {
err := r.Registry.Register(service.ToService(req))
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
return nil
}
func (r *Registry) Deregister(ctx context.Context, req *pb.Service, rsp *pb.EmptyResponse) error {
err := r.Registry.Deregister(service.ToService(req))
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
return nil
}
func (r *Registry) ListServices(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
services, err := r.Registry.ListServices()
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
for _, srv := range services {
rsp.Services = append(rsp.Services, service.ToProto(srv))
}
return nil
}
func (r *Registry) Watch(ctx context.Context, req *pb.WatchRequest, rsp pb.Registry_WatchStream) error {
watcher, err := r.Registry.Watch(registry.WatchService(req.Service))
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
for {
next, err := watcher.Next()
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
err = rsp.Send(&pb.Result{
Action: next.Action,
Service: service.ToProto(next.Service),
})
if err != nil {
return errors.InternalServerError("go.micro.registry", err.Error())
}
}
return nil
}

View File

@ -2,6 +2,8 @@
package mdns package mdns
import ( import (
"context"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
) )
@ -9,3 +11,13 @@ import (
func NewRegistry(opts ...registry.Option) registry.Registry { func NewRegistry(opts ...registry.Option) registry.Registry {
return registry.NewRegistry(opts...) return registry.NewRegistry(opts...)
} }
// Domain sets the mdnsDomain
func Domain(d string) registry.Option {
return func(o *registry.Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, "mdns.domain", d)
}
}

View File

@ -11,7 +11,11 @@ import (
"time" "time"
"github.com/micro/mdns" "github.com/micro/mdns"
hash "github.com/mitchellh/hashstructure" )
var (
// use a .micro domain rather than .local
mdnsDomain = "micro"
) )
type mdnsTxt struct { type mdnsTxt struct {
@ -22,13 +26,14 @@ type mdnsTxt struct {
} }
type mdnsEntry struct { type mdnsEntry struct {
hash uint64
id string id string
node *mdns.Server node *mdns.Server
} }
type mdnsRegistry struct { type mdnsRegistry struct {
opts Options opts Options
// the mdns domain
domain string
sync.Mutex sync.Mutex
services map[string][]*mdnsEntry services map[string][]*mdnsEntry
@ -36,11 +41,25 @@ type mdnsRegistry struct {
func newRegistry(opts ...Option) Registry { func newRegistry(opts ...Option) Registry {
options := Options{ options := Options{
Context: context.Background(),
Timeout: time.Millisecond * 100, Timeout: time.Millisecond * 100,
} }
for _, o := range opts {
o(&options)
}
// set the domain
domain := mdnsDomain
d, ok := options.Context.Value("mdns.domain").(string)
if ok {
domain = d
}
return &mdnsRegistry{ return &mdnsRegistry{
opts: options, opts: options,
domain: domain,
services: make(map[string][]*mdnsEntry), services: make(map[string][]*mdnsEntry),
} }
} }
@ -66,7 +85,7 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error
s, err := mdns.NewMDNSService( s, err := mdns.NewMDNSService(
service.Name, service.Name,
"_services", "_services",
"", m.domain+".",
"", "",
9999, 9999,
[]net.IP{net.ParseIP("0.0.0.0")}, []net.IP{net.ParseIP("0.0.0.0")},
@ -88,13 +107,6 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error
var gerr error var gerr error
for _, node := range service.Nodes { for _, node := range service.Nodes {
// create hash of service; uint64
h, err := hash.Hash(node, nil)
if err != nil {
gerr = err
continue
}
var seen bool var seen bool
var e *mdnsEntry var e *mdnsEntry
@ -107,14 +119,11 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error
} }
// already registered, continue // already registered, continue
if seen && e.hash == h { if seen {
continue continue
// hash doesn't match, shutdown
} else if seen {
e.node.Shutdown()
// doesn't exist // doesn't exist
} else { } else {
e = &mdnsEntry{hash: h} e = &mdnsEntry{}
} }
txt, err := encode(&mdnsTxt{ txt, err := encode(&mdnsTxt{
@ -141,7 +150,7 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error
s, err := mdns.NewMDNSService( s, err := mdns.NewMDNSService(
node.Id, node.Id,
service.Name, service.Name,
"", m.domain+".",
"", "",
port, port,
[]net.IP{net.ParseIP(host)}, []net.IP{net.ParseIP(host)},
@ -214,6 +223,8 @@ func (m *mdnsRegistry) GetService(service string) ([]*Service, error) {
p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout) p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout)
// set entries channel // set entries channel
p.Entries = entries p.Entries = entries
// set the domain
p.Domain = m.domain
go func() { go func() {
for { for {
@ -223,7 +234,9 @@ func (m *mdnsRegistry) GetService(service string) ([]*Service, error) {
if p.Service == "_services" { if p.Service == "_services" {
continue continue
} }
if p.Domain != m.domain {
continue
}
if e.TTL == 0 { if e.TTL == 0 {
continue continue
} }
@ -288,6 +301,8 @@ func (m *mdnsRegistry) ListServices() ([]*Service, error) {
p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout) p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout)
// set entries channel // set entries channel
p.Entries = entries p.Entries = entries
// set domain
p.Domain = m.domain
var services []*Service var services []*Service
@ -298,7 +313,9 @@ func (m *mdnsRegistry) ListServices() ([]*Service, error) {
if e.TTL == 0 { if e.TTL == 0 {
continue continue
} }
if !strings.HasSuffix(e.Name, p.Domain+".") {
continue
}
name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".") name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".")
if !serviceMap[name] { if !serviceMap[name] {
serviceMap[name] = true serviceMap[name] = true
@ -332,6 +349,7 @@ func (m *mdnsRegistry) Watch(opts ...WatchOption) (Watcher, error) {
wo: wo, wo: wo,
ch: make(chan *mdns.ServiceEntry, 32), ch: make(chan *mdns.ServiceEntry, 32),
exit: make(chan struct{}), exit: make(chan struct{}),
domain: m.domain,
} }
go func() { go func() {

View File

@ -11,6 +11,8 @@ type mdnsWatcher struct {
wo WatchOptions wo WatchOptions
ch chan *mdns.ServiceEntry ch chan *mdns.ServiceEntry
exit chan struct{} exit chan struct{}
// the mdns domain
domain string
} }
func (m *mdnsWatcher) Next() (*Result, error) { func (m *mdnsWatcher) Next() (*Result, error) {
@ -46,13 +48,14 @@ func (m *mdnsWatcher) Next() (*Result, error) {
Endpoints: txt.Endpoints, Endpoints: txt.Endpoints,
} }
// TODO: don't hardcode .local. // skip anything without the domain we care about
if !strings.HasSuffix(e.Name, "."+service.Name+".local.") { suffix := fmt.Sprintf(".%s.%s.", service.Name, m.domain)
if !strings.HasSuffix(e.Name, suffix) {
continue continue
} }
service.Nodes = append(service.Nodes, &Node{ service.Nodes = append(service.Nodes, &Node{
Id: strings.TrimSuffix(e.Name, "."+service.Name+".local."), Id: strings.TrimSuffix(e.Name, suffix),
Address: fmt.Sprintf("%s:%d", e.AddrV4.String(), e.Port), Address: fmt.Sprintf("%s:%d", e.AddrV4.String(), e.Port),
Metadata: txt.Metadata, Metadata: txt.Metadata,
}) })

View File

@ -10,6 +10,10 @@ import (
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
) )
var (
timeout = time.Millisecond * 10
)
type Registry struct { type Registry struct {
options registry.Options options registry.Options
@ -18,11 +22,28 @@ type Registry struct {
Watchers map[string]*Watcher Watchers map[string]*Watcher
} }
var ( func NewRegistry(opts ...registry.Option) registry.Registry {
timeout = time.Millisecond * 10 options := registry.Options{
) Context: context.Background(),
}
func (m *Registry) watch(r *registry.Result) { for _, o := range opts {
o(&options)
}
services := getServices(options.Context)
if services == nil {
services = make(map[string][]*registry.Service)
}
return &Registry{
options: options,
Services: services,
Watchers: make(map[string]*Watcher),
}
}
func (m *Registry) sendEvent(r *registry.Result) {
var watchers []*Watcher var watchers []*Watcher
m.RLock() m.RLock()
@ -87,30 +108,55 @@ func (m *Registry) ListServices() ([]*registry.Service, error) {
} }
func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error { func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
go m.watch(&registry.Result{Action: "update", Service: s})
m.Lock() m.Lock()
defer m.Unlock()
if service, ok := m.Services[s.Name]; !ok { if service, ok := m.Services[s.Name]; !ok {
m.Services[s.Name] = []*registry.Service{s} m.Services[s.Name] = []*registry.Service{s}
go m.sendEvent(&registry.Result{Action: "update", Service: s})
} else { } else {
m.Services[s.Name] = registry.Merge(service, []*registry.Service{s}) svcCount := len(service)
svcNodeCounts := make(map[string]map[string]int)
for _, s := range service {
if _, ok := svcNodeCounts[s.Name]; !ok {
svcNodeCounts[s.Name] = make(map[string]int)
}
if _, ok := svcNodeCounts[s.Name][s.Version]; !ok {
svcNodeCounts[s.Name][s.Version] = len(s.Nodes)
}
}
// if merged count and original service counts changed we added new version of the service
merged := registry.Merge(service, []*registry.Service{s})
if len(merged) != svcCount {
m.Services[s.Name] = merged
go m.sendEvent(&registry.Result{Action: "update", Service: s})
return nil
}
// if the node count for a particular service has changed we added a new node to the service
for _, s := range merged {
if len(s.Nodes) != svcNodeCounts[s.Name][s.Version] {
m.Services[s.Name] = merged
go m.sendEvent(&registry.Result{Action: "update", Service: s})
return nil
}
}
} }
m.Unlock()
return nil return nil
} }
func (m *Registry) Deregister(s *registry.Service) error { func (m *Registry) Deregister(s *registry.Service) error {
go m.watch(&registry.Result{Action: "delete", Service: s})
m.Lock() m.Lock()
defer m.Unlock()
if service, ok := m.Services[s.Name]; ok { if service, ok := m.Services[s.Name]; ok {
go m.sendEvent(&registry.Result{Action: "delete", Service: s})
if service := registry.Remove(service, []*registry.Service{s}); len(service) == 0 { if service := registry.Remove(service, []*registry.Service{s}); len(service) == 0 {
delete(m.Services, s.Name) delete(m.Services, s.Name)
} else { } else {
m.Services[s.Name] = service m.Services[s.Name] = service
} }
} }
m.Unlock()
return nil return nil
} }
@ -137,24 +183,3 @@ func (m *Registry) Watch(opts ...registry.WatchOption) (registry.Watcher, error)
func (m *Registry) String() string { func (m *Registry) String() string {
return "memory" return "memory"
} }
func NewRegistry(opts ...registry.Option) registry.Registry {
options := registry.Options{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
services := getServices(options.Context)
if services == nil {
services = make(map[string][]*registry.Service)
}
return &Registry{
options: options,
Services: services,
Watchers: make(map[string]*Watcher),
}
}

View File

@ -0,0 +1,224 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: registry.proto
package go_micro_registry
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 Registry service
type RegistryService interface {
GetService(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error)
Register(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error)
Deregister(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error)
ListServices(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Registry_WatchService, error)
}
type registryService struct {
c client.Client
name string
}
func NewRegistryService(name string, c client.Client) RegistryService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.registry"
}
return &registryService{
c: c,
name: name,
}
}
func (c *registryService) GetService(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error) {
req := c.c.NewRequest(c.name, "Registry.GetService", in)
out := new(GetResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) Register(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) {
req := c.c.NewRequest(c.name, "Registry.Register", in)
out := new(EmptyResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) Deregister(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) {
req := c.c.NewRequest(c.name, "Registry.Deregister", in)
out := new(EmptyResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) ListServices(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
req := c.c.NewRequest(c.name, "Registry.ListServices", in)
out := new(ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryService) Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Registry_WatchService, error) {
req := c.c.NewRequest(c.name, "Registry.Watch", &WatchRequest{})
stream, err := c.c.Stream(ctx, req, opts...)
if err != nil {
return nil, err
}
if err := stream.Send(in); err != nil {
return nil, err
}
return &registryServiceWatch{stream}, nil
}
type Registry_WatchService interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Recv() (*Result, error)
}
type registryServiceWatch struct {
stream client.Stream
}
func (x *registryServiceWatch) Close() error {
return x.stream.Close()
}
func (x *registryServiceWatch) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *registryServiceWatch) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *registryServiceWatch) Recv() (*Result, error) {
m := new(Result)
err := x.stream.Recv(m)
if err != nil {
return nil, err
}
return m, nil
}
// Server API for Registry service
type RegistryHandler interface {
GetService(context.Context, *GetRequest, *GetResponse) error
Register(context.Context, *Service, *EmptyResponse) error
Deregister(context.Context, *Service, *EmptyResponse) error
ListServices(context.Context, *ListRequest, *ListResponse) error
Watch(context.Context, *WatchRequest, Registry_WatchStream) error
}
func RegisterRegistryHandler(s server.Server, hdlr RegistryHandler, opts ...server.HandlerOption) error {
type registry interface {
GetService(ctx context.Context, in *GetRequest, out *GetResponse) error
Register(ctx context.Context, in *Service, out *EmptyResponse) error
Deregister(ctx context.Context, in *Service, out *EmptyResponse) error
ListServices(ctx context.Context, in *ListRequest, out *ListResponse) error
Watch(ctx context.Context, stream server.Stream) error
}
type Registry struct {
registry
}
h := &registryHandler{hdlr}
return s.Handle(s.NewHandler(&Registry{h}, opts...))
}
type registryHandler struct {
RegistryHandler
}
func (h *registryHandler) GetService(ctx context.Context, in *GetRequest, out *GetResponse) error {
return h.RegistryHandler.GetService(ctx, in, out)
}
func (h *registryHandler) Register(ctx context.Context, in *Service, out *EmptyResponse) error {
return h.RegistryHandler.Register(ctx, in, out)
}
func (h *registryHandler) Deregister(ctx context.Context, in *Service, out *EmptyResponse) error {
return h.RegistryHandler.Deregister(ctx, in, out)
}
func (h *registryHandler) ListServices(ctx context.Context, in *ListRequest, out *ListResponse) error {
return h.RegistryHandler.ListServices(ctx, in, out)
}
func (h *registryHandler) Watch(ctx context.Context, stream server.Stream) error {
m := new(WatchRequest)
if err := stream.Recv(m); err != nil {
return err
}
return h.RegistryHandler.Watch(ctx, m, &registryWatchStream{stream})
}
type Registry_WatchStream interface {
SendMsg(interface{}) error
RecvMsg(interface{}) error
Close() error
Send(*Result) error
}
type registryWatchStream struct {
stream server.Stream
}
func (x *registryWatchStream) Close() error {
return x.stream.Close()
}
func (x *registryWatchStream) SendMsg(m interface{}) error {
return x.stream.Send(m)
}
func (x *registryWatchStream) RecvMsg(m interface{}) error {
return x.stream.Recv(m)
}
func (x *registryWatchStream) Send(m *Result) error {
return x.stream.Send(m)
}

View File

@ -0,0 +1,714 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: registry.proto
package go_micro_registry
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
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
// EventType defines the type of event
type EventType int32
const (
EventType_Create EventType = 0
EventType_Delete EventType = 1
EventType_Update EventType = 2
)
var EventType_name = map[int32]string{
0: "Create",
1: "Delete",
2: "Update",
}
var EventType_value = map[string]int32{
"Create": 0,
"Delete": 1,
"Update": 2,
}
func (x EventType) String() string {
return proto.EnumName(EventType_name, int32(x))
}
func (EventType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{0}
}
// Service represents a go-micro service
type Service struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Endpoints []*Endpoint `protobuf:"bytes,4,rep,name=endpoints,proto3" json:"endpoints,omitempty"`
Nodes []*Node `protobuf:"bytes,5,rep,name=nodes,proto3" json:"nodes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Service) Reset() { *m = Service{} }
func (m *Service) String() string { return proto.CompactTextString(m) }
func (*Service) ProtoMessage() {}
func (*Service) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{0}
}
func (m *Service) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Service.Unmarshal(m, b)
}
func (m *Service) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Service.Marshal(b, m, deterministic)
}
func (m *Service) XXX_Merge(src proto.Message) {
xxx_messageInfo_Service.Merge(m, src)
}
func (m *Service) XXX_Size() int {
return xxx_messageInfo_Service.Size(m)
}
func (m *Service) XXX_DiscardUnknown() {
xxx_messageInfo_Service.DiscardUnknown(m)
}
var xxx_messageInfo_Service proto.InternalMessageInfo
func (m *Service) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Service) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Service) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
func (m *Service) GetEndpoints() []*Endpoint {
if m != nil {
return m.Endpoints
}
return nil
}
func (m *Service) GetNodes() []*Node {
if m != nil {
return m.Nodes
}
return nil
}
// Node represents the node the service is on
type Node struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
Port int64 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Node) Reset() { *m = Node{} }
func (m *Node) String() string { return proto.CompactTextString(m) }
func (*Node) ProtoMessage() {}
func (*Node) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{1}
}
func (m *Node) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Node.Unmarshal(m, b)
}
func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Node.Marshal(b, m, deterministic)
}
func (m *Node) XXX_Merge(src proto.Message) {
xxx_messageInfo_Node.Merge(m, src)
}
func (m *Node) XXX_Size() int {
return xxx_messageInfo_Node.Size(m)
}
func (m *Node) XXX_DiscardUnknown() {
xxx_messageInfo_Node.DiscardUnknown(m)
}
var xxx_messageInfo_Node proto.InternalMessageInfo
func (m *Node) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Node) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
func (m *Node) GetPort() int64 {
if m != nil {
return m.Port
}
return 0
}
func (m *Node) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
// Endpoint is a endpoint provided by a service
type Endpoint struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Request *Value `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"`
Response *Value `protobuf:"bytes,3,opt,name=response,proto3" json:"response,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Endpoint) Reset() { *m = Endpoint{} }
func (m *Endpoint) String() string { return proto.CompactTextString(m) }
func (*Endpoint) ProtoMessage() {}
func (*Endpoint) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{2}
}
func (m *Endpoint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Endpoint.Unmarshal(m, b)
}
func (m *Endpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Endpoint.Marshal(b, m, deterministic)
}
func (m *Endpoint) XXX_Merge(src proto.Message) {
xxx_messageInfo_Endpoint.Merge(m, src)
}
func (m *Endpoint) XXX_Size() int {
return xxx_messageInfo_Endpoint.Size(m)
}
func (m *Endpoint) XXX_DiscardUnknown() {
xxx_messageInfo_Endpoint.DiscardUnknown(m)
}
var xxx_messageInfo_Endpoint proto.InternalMessageInfo
func (m *Endpoint) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Endpoint) GetRequest() *Value {
if m != nil {
return m.Request
}
return nil
}
func (m *Endpoint) GetResponse() *Value {
if m != nil {
return m.Response
}
return nil
}
func (m *Endpoint) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
// Value is an opaque value for a request or response
type Value struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Values []*Value `protobuf:"bytes,3,rep,name=values,proto3" json:"values,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Value) Reset() { *m = Value{} }
func (m *Value) String() string { return proto.CompactTextString(m) }
func (*Value) ProtoMessage() {}
func (*Value) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{3}
}
func (m *Value) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Value.Unmarshal(m, b)
}
func (m *Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Value.Marshal(b, m, deterministic)
}
func (m *Value) XXX_Merge(src proto.Message) {
xxx_messageInfo_Value.Merge(m, src)
}
func (m *Value) XXX_Size() int {
return xxx_messageInfo_Value.Size(m)
}
func (m *Value) XXX_DiscardUnknown() {
xxx_messageInfo_Value.DiscardUnknown(m)
}
var xxx_messageInfo_Value proto.InternalMessageInfo
func (m *Value) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Value) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *Value) GetValues() []*Value {
if m != nil {
return m.Values
}
return nil
}
// Result is returns by the watcher
type Result struct {
Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"`
Service *Service `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"`
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Result) Reset() { *m = Result{} }
func (m *Result) String() string { return proto.CompactTextString(m) }
func (*Result) ProtoMessage() {}
func (*Result) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{4}
}
func (m *Result) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Result.Unmarshal(m, b)
}
func (m *Result) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Result.Marshal(b, m, deterministic)
}
func (m *Result) XXX_Merge(src proto.Message) {
xxx_messageInfo_Result.Merge(m, src)
}
func (m *Result) XXX_Size() int {
return xxx_messageInfo_Result.Size(m)
}
func (m *Result) XXX_DiscardUnknown() {
xxx_messageInfo_Result.DiscardUnknown(m)
}
var xxx_messageInfo_Result proto.InternalMessageInfo
func (m *Result) GetAction() string {
if m != nil {
return m.Action
}
return ""
}
func (m *Result) GetService() *Service {
if m != nil {
return m.Service
}
return nil
}
func (m *Result) GetTimestamp() int64 {
if m != nil {
return m.Timestamp
}
return 0
}
type EmptyResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
func (*EmptyResponse) ProtoMessage() {}
func (*EmptyResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{5}
}
func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EmptyResponse.Unmarshal(m, b)
}
func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
}
func (m *EmptyResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_EmptyResponse.Merge(m, src)
}
func (m *EmptyResponse) XXX_Size() int {
return xxx_messageInfo_EmptyResponse.Size(m)
}
func (m *EmptyResponse) XXX_DiscardUnknown() {
xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
}
var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
type GetRequest struct {
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetRequest) Reset() { *m = GetRequest{} }
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
func (*GetRequest) ProtoMessage() {}
func (*GetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{6}
}
func (m *GetRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetRequest.Unmarshal(m, b)
}
func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic)
}
func (m *GetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetRequest.Merge(m, src)
}
func (m *GetRequest) XXX_Size() int {
return xxx_messageInfo_GetRequest.Size(m)
}
func (m *GetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetRequest proto.InternalMessageInfo
func (m *GetRequest) GetService() string {
if m != nil {
return m.Service
}
return ""
}
type GetResponse struct {
Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetResponse) Reset() { *m = GetResponse{} }
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
func (*GetResponse) ProtoMessage() {}
func (*GetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{7}
}
func (m *GetResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetResponse.Unmarshal(m, b)
}
func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic)
}
func (m *GetResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetResponse.Merge(m, src)
}
func (m *GetResponse) XXX_Size() int {
return xxx_messageInfo_GetResponse.Size(m)
}
func (m *GetResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetResponse proto.InternalMessageInfo
func (m *GetResponse) GetServices() []*Service {
if m != nil {
return m.Services
}
return nil
}
type ListRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListRequest) Reset() { *m = ListRequest{} }
func (m *ListRequest) String() string { return proto.CompactTextString(m) }
func (*ListRequest) ProtoMessage() {}
func (*ListRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{8}
}
func (m *ListRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListRequest.Unmarshal(m, b)
}
func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic)
}
func (m *ListRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListRequest.Merge(m, src)
}
func (m *ListRequest) XXX_Size() int {
return xxx_messageInfo_ListRequest.Size(m)
}
func (m *ListRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ListRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ListRequest proto.InternalMessageInfo
type ListResponse struct {
Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{9}
}
func (m *ListResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListResponse.Unmarshal(m, b)
}
func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic)
}
func (m *ListResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ListResponse.Merge(m, src)
}
func (m *ListResponse) XXX_Size() int {
return xxx_messageInfo_ListResponse.Size(m)
}
func (m *ListResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ListResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ListResponse proto.InternalMessageInfo
func (m *ListResponse) GetServices() []*Service {
if m != nil {
return m.Services
}
return nil
}
type WatchRequest struct {
// service is optional
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WatchRequest) Reset() { *m = WatchRequest{} }
func (m *WatchRequest) String() string { return proto.CompactTextString(m) }
func (*WatchRequest) ProtoMessage() {}
func (*WatchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{10}
}
func (m *WatchRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WatchRequest.Unmarshal(m, b)
}
func (m *WatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_WatchRequest.Marshal(b, m, deterministic)
}
func (m *WatchRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_WatchRequest.Merge(m, src)
}
func (m *WatchRequest) XXX_Size() int {
return xxx_messageInfo_WatchRequest.Size(m)
}
func (m *WatchRequest) XXX_DiscardUnknown() {
xxx_messageInfo_WatchRequest.DiscardUnknown(m)
}
var xxx_messageInfo_WatchRequest proto.InternalMessageInfo
func (m *WatchRequest) GetService() string {
if m != nil {
return m.Service
}
return ""
}
// Event is registry event
type Event struct {
// Event Id
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// type of event
Type EventType `protobuf:"varint,2,opt,name=type,proto3,enum=go.micro.registry.EventType" json:"type,omitempty"`
// unix timestamp of event
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// service entry
Service *Service `protobuf:"bytes,4,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_41af05d40a615591, []int{11}
}
func (m *Event) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Event.Unmarshal(m, b)
}
func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Event.Marshal(b, m, deterministic)
}
func (m *Event) XXX_Merge(src proto.Message) {
xxx_messageInfo_Event.Merge(m, src)
}
func (m *Event) XXX_Size() int {
return xxx_messageInfo_Event.Size(m)
}
func (m *Event) XXX_DiscardUnknown() {
xxx_messageInfo_Event.DiscardUnknown(m)
}
var xxx_messageInfo_Event proto.InternalMessageInfo
func (m *Event) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Event) GetType() EventType {
if m != nil {
return m.Type
}
return EventType_Create
}
func (m *Event) GetTimestamp() int64 {
if m != nil {
return m.Timestamp
}
return 0
}
func (m *Event) GetService() *Service {
if m != nil {
return m.Service
}
return nil
}
func init() {
proto.RegisterEnum("go.micro.registry.EventType", EventType_name, EventType_value)
proto.RegisterType((*Service)(nil), "go.micro.registry.Service")
proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Service.MetadataEntry")
proto.RegisterType((*Node)(nil), "go.micro.registry.Node")
proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Node.MetadataEntry")
proto.RegisterType((*Endpoint)(nil), "go.micro.registry.Endpoint")
proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Endpoint.MetadataEntry")
proto.RegisterType((*Value)(nil), "go.micro.registry.Value")
proto.RegisterType((*Result)(nil), "go.micro.registry.Result")
proto.RegisterType((*EmptyResponse)(nil), "go.micro.registry.EmptyResponse")
proto.RegisterType((*GetRequest)(nil), "go.micro.registry.GetRequest")
proto.RegisterType((*GetResponse)(nil), "go.micro.registry.GetResponse")
proto.RegisterType((*ListRequest)(nil), "go.micro.registry.ListRequest")
proto.RegisterType((*ListResponse)(nil), "go.micro.registry.ListResponse")
proto.RegisterType((*WatchRequest)(nil), "go.micro.registry.WatchRequest")
proto.RegisterType((*Event)(nil), "go.micro.registry.Event")
}
func init() { proto.RegisterFile("registry.proto", fileDescriptor_41af05d40a615591) }
var fileDescriptor_41af05d40a615591 = []byte{
// 632 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdb, 0x6e, 0xd3, 0x4c,
0x10, 0x8e, 0xed, 0x38, 0x87, 0x49, 0xdb, 0xbf, 0xff, 0x08, 0x81, 0x31, 0x05, 0x22, 0x4b, 0xa0,
0x80, 0x84, 0xa9, 0x42, 0x85, 0x38, 0x5c, 0x21, 0x62, 0x2a, 0xa1, 0x16, 0x09, 0x73, 0xba, 0x36,
0xf1, 0xa8, 0x58, 0xc4, 0x07, 0x76, 0x37, 0x91, 0xfc, 0x0e, 0x48, 0x3c, 0x01, 0x77, 0x3c, 0x0a,
0x0f, 0x86, 0xbc, 0x5e, 0x27, 0xa9, 0x62, 0x07, 0xa4, 0xc2, 0xdd, 0x8c, 0xf7, 0x9b, 0x6f, 0x67,
0xbe, 0x6f, 0x36, 0x81, 0x3d, 0x46, 0x67, 0x11, 0x17, 0x2c, 0x77, 0x33, 0x96, 0x8a, 0x14, 0xff,
0x3f, 0x4b, 0xdd, 0x38, 0x9a, 0xb2, 0xd4, 0xad, 0x0e, 0x9c, 0x1f, 0x3a, 0x74, 0xdf, 0x10, 0x5b,
0x44, 0x53, 0x42, 0x84, 0x76, 0x12, 0xc4, 0x64, 0x69, 0x43, 0x6d, 0xd4, 0xf7, 0x65, 0x8c, 0x16,
0x74, 0x17, 0xc4, 0x78, 0x94, 0x26, 0x96, 0x2e, 0x3f, 0x57, 0x29, 0x4e, 0xa0, 0x17, 0x93, 0x08,
0xc2, 0x40, 0x04, 0x96, 0x31, 0x34, 0x46, 0x83, 0xf1, 0xc8, 0xdd, 0xe0, 0x77, 0x15, 0xb7, 0x7b,
0xaa, 0xa0, 0x5e, 0x22, 0x58, 0xee, 0x2f, 0x2b, 0xf1, 0x31, 0xf4, 0x29, 0x09, 0xb3, 0x34, 0x4a,
0x04, 0xb7, 0xda, 0x92, 0xe6, 0x5a, 0x0d, 0x8d, 0xa7, 0x30, 0xfe, 0x0a, 0x8d, 0xf7, 0xc0, 0x4c,
0xd2, 0x90, 0xb8, 0x65, 0xca, 0xb2, 0x2b, 0x35, 0x65, 0xaf, 0xd2, 0x90, 0xfc, 0x12, 0x65, 0x3f,
0x85, 0xdd, 0x73, 0x4d, 0xe0, 0x3e, 0x18, 0x9f, 0x29, 0x57, 0xd3, 0x16, 0x21, 0x5e, 0x02, 0x73,
0x11, 0xcc, 0xe6, 0xa4, 0x46, 0x2d, 0x93, 0x27, 0xfa, 0x23, 0xcd, 0xf9, 0xa9, 0x41, 0xbb, 0x20,
0xc3, 0x3d, 0xd0, 0xa3, 0x50, 0xd5, 0xe8, 0x51, 0x58, 0xe8, 0x13, 0x84, 0x21, 0x23, 0xce, 0x2b,
0x7d, 0x54, 0x5a, 0xa8, 0x99, 0xa5, 0x4c, 0x58, 0xc6, 0x50, 0x1b, 0x19, 0xbe, 0x8c, 0xf1, 0xd9,
0x9a, 0x66, 0xe5, 0xb0, 0xb7, 0x1a, 0xba, 0x6e, 0x12, 0xec, 0x62, 0x63, 0x7c, 0xd5, 0xa1, 0x57,
0x49, 0x59, 0x6b, 0xf7, 0x18, 0xba, 0x8c, 0xbe, 0xcc, 0x89, 0x0b, 0x59, 0x3c, 0x18, 0x5b, 0x35,
0xfd, 0xbd, 0x2f, 0xf8, 0xfc, 0x0a, 0x88, 0x47, 0xd0, 0x63, 0xc4, 0xb3, 0x34, 0xe1, 0x24, 0x87,
0xdd, 0x56, 0xb4, 0x44, 0xa2, 0xb7, 0x21, 0xc5, 0x9d, 0x2d, 0xbe, 0xff, 0x1b, 0x39, 0x02, 0x30,
0x65, 0x5b, 0xb5, 0x52, 0x20, 0xb4, 0x45, 0x9e, 0x55, 0x55, 0x32, 0xc6, 0x43, 0xe8, 0xc8, 0x6a,
0xae, 0x36, 0xbe, 0x79, 0x50, 0x85, 0x73, 0x04, 0x74, 0x7c, 0xe2, 0xf3, 0x99, 0xc0, 0xcb, 0xd0,
0x09, 0xa6, 0xa2, 0x78, 0x48, 0xe5, 0x2d, 0x2a, 0xc3, 0x23, 0xe8, 0xf2, 0xf2, 0x91, 0x28, 0xc9,
0xed, 0xe6, 0x67, 0xe4, 0x57, 0x50, 0x3c, 0x80, 0xbe, 0x88, 0x62, 0xe2, 0x22, 0x88, 0x33, 0xb5,
0x62, 0xab, 0x0f, 0xce, 0x7f, 0xb0, 0xeb, 0xc5, 0x99, 0xc8, 0x7d, 0xa5, 0xb6, 0x73, 0x1b, 0xe0,
0x98, 0x84, 0xaf, 0x1c, 0xb3, 0x56, 0x57, 0x96, 0xbd, 0x54, 0xa9, 0xe3, 0xc1, 0x40, 0xe2, 0x94,
0x49, 0x0f, 0xa1, 0xa7, 0x4e, 0xb8, 0xa5, 0xc9, 0x89, 0xb7, 0x35, 0xb7, 0xc4, 0x3a, 0xbb, 0x30,
0x38, 0x89, 0x78, 0x75, 0x9f, 0xf3, 0x02, 0x76, 0xca, 0xf4, 0x82, 0xb4, 0x23, 0xd8, 0xf9, 0x10,
0x88, 0xe9, 0xa7, 0xdf, 0xcf, 0xf1, 0x5d, 0x03, 0xd3, 0x5b, 0x50, 0x22, 0x36, 0x1e, 0xec, 0xe1,
0x9a, 0xad, 0x7b, 0xe3, 0x83, 0xba, 0x9d, 0x2b, 0xea, 0xde, 0xe6, 0x19, 0x29, 0xd3, 0xb7, 0x4a,
0xbd, 0x6e, 0x5f, 0xfb, 0x8f, 0xed, 0xbb, 0x7b, 0x1f, 0xfa, 0xcb, 0x6b, 0x10, 0xa0, 0xf3, 0x9c,
0x51, 0x20, 0x68, 0xbf, 0x55, 0xc4, 0x13, 0x9a, 0x91, 0xa0, 0x7d, 0xad, 0x88, 0xdf, 0x65, 0x61,
0xf1, 0x5d, 0x1f, 0x7f, 0x33, 0xa0, 0xe7, 0x2b, 0x3a, 0x3c, 0x95, 0x6e, 0x56, 0x3f, 0xdb, 0xd7,
0x6b, 0x2e, 0x5c, 0x99, 0x6d, 0xdf, 0x68, 0x3a, 0x56, 0xab, 0xd1, 0xc2, 0x97, 0x15, 0x35, 0x31,
0xdc, 0xd2, 0xbd, 0x3d, 0xac, 0x13, 0xeb, 0xdc, 0x9a, 0xb5, 0xf0, 0x04, 0x60, 0x42, 0xec, 0x6f,
0xb1, 0xbd, 0x2e, 0x17, 0x47, 0x95, 0x70, 0xac, 0x9b, 0x65, 0x6d, 0xd1, 0xec, 0x9b, 0x8d, 0xe7,
0x4b, 0xca, 0x63, 0x30, 0xe5, 0x0e, 0x61, 0x1d, 0x76, 0x7d, 0xbb, 0xec, 0xab, 0x35, 0x80, 0xf2,
0x2d, 0x3b, 0xad, 0x43, 0xed, 0x63, 0x47, 0xfe, 0xa7, 0x3e, 0xf8, 0x15, 0x00, 0x00, 0xff, 0xff,
0x08, 0x0e, 0xe4, 0xae, 0x65, 0x07, 0x00, 0x00,
}

View File

@ -0,0 +1,92 @@
syntax = "proto3";
package go.micro.registry;
service Registry {
rpc GetService(GetRequest) returns (GetResponse) {};
rpc Register(Service) returns (EmptyResponse) {};
rpc Deregister(Service) returns (EmptyResponse) {};
rpc ListServices(ListRequest) returns (ListResponse) {};
rpc Watch(WatchRequest) returns (stream Result) {};
}
// Service represents a go-micro service
message Service {
string name = 1;
string version = 2;
map<string,string> metadata = 3;
repeated Endpoint endpoints = 4;
repeated Node nodes = 5;
}
// Node represents the node the service is on
message Node {
string id = 1;
string address = 2;
int64 port = 3;
map<string,string> metadata = 4;
}
// Endpoint is a endpoint provided by a service
message Endpoint {
string name = 1;
Value request = 2;
Value response = 3;
map<string, string> metadata = 4;
}
// Value is an opaque value for a request or response
message Value {
string name = 1;
string type = 2;
repeated Value values = 3;
}
// Result is returns by the watcher
message Result {
string action = 1; // create, update, delete
Service service = 2;
int64 timestamp = 3; // unix timestamp
}
message EmptyResponse {}
message GetRequest {
string service = 1;
}
message GetResponse {
repeated Service services = 1;
}
message ListRequest {
// TODO: filtering
}
message ListResponse {
repeated Service services = 1;
}
message WatchRequest {
// service is optional
string service = 1;
}
// EventType defines the type of event
enum EventType {
Create = 0;
Delete = 1;
Update = 2;
}
// Event is registry event
message Event {
// Event Id
string id = 1;
// type of event
EventType type = 2;
// unix timestamp of event
int64 timestamp = 3;
// service entry
Service service = 4;
}

View File

@ -5,6 +5,15 @@ import (
"errors" "errors"
) )
var (
DefaultRegistry = NewRegistry()
// Not found error when GetService is called
ErrNotFound = errors.New("service not found")
// Watcher stopped error when watcher is stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// The registry provides an interface for service discovery // The registry provides an interface for service discovery
// and an abstraction over varying implementations // and an abstraction over varying implementations
// {consul, etcd, zookeeper, ...} // {consul, etcd, zookeeper, ...}
@ -25,15 +34,6 @@ type RegisterOption func(*RegisterOptions)
type WatchOption func(*WatchOptions) type WatchOption func(*WatchOptions)
var (
DefaultRegistry = NewRegistry()
// Not found error when GetService is called
ErrNotFound = errors.New("not found")
// Watcher stopped error when watcher is stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// Register a service node. Additionally supply options such as TTL. // Register a service node. Additionally supply options such as TTL.
func Register(s *Service, opts ...RegisterOption) error { func Register(s *Service, opts ...RegisterOption) error {
return DefaultRegistry.Register(s, opts...) return DefaultRegistry.Register(s, opts...)

162
registry/service/service.go Normal file
View File

@ -0,0 +1,162 @@
// Package service uses the registry service
package service
import (
"context"
"time"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
)
var (
// The default service name
DefaultService = "go.micro.service"
)
type serviceRegistry struct {
opts registry.Options
// name of the registry
name string
// address
address []string
// client to call registry
client pb.RegistryService
}
func (s *serviceRegistry) callOpts() []client.CallOption {
var opts []client.CallOption
// set registry address
if len(s.address) > 0 {
opts = append(opts, client.WithAddress(s.address...))
}
// set timeout
if s.opts.Timeout > time.Duration(0) {
opts = append(opts, client.WithRequestTimeout(s.opts.Timeout))
}
return opts
}
func (s *serviceRegistry) Init(opts ...registry.Option) error {
for _, o := range opts {
o(&s.opts)
}
return nil
}
func (s *serviceRegistry) Options() registry.Options {
return s.opts
}
func (s *serviceRegistry) Register(srv *registry.Service, opts ...registry.RegisterOption) error {
var options registry.RegisterOptions
for _, o := range opts {
o(&options)
}
// register the service
_, err := s.client.Register(context.TODO(), ToProto(srv), s.callOpts()...)
if err != nil {
return err
}
return nil
}
func (s *serviceRegistry) Deregister(srv *registry.Service) error {
// deregister the service
_, err := s.client.Deregister(context.TODO(), ToProto(srv), s.callOpts()...)
if err != nil {
return err
}
return nil
}
func (s *serviceRegistry) GetService(name string) ([]*registry.Service, error) {
rsp, err := s.client.GetService(context.TODO(), &pb.GetRequest{
Service: name,
}, s.callOpts()...)
if err != nil {
return nil, err
}
var services []*registry.Service
for _, service := range rsp.Services {
services = append(services, ToService(service))
}
return services, nil
}
func (s *serviceRegistry) ListServices() ([]*registry.Service, error) {
rsp, err := s.client.ListServices(context.TODO(), &pb.ListRequest{}, s.callOpts()...)
if err != nil {
return nil, err
}
var services []*registry.Service
for _, service := range rsp.Services {
services = append(services, ToService(service))
}
return services, nil
}
func (s *serviceRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
var options registry.WatchOptions
for _, o := range opts {
o(&options)
}
stream, err := s.client.Watch(context.TODO(), &pb.WatchRequest{
Service: options.Service,
}, s.callOpts()...)
if err != nil {
return nil, err
}
return newWatcher(stream), nil
}
func (s *serviceRegistry) String() string {
return s.name
}
// NewRegistry returns a new registry service client
func NewRegistry(opts ...registry.Option) registry.Registry {
var options registry.Options
for _, o := range opts {
o(&options)
}
// the registry address
addrs := options.Addrs
if len(addrs) == 0 {
addrs = []string{"127.0.0.1:8000"}
}
// use mdns as a fall back in case its used
mReg := registry.NewRegistry()
// create new client with mdns
cli := client.NewClient(
client.Registry(mReg),
)
// service name
// TODO: accept option
name := DefaultService
return &serviceRegistry{
opts: options,
name: name,
address: addrs,
client: pb.NewRegistryService(name, cli),
}
}

133
registry/service/util.go Normal file
View File

@ -0,0 +1,133 @@
package service
import (
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
)
func values(v []*registry.Value) []*pb.Value {
if len(v) == 0 {
return []*pb.Value{}
}
var vs []*pb.Value
for _, vi := range v {
vs = append(vs, &pb.Value{
Name: vi.Name,
Type: vi.Type,
Values: values(vi.Values),
})
}
return vs
}
func toValues(v []*pb.Value) []*registry.Value {
if len(v) == 0 {
return []*registry.Value{}
}
var vs []*registry.Value
for _, vi := range v {
vs = append(vs, &registry.Value{
Name: vi.Name,
Type: vi.Type,
Values: toValues(vi.Values),
})
}
return vs
}
func ToProto(s *registry.Service) *pb.Service {
var endpoints []*pb.Endpoint
for _, ep := range s.Endpoints {
var request, response *pb.Value
if ep.Request != nil {
request = &pb.Value{
Name: ep.Request.Name,
Type: ep.Request.Type,
Values: values(ep.Request.Values),
}
}
if ep.Response != nil {
response = &pb.Value{
Name: ep.Response.Name,
Type: ep.Response.Type,
Values: values(ep.Response.Values),
}
}
endpoints = append(endpoints, &pb.Endpoint{
Name: ep.Name,
Request: request,
Response: response,
Metadata: ep.Metadata,
})
}
var nodes []*pb.Node
for _, node := range s.Nodes {
nodes = append(nodes, &pb.Node{
Id: node.Id,
Address: node.Address,
Metadata: node.Metadata,
})
}
return &pb.Service{
Name: s.Name,
Version: s.Version,
Metadata: s.Metadata,
Endpoints: endpoints,
Nodes: nodes,
}
}
func ToService(s *pb.Service) *registry.Service {
var endpoints []*registry.Endpoint
for _, ep := range s.Endpoints {
var request, response *registry.Value
if ep.Request != nil {
request = &registry.Value{
Name: ep.Request.Name,
Type: ep.Request.Type,
Values: toValues(ep.Request.Values),
}
}
if ep.Response != nil {
response = &registry.Value{
Name: ep.Response.Name,
Type: ep.Response.Type,
Values: toValues(ep.Response.Values),
}
}
endpoints = append(endpoints, &registry.Endpoint{
Name: ep.Name,
Request: request,
Response: response,
Metadata: ep.Metadata,
})
}
var nodes []*registry.Node
for _, node := range s.Nodes {
nodes = append(nodes, &registry.Node{
Id: node.Id,
Address: node.Address,
Metadata: node.Metadata,
})
}
return &registry.Service{
Name: s.Name,
Version: s.Version,
Metadata: s.Metadata,
Endpoints: endpoints,
Nodes: nodes,
}
}

View File

@ -0,0 +1,49 @@
package service
import (
"github.com/micro/go-micro/registry"
pb "github.com/micro/go-micro/registry/proto"
)
type serviceWatcher struct {
stream pb.Registry_WatchService
closed chan bool
}
func (s *serviceWatcher) Next() (*registry.Result, error) {
for {
// check if closed
select {
case <-s.closed:
return nil, registry.ErrWatcherStopped
default:
}
r, err := s.stream.Recv()
if err != nil {
return nil, err
}
return &registry.Result{
Action: r.Action,
Service: ToService(r.Service),
}, nil
}
}
func (s *serviceWatcher) Stop() {
select {
case <-s.closed:
return
default:
close(s.closed)
s.stream.Close()
}
}
func newWatcher(stream pb.Registry_WatchService) registry.Watcher {
return &serviceWatcher{
stream: stream,
closed: make(chan bool),
}
}

View File

@ -48,10 +48,8 @@ func delNodes(old, del []*Node) []*Node {
return nodes return nodes
} }
// Copy makes a copy of services // CopyService make a copy of service
func Copy(current []*Service) []*Service { func CopyService(service *Service) *Service {
services := make([]*Service, len(current))
for i, service := range current {
// copy service // copy service
s := new(Service) s := new(Service)
*s = *service *s = *service
@ -73,11 +71,15 @@ func Copy(current []*Service) []*Service {
eps[j] = e eps[j] = e
} }
s.Endpoints = eps s.Endpoints = eps
return s
}
// append service // Copy makes a copy of services
services[i] = s func Copy(current []*Service) []*Service {
services := make([]*Service, len(current))
for i, service := range current {
services[i] = CopyService(service)
} }
return services return services
} }

View File

@ -1,5 +1,7 @@
package registry package registry
import "time"
// Watcher is an interface that returns updates // Watcher is an interface that returns updates
// about services within the registry. // about services within the registry.
type Watcher interface { type Watcher interface {
@ -14,3 +16,41 @@ type Result struct {
Action string Action string
Service *Service Service *Service
} }
// EventType defines registry event type
type EventType int
const (
// Create is emitted when a new service is registered
Create EventType = iota
// Delete is emitted when an existing service is deregsitered
Delete
// Update is emitted when an existing servicec is updated
Update
)
// String returns human readable event type
func (t EventType) String() string {
switch t {
case Create:
return "create"
case Delete:
return "delete"
case Update:
return "update"
default:
return "unknown"
}
}
// Event is registry event
type Event struct {
// Id is registry id
Id string
// Type defines type of event
Type EventType
// Timestamp is event timestamp
Timestamp time.Time
// Service is registry service
Service *Service
}

View File

@ -10,6 +10,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/util/log"
) )
const ( const (
@ -43,10 +44,9 @@ var (
// router implements default router // router implements default router
type router struct { type router struct {
sync.RWMutex sync.RWMutex
// embed the table options Options
table *table
opts Options
status Status status Status
table *table
exit chan struct{} exit chan struct{}
errChan chan error errChan chan error
eventChan chan *Event eventChan chan *Event
@ -67,33 +67,41 @@ func newRouter(opts ...Option) Router {
o(&options) o(&options)
} }
r := &router{ // set initial status to Stopped
status := Status{Code: Stopped, Error: nil}
return &router{
options: options,
status: status,
table: newTable(), table: newTable(),
opts: options,
status: Status{Code: Stopped, Error: nil},
advertWg: &sync.WaitGroup{}, advertWg: &sync.WaitGroup{},
wg: &sync.WaitGroup{}, wg: &sync.WaitGroup{},
subscribers: make(map[string]chan *Advert), subscribers: make(map[string]chan *Advert),
} }
go r.run()
return r
} }
// Init initializes router with given options // Init initializes router with given options
func (r *router) Init(opts ...Option) error { func (r *router) Init(opts ...Option) error {
r.Lock()
defer r.Unlock()
for _, o := range opts { for _, o := range opts {
o(&r.opts) o(&r.options)
} }
return nil return nil
} }
// Options returns router options // Options returns router options
func (r *router) Options() Options { func (r *router) Options() Options {
return r.opts r.Lock()
options := r.options
r.Unlock()
return options
} }
// Table returns routing table
func (r *router) Table() Table { func (r *router) Table() Table {
return r.table return r.table
} }
@ -105,16 +113,19 @@ func (r *router) manageRoute(route Route, action string) error {
if err := r.table.Create(route); err != nil && err != ErrDuplicateRoute { if err := r.table.Create(route); err != nil && err != ErrDuplicateRoute {
return fmt.Errorf("failed adding route for service %s: %s", route.Service, err) return fmt.Errorf("failed adding route for service %s: %s", route.Service, err)
} }
case "update":
if err := r.table.Update(route); err != nil && err != ErrDuplicateRoute {
return fmt.Errorf("failed updating route for service %s: %s", route.Service, err)
}
case "delete": case "delete":
if err := r.table.Delete(route); err != nil && err != ErrRouteNotFound { if err := r.table.Delete(route); err != nil && err != ErrRouteNotFound {
return fmt.Errorf("failed deleting route for service %s: %s", route.Service, err) return fmt.Errorf("failed deleting route for service %s: %s", route.Service, err)
} }
case "update":
if err := r.table.Update(route); err != nil {
return fmt.Errorf("failed updating route for service %s: %s", route.Service, err)
}
case "solicit":
// nothing to do here
return nil
default: default:
return fmt.Errorf("failed to manage route for service %s. Unknown action: %s", route.Service, action) return fmt.Errorf("failed to manage route for service %s: unknown action %s", route.Service, action)
} }
return nil return nil
@ -132,7 +143,8 @@ func (r *router) manageServiceRoutes(service *registry.Service, action string) e
Service: service.Name, Service: service.Name,
Address: node.Address, Address: node.Address,
Gateway: "", Gateway: "",
Network: r.opts.Network, Network: r.options.Network,
Router: r.options.Id,
Link: DefaultLink, Link: DefaultLink,
Metric: DefaultLocalMetric, Metric: DefaultLocalMetric,
} }
@ -174,10 +186,6 @@ func (r *router) manageRegistryRoutes(reg registry.Registry, action string) erro
// watchRegistry watches registry and updates routing table based on the received events. // watchRegistry watches registry and updates routing table based on the received events.
// It returns error if either the registry watcher fails with error or if the routing table update fails. // It returns error if either the registry watcher fails with error or if the routing table update fails.
func (r *router) watchRegistry(w registry.Watcher) error { func (r *router) watchRegistry(w registry.Watcher) error {
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
exit := make(chan bool) exit := make(chan bool)
defer func() { defer func() {
@ -186,6 +194,9 @@ func (r *router) watchRegistry(w registry.Watcher) error {
r.wg.Done() r.wg.Done()
}() }()
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
go func() { go func() {
defer w.Stop() defer w.Stop()
@ -219,9 +230,6 @@ func (r *router) watchRegistry(w registry.Watcher) error {
// watchTable watches routing table entries and either adds or deletes locally registered service to/from network registry // watchTable watches routing table entries and either adds or deletes locally registered service to/from network registry
// It returns error if the locally registered services either fails to be added/deleted to/from network registry. // It returns error if the locally registered services either fails to be added/deleted to/from network registry.
func (r *router) watchTable(w Watcher) error { func (r *router) watchTable(w Watcher) error {
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
exit := make(chan bool) exit := make(chan bool)
defer func() { defer func() {
@ -230,6 +238,9 @@ func (r *router) watchTable(w Watcher) error {
r.wg.Done() r.wg.Done()
}() }()
// wait in the background for the router to stop
// when the router stops, stop the watcher and exit
r.wg.Add(1)
go func() { go func() {
defer w.Stop() defer w.Stop()
@ -272,13 +283,14 @@ func (r *router) publishAdvert(advType AdvertType, events []*Event) {
defer r.advertWg.Done() defer r.advertWg.Done()
a := &Advert{ a := &Advert{
Id: r.opts.Id, Id: r.options.Id,
Type: advType, Type: advType,
TTL: DefaultAdvertTTL, TTL: DefaultAdvertTTL,
Timestamp: time.Now(), Timestamp: time.Now(),
Events: events, Events: events,
} }
log.Debugf("Router publishing advert; %+v", a)
r.RLock() r.RLock()
for _, sub := range r.subscribers { for _, sub := range r.subscribers {
// check the exit chan first // check the exit chan first
@ -325,6 +337,7 @@ func (r *router) advertiseTable() error {
// advertise all routes as Update events to subscribers // advertise all routes as Update events to subscribers
if len(events) > 0 { if len(events) > 0 {
log.Debugf("Router flushing table with %d events: %s", len(events), r.options.Id)
r.advertWg.Add(1) r.advertWg.Add(1)
go r.publishAdvert(RouteUpdate, events) go r.publishAdvert(RouteUpdate, events)
} }
@ -334,9 +347,10 @@ func (r *router) advertiseTable() error {
} }
} }
// routeAdvert contains a list of route events to be advertised // routeAdvert contains a route event to be advertised
type routeAdvert struct { type routeAdvert struct {
events []*Event // event received from routing table
event *Event
// lastUpdate records the time of the last advert update // lastUpdate records the time of the last advert update
lastUpdate time.Time lastUpdate time.Time
// penalty is current advert penalty // penalty is current advert penalty
@ -385,9 +399,11 @@ func (r *router) advertiseEvents() error {
// suppress/recover the event based on its penalty level // suppress/recover the event based on its penalty level
switch { switch {
case advert.penalty > AdvertSuppress && !advert.isSuppressed: case advert.penalty > AdvertSuppress && !advert.isSuppressed:
log.Debugf("Router supressing advert %d for route %s", key, advert.event.Route.Address)
advert.isSuppressed = true advert.isSuppressed = true
advert.suppressTime = time.Now() advert.suppressTime = time.Now()
case advert.penalty < AdvertRecover && advert.isSuppressed: case advert.penalty < AdvertRecover && advert.isSuppressed:
log.Debugf("Router recovering advert %d for route %s", key, advert.event.Route.Address)
advert.isSuppressed = false advert.isSuppressed = false
} }
@ -400,19 +416,18 @@ func (r *router) advertiseEvents() error {
} }
if !advert.isSuppressed { if !advert.isSuppressed {
for _, event := range advert.events {
e := new(Event) e := new(Event)
*e = *event *e = *(advert.event)
events = append(events, e) events = append(events, e)
// delete the advert from the advertMap // delete the advert from the advertMap
delete(advertMap, key) delete(advertMap, key)
} }
} }
}
// advertise all Update events to subscribers // advertise all Update events to subscribers
if len(events) > 0 { if len(events) > 0 {
r.advertWg.Add(1) r.advertWg.Add(1)
log.Debugf("Router publishing %d events", len(events))
go r.publishAdvert(RouteUpdate, events) go r.publishAdvert(RouteUpdate, events)
} }
case e := <-r.eventChan: case e := <-r.eventChan:
@ -420,7 +435,7 @@ func (r *router) advertiseEvents() error {
if e == nil { if e == nil {
continue continue
} }
log.Debugf("Router processing table event %s for service %s", e.Type, e.Route.Address)
// determine the event penalty // determine the event penalty
var penalty float64 var penalty float64
switch e.Type { switch e.Type {
@ -431,13 +446,11 @@ func (r *router) advertiseEvents() error {
} }
// check if we have already registered the route // check if we have already registered the route
// we use the route hash as advertMap key
hash := e.Route.Hash() hash := e.Route.Hash()
advert, ok := advertMap[hash] advert, ok := advertMap[hash]
if !ok { if !ok {
events := []*Event{e}
advert = &routeAdvert{ advert = &routeAdvert{
events: events, event: e,
penalty: penalty, penalty: penalty,
lastUpdate: time.Now(), lastUpdate: time.Now(),
} }
@ -445,17 +458,15 @@ func (r *router) advertiseEvents() error {
continue continue
} }
// attempt to squash last two events if possible // override the route event only if the last event was different
lastEvent := advert.events[len(advert.events)-1] if advert.event.Type != e.Type {
if lastEvent.Type == e.Type { advert.event = e
advert.events[len(advert.events)-1] = e
} else {
advert.events = append(advert.events, e)
} }
// update event penalty and recorded timestamp // update event penalty and timestamp
advert.lastUpdate = time.Now() advert.lastUpdate = time.Now()
advert.penalty += penalty advert.penalty += penalty
log.Debugf("Router advert %d for route %s event penalty: %f", hash, advert.event.Route.Address, advert.penalty)
case <-r.exit: case <-r.exit:
// first wait for the advertiser to finish // first wait for the advertiser to finish
r.advertWg.Wait() r.advertWg.Wait()
@ -464,18 +475,8 @@ func (r *router) advertiseEvents() error {
} }
} }
// watchErrors watches router errors and takes appropriate actions // close closes exit channels
func (r *router) watchErrors() { func (r *router) close() {
var err error
select {
case <-r.exit:
case err = <-r.errChan:
}
r.Lock()
defer r.Unlock()
if r.status.Code != Stopped {
// notify all goroutines to finish // notify all goroutines to finish
close(r.exit) close(r.exit)
@ -484,43 +485,77 @@ func (r *router) watchErrors() {
// drain the event channel // drain the event channel
for range r.eventChan { for range r.eventChan {
} }
// close advert subscribers
for id, sub := range r.subscribers {
// close the channel
close(sub)
// delete the subscriber
delete(r.subscribers, id)
}
} }
// mark the router as Stopped and set its Error to nil // mark the router as Stopped and set its Error to nil
r.status = Status{Code: Stopped, Error: nil} r.status = Status{Code: Stopped, Error: nil}
}
// watchErrors watches router errors and takes appropriate actions
func (r *router) watchErrors() {
var err error
select {
case <-r.exit:
return
case err = <-r.errChan:
} }
r.Lock()
defer r.Unlock()
// if the router is not stopped, stop it
if r.status.Code != Stopped {
// close all the channels
r.close()
// set the status error
if err != nil { if err != nil {
r.status = Status{Code: Error, Error: err} r.status.Error = err
}
} }
} }
// Run runs the router. // Start starts the router
func (r *router) run() { func (r *router) Start() error {
r.Lock() r.Lock()
defer r.Unlock() defer r.Unlock()
switch r.status.Code { // only start if we're stopped
case Stopped, Error: if r.status.Code != Stopped {
return nil
}
// add all local service routes into the routing table // add all local service routes into the routing table
if err := r.manageRegistryRoutes(r.opts.Registry, "create"); err != nil { if err := r.manageRegistryRoutes(r.options.Registry, "create"); err != nil {
r.status = Status{Code: Error, Error: fmt.Errorf("failed adding registry routes: %s", err)} e := fmt.Errorf("failed adding registry routes: %s", err)
return r.status = Status{Code: Error, Error: e}
return e
} }
// add default gateway into routing table // add default gateway into routing table
if r.opts.Gateway != "" { if r.options.Gateway != "" {
// note, the only non-default value is the gateway // note, the only non-default value is the gateway
route := Route{ route := Route{
Service: "*", Service: "*",
Address: "*", Address: "*",
Gateway: r.opts.Gateway, Gateway: r.options.Gateway,
Network: "*", Network: "*",
Router: r.options.Id,
Link: DefaultLink,
Metric: DefaultLocalMetric, Metric: DefaultLocalMetric,
} }
if err := r.table.Create(route); err != nil { if err := r.table.Create(route); err != nil {
r.status = Status{Code: Error, Error: fmt.Errorf("failed adding default gateway route: %s", err)} e := fmt.Errorf("failed adding default gateway route: %s", err)
return r.status = Status{Code: Error, Error: e}
return e
} }
} }
@ -529,10 +564,11 @@ func (r *router) run() {
r.exit = make(chan struct{}) r.exit = make(chan struct{})
// registry watcher // registry watcher
regWatcher, err := r.opts.Registry.Watch() regWatcher, err := r.options.Registry.Watch()
if err != nil { if err != nil {
r.status = Status{Code: Error, Error: fmt.Errorf("failed creating registry watcher: %v", err)} e := fmt.Errorf("failed creating registry watcher: %v", err)
return r.status = Status{Code: Error, Error: e}
return e
} }
r.wg.Add(1) r.wg.Add(1)
@ -551,13 +587,10 @@ func (r *router) run() {
r.watchErrors() r.watchErrors()
}() }()
// mark router as Running and set its Error to nil // mark router as Running
r.status = Status{Code: Running, Error: nil} r.status = Status{Code: Running, Error: nil}
return return nil
}
return
} }
// Advertise stars advertising the routes to the network and returns the advertisements channel to consume from. // Advertise stars advertising the routes to the network and returns the advertisements channel to consume from.
@ -569,24 +602,14 @@ func (r *router) Advertise() (<-chan *Advert, error) {
switch r.status.Code { switch r.status.Code {
case Advertising: case Advertising:
advertChan := make(chan *Advert) advertChan := make(chan *Advert, 128)
r.subscribers[uuid.New().String()] = advertChan r.subscribers[uuid.New().String()] = advertChan
return advertChan, nil return advertChan, nil
case Running: case Running:
// list routing table routes to announce // list all the routes and pack them into even slice to advertise
routes, err := r.table.List() events, err := r.flushRouteEvents(Create)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed listing routes: %s", err) return nil, fmt.Errorf("failed to flush 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 {
event := &Event{
Type: Create,
Timestamp: time.Now(),
Route: route,
}
events[i] = event
} }
// create event channels // create event channels
@ -619,7 +642,7 @@ func (r *router) Advertise() (<-chan *Advert, error) {
r.status = Status{Code: Advertising, Error: nil} r.status = Status{Code: Advertising, Error: nil}
// create advert channel // create advert channel
advertChan := make(chan *Advert) advertChan := make(chan *Advert, 128)
r.subscribers[uuid.New().String()] = advertChan r.subscribers[uuid.New().String()] = advertChan
return advertChan, nil return advertChan, nil
@ -641,10 +664,18 @@ func (r *router) Process(a *Advert) error {
return events[i].Timestamp.Before(events[j].Timestamp) return events[i].Timestamp.Before(events[j].Timestamp)
}) })
log.Debugf("Router %s processing advert from: %s", r.options.Id, a.Id)
for _, event := range events { for _, event := range events {
// skip if the router is the origin of this route
if event.Route.Router == r.options.Id {
log.Debugf("Router skipping processing its own route: %s", r.options.Id)
continue
}
// create a copy of the route // create a copy of the route
route := event.Route route := event.Route
action := event.Type action := event.Type
log.Debugf("Router %s applying %s from router %s for address: %s", r.options.Id, action, route.Router, route.Address)
if err := r.manageRoute(route, fmt.Sprintf("%s", action)); err != nil { if err := r.manageRoute(route, fmt.Sprintf("%s", action)); err != nil {
return fmt.Errorf("failed applying action %s to routing table: %s", action, err) return fmt.Errorf("failed applying action %s to routing table: %s", action, err)
} }
@ -653,10 +684,49 @@ func (r *router) Process(a *Advert) error {
return nil return nil
} }
// flushRouteEvents returns a slice of events, one per each route in the routing table
func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) {
// list all routes
routes, err := r.table.List()
if err != nil {
return nil, fmt.Errorf("failed listing routes: %s", err)
}
// build a list of events to advertise
events := make([]*Event, len(routes))
for i, route := range routes {
event := &Event{
Type: evType,
Timestamp: time.Now(),
Route: route,
}
events[i] = event
}
return events, nil
}
// Solicit advertises all of its routes to the network
// It returns error if the router fails to list the routes
func (r *router) Solicit() error {
events, err := r.flushRouteEvents(Update)
if err != nil {
return fmt.Errorf("failed solicit routes: %s", err)
}
// advertise the routes
r.advertWg.Add(1)
go r.publishAdvert(RouteUpdate, events)
return nil
}
// Lookup routes in the routing table
func (r *router) Lookup(q Query) ([]Route, error) { func (r *router) Lookup(q Query) ([]Route, error) {
return r.table.Query(q) return r.table.Query(q)
} }
// Watch routes
func (r *router) Watch(opts ...WatchOption) (Watcher, error) { func (r *router) Watch(opts ...WatchOption) (Watcher, error) {
return r.table.Watch(opts...) return r.table.Watch(opts...)
} }
@ -675,31 +745,15 @@ func (r *router) Status() Status {
// Stop stops the router // Stop stops the router
func (r *router) Stop() error { func (r *router) Stop() error {
r.Lock() r.Lock()
// only close the channel if the router is running and/or advertising defer r.Unlock()
if r.status.Code == Running || r.status.Code == Advertising {
// notify all goroutines to finish
close(r.exit)
// drain the advertise channel only if advertising switch r.status.Code {
if r.status.Code == Advertising { case Stopped, Error:
// drain the event channel return r.status.Error
for range r.eventChan { case Running, Advertising:
// close all the channels
r.close()
} }
}
// close advert subscribers
for id, sub := range r.subscribers {
// close the channel
close(sub)
// delete the subscriber
delete(r.subscribers, id)
}
// mark the router as Stopped and set its Error to nil
r.status = Status{Code: Stopped, Error: nil}
}
r.Unlock()
// wait for all goroutines to finish // wait for all goroutines to finish
r.wg.Wait() r.wg.Wait()
@ -709,5 +763,5 @@ func (r *router) Stop() error {
// String prints debugging information about router // String prints debugging information about router
func (r *router) String() string { func (r *router) String() string {
return "default" return "memory"
} }

View File

@ -33,6 +33,7 @@ func (r *Router) Lookup(ctx context.Context, req *pb.LookupRequest, resp *pb.Loo
Address: route.Address, Address: route.Address,
Gateway: route.Gateway, Gateway: route.Gateway,
Network: route.Network, Network: route.Network,
Router: route.Router,
Link: route.Link, Link: route.Link,
Metric: int64(route.Metric), Metric: int64(route.Metric),
} }
@ -44,6 +45,16 @@ func (r *Router) Lookup(ctx context.Context, req *pb.LookupRequest, resp *pb.Loo
return nil return nil
} }
// Solicit triggers full routing table advertisement
func (r *Router) Solicit(ctx context.Context, req *pb.Request, resp *pb.Response) error {
if err := r.Router.Solicit(); err != nil {
return err
}
return nil
}
// Advertise streams router advertisements
func (r *Router) Advertise(ctx context.Context, req *pb.Request, stream pb.Router_AdvertiseStream) error { func (r *Router) Advertise(ctx context.Context, req *pb.Request, stream pb.Router_AdvertiseStream) error {
advertChan, err := r.Router.Advertise() advertChan, err := r.Router.Advertise()
if err != nil { if err != nil {
@ -58,6 +69,7 @@ func (r *Router) Advertise(ctx context.Context, req *pb.Request, stream pb.Route
Address: event.Route.Address, Address: event.Route.Address,
Gateway: event.Route.Gateway, Gateway: event.Route.Gateway,
Network: event.Route.Network, Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link, Link: event.Route.Link,
Metric: int64(event.Route.Metric), Metric: int64(event.Route.Metric),
} }
@ -89,6 +101,7 @@ func (r *Router) Advertise(ctx context.Context, req *pb.Request, stream pb.Route
return nil return nil
} }
// Process processes advertisements
func (r *Router) Process(ctx context.Context, req *pb.Advert, rsp *pb.ProcessResponse) error { func (r *Router) Process(ctx context.Context, req *pb.Advert, rsp *pb.ProcessResponse) error {
events := make([]*router.Event, len(req.Events)) events := make([]*router.Event, len(req.Events))
for i, event := range req.Events { for i, event := range req.Events {
@ -97,6 +110,7 @@ func (r *Router) Process(ctx context.Context, req *pb.Advert, rsp *pb.ProcessRes
Address: event.Route.Address, Address: event.Route.Address,
Gateway: event.Route.Gateway, Gateway: event.Route.Gateway,
Network: event.Route.Network, Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link, Link: event.Route.Link,
Metric: int(event.Route.Metric), Metric: int(event.Route.Metric),
} }
@ -123,6 +137,7 @@ func (r *Router) Process(ctx context.Context, req *pb.Advert, rsp *pb.ProcessRes
return nil return nil
} }
// Status returns router status
func (r *Router) Status(ctx context.Context, req *pb.Request, rsp *pb.StatusResponse) error { func (r *Router) Status(ctx context.Context, req *pb.Request, rsp *pb.StatusResponse) error {
status := r.Router.Status() status := r.Router.Status()
@ -149,7 +164,7 @@ func (r *Router) Watch(ctx context.Context, req *pb.WatchRequest, stream pb.Rout
for { for {
event, err := watcher.Next() event, err := watcher.Next()
if err == router.ErrWatcherStopped { if err == router.ErrWatcherStopped {
break return errors.InternalServerError("go.micro.router", "watcher stopped")
} }
if err != nil { if err != nil {
@ -161,6 +176,7 @@ func (r *Router) Watch(ctx context.Context, req *pb.WatchRequest, stream pb.Rout
Address: event.Route.Address, Address: event.Route.Address,
Gateway: event.Route.Gateway, Gateway: event.Route.Gateway,
Network: event.Route.Network, Network: event.Route.Network,
Router: event.Route.Router,
Link: event.Route.Link, Link: event.Route.Link,
Metric: int64(event.Route.Metric), Metric: int64(event.Route.Metric),
} }

View File

@ -18,6 +18,7 @@ func (t *Table) Create(ctx context.Context, route *pb.Route, resp *pb.CreateResp
Address: route.Address, Address: route.Address,
Gateway: route.Gateway, Gateway: route.Gateway,
Network: route.Network, Network: route.Network,
Router: route.Router,
Link: route.Link, Link: route.Link,
Metric: int(route.Metric), Metric: int(route.Metric),
}) })
@ -34,6 +35,7 @@ func (t *Table) Update(ctx context.Context, route *pb.Route, resp *pb.UpdateResp
Address: route.Address, Address: route.Address,
Gateway: route.Gateway, Gateway: route.Gateway,
Network: route.Network, Network: route.Network,
Router: route.Router,
Link: route.Link, Link: route.Link,
Metric: int(route.Metric), Metric: int(route.Metric),
}) })
@ -50,6 +52,7 @@ func (t *Table) Delete(ctx context.Context, route *pb.Route, resp *pb.DeleteResp
Address: route.Address, Address: route.Address,
Gateway: route.Gateway, Gateway: route.Gateway,
Network: route.Network, Network: route.Network,
Router: route.Router,
Link: route.Link, Link: route.Link,
Metric: int(route.Metric), Metric: int(route.Metric),
}) })
@ -74,6 +77,7 @@ func (t *Table) List(ctx context.Context, req *pb.Request, resp *pb.ListResponse
Address: route.Address, Address: route.Address,
Gateway: route.Gateway, Gateway: route.Gateway,
Network: route.Network, Network: route.Network,
Router: route.Router,
Link: route.Link, Link: route.Link,
Metric: int64(route.Metric), Metric: int64(route.Metric),
} }
@ -102,6 +106,7 @@ func (t *Table) Query(ctx context.Context, req *pb.QueryRequest, resp *pb.QueryR
Address: route.Address, Address: route.Address,
Gateway: route.Gateway, Gateway: route.Gateway,
Network: route.Network, Network: route.Network,
Router: route.Router,
Link: route.Link, Link: route.Link,
Metric: int64(route.Metric), Metric: int64(route.Metric),
} }

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT. // Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/router/proto/router.proto // source: router.proto
package go_micro_router package go_micro_router
@ -37,6 +37,7 @@ type RouterService interface {
Lookup(ctx context.Context, in *LookupRequest, opts ...client.CallOption) (*LookupResponse, error) Lookup(ctx context.Context, in *LookupRequest, opts ...client.CallOption) (*LookupResponse, error)
Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Router_WatchService, error) Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Router_WatchService, error)
Advertise(ctx context.Context, in *Request, opts ...client.CallOption) (Router_AdvertiseService, error) Advertise(ctx context.Context, in *Request, opts ...client.CallOption) (Router_AdvertiseService, error)
Solicit(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
Process(ctx context.Context, in *Advert, opts ...client.CallOption) (*ProcessResponse, error) Process(ctx context.Context, in *Advert, opts ...client.CallOption) (*ProcessResponse, error)
Status(ctx context.Context, in *Request, opts ...client.CallOption) (*StatusResponse, error) Status(ctx context.Context, in *Request, opts ...client.CallOption) (*StatusResponse, error)
} }
@ -157,6 +158,16 @@ func (x *routerServiceAdvertise) Recv() (*Advert, error) {
return m, nil return m, nil
} }
func (c *routerService) Solicit(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.name, "Router.Solicit", in)
out := new(Response)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *routerService) Process(ctx context.Context, in *Advert, opts ...client.CallOption) (*ProcessResponse, error) { func (c *routerService) Process(ctx context.Context, in *Advert, opts ...client.CallOption) (*ProcessResponse, error) {
req := c.c.NewRequest(c.name, "Router.Process", in) req := c.c.NewRequest(c.name, "Router.Process", in)
out := new(ProcessResponse) out := new(ProcessResponse)
@ -183,6 +194,7 @@ type RouterHandler interface {
Lookup(context.Context, *LookupRequest, *LookupResponse) error Lookup(context.Context, *LookupRequest, *LookupResponse) error
Watch(context.Context, *WatchRequest, Router_WatchStream) error Watch(context.Context, *WatchRequest, Router_WatchStream) error
Advertise(context.Context, *Request, Router_AdvertiseStream) error Advertise(context.Context, *Request, Router_AdvertiseStream) error
Solicit(context.Context, *Request, *Response) error
Process(context.Context, *Advert, *ProcessResponse) error Process(context.Context, *Advert, *ProcessResponse) error
Status(context.Context, *Request, *StatusResponse) error Status(context.Context, *Request, *StatusResponse) error
} }
@ -192,6 +204,7 @@ func RegisterRouterHandler(s server.Server, hdlr RouterHandler, opts ...server.H
Lookup(ctx context.Context, in *LookupRequest, out *LookupResponse) error Lookup(ctx context.Context, in *LookupRequest, out *LookupResponse) error
Watch(ctx context.Context, stream server.Stream) error Watch(ctx context.Context, stream server.Stream) error
Advertise(ctx context.Context, stream server.Stream) error Advertise(ctx context.Context, stream server.Stream) error
Solicit(ctx context.Context, in *Request, out *Response) error
Process(ctx context.Context, in *Advert, out *ProcessResponse) error Process(ctx context.Context, in *Advert, out *ProcessResponse) error
Status(ctx context.Context, in *Request, out *StatusResponse) error Status(ctx context.Context, in *Request, out *StatusResponse) error
} }
@ -280,6 +293,10 @@ func (x *routerAdvertiseStream) Send(m *Advert) error {
return x.stream.Send(m) return x.stream.Send(m)
} }
func (h *routerHandler) Solicit(ctx context.Context, in *Request, out *Response) error {
return h.RouterHandler.Solicit(ctx, in, out)
}
func (h *routerHandler) Process(ctx context.Context, in *Advert, out *ProcessResponse) error { func (h *routerHandler) Process(ctx context.Context, in *Advert, out *ProcessResponse) error {
return h.RouterHandler.Process(ctx, in, out) return h.RouterHandler.Process(ctx, in, out)
} }

View File

@ -1,13 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/router/proto/router.proto // source: router.proto
package go_micro_router package go_micro_router
import ( import (
context "context"
fmt "fmt" fmt "fmt"
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
math "math" math "math"
) )
@ -45,7 +43,7 @@ func (x AdvertType) String() string {
} }
func (AdvertType) EnumDescriptor() ([]byte, []int) { func (AdvertType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{0} return fileDescriptor_367072455c71aedc, []int{0}
} }
// EventType defines the type of event // EventType defines the type of event
@ -74,7 +72,7 @@ func (x EventType) String() string {
} }
func (EventType) EnumDescriptor() ([]byte, []int) { func (EventType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{1} return fileDescriptor_367072455c71aedc, []int{1}
} }
// Empty request // Empty request
@ -88,7 +86,7 @@ func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) } func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {} func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) { func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{0} return fileDescriptor_367072455c71aedc, []int{0}
} }
func (m *Request) XXX_Unmarshal(b []byte) error { func (m *Request) XXX_Unmarshal(b []byte) error {
@ -109,6 +107,38 @@ func (m *Request) XXX_DiscardUnknown() {
var xxx_messageInfo_Request proto.InternalMessageInfo var xxx_messageInfo_Request proto.InternalMessageInfo
// Empty response
type Response struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_367072455c71aedc, []int{1}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Response.Unmarshal(m, b)
}
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
}
func (m *Response) XXX_Merge(src proto.Message) {
xxx_messageInfo_Response.Merge(m, src)
}
func (m *Response) XXX_Size() int {
return xxx_messageInfo_Response.Size(m)
}
func (m *Response) XXX_DiscardUnknown() {
xxx_messageInfo_Response.DiscardUnknown(m)
}
var xxx_messageInfo_Response proto.InternalMessageInfo
// ListResponse is returned by List // ListResponse is returned by List
type ListResponse struct { type ListResponse struct {
Routes []*Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"` Routes []*Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
@ -121,7 +151,7 @@ func (m *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) } func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {} func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) { func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{1} return fileDescriptor_367072455c71aedc, []int{2}
} }
func (m *ListResponse) XXX_Unmarshal(b []byte) error { func (m *ListResponse) XXX_Unmarshal(b []byte) error {
@ -161,7 +191,7 @@ func (m *LookupRequest) Reset() { *m = LookupRequest{} }
func (m *LookupRequest) String() string { return proto.CompactTextString(m) } func (m *LookupRequest) String() string { return proto.CompactTextString(m) }
func (*LookupRequest) ProtoMessage() {} func (*LookupRequest) ProtoMessage() {}
func (*LookupRequest) Descriptor() ([]byte, []int) { func (*LookupRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{2} return fileDescriptor_367072455c71aedc, []int{3}
} }
func (m *LookupRequest) XXX_Unmarshal(b []byte) error { func (m *LookupRequest) XXX_Unmarshal(b []byte) error {
@ -201,7 +231,7 @@ func (m *LookupResponse) Reset() { *m = LookupResponse{} }
func (m *LookupResponse) String() string { return proto.CompactTextString(m) } func (m *LookupResponse) String() string { return proto.CompactTextString(m) }
func (*LookupResponse) ProtoMessage() {} func (*LookupResponse) ProtoMessage() {}
func (*LookupResponse) Descriptor() ([]byte, []int) { func (*LookupResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{3} return fileDescriptor_367072455c71aedc, []int{4}
} }
func (m *LookupResponse) XXX_Unmarshal(b []byte) error { func (m *LookupResponse) XXX_Unmarshal(b []byte) error {
@ -229,6 +259,7 @@ func (m *LookupResponse) GetRoutes() []*Route {
return nil return nil
} }
// QueryRequest queries Table for Routes
type QueryRequest struct { type QueryRequest struct {
Query *Query `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` Query *Query `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -240,7 +271,7 @@ func (m *QueryRequest) Reset() { *m = QueryRequest{} }
func (m *QueryRequest) String() string { return proto.CompactTextString(m) } func (m *QueryRequest) String() string { return proto.CompactTextString(m) }
func (*QueryRequest) ProtoMessage() {} func (*QueryRequest) ProtoMessage() {}
func (*QueryRequest) Descriptor() ([]byte, []int) { func (*QueryRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{4} return fileDescriptor_367072455c71aedc, []int{5}
} }
func (m *QueryRequest) XXX_Unmarshal(b []byte) error { func (m *QueryRequest) XXX_Unmarshal(b []byte) error {
@ -268,6 +299,7 @@ func (m *QueryRequest) GetQuery() *Query {
return nil return nil
} }
// QueryResponse is returned by Query
type QueryResponse struct { type QueryResponse struct {
Routes []*Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"` Routes []*Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -279,7 +311,7 @@ func (m *QueryResponse) Reset() { *m = QueryResponse{} }
func (m *QueryResponse) String() string { return proto.CompactTextString(m) } func (m *QueryResponse) String() string { return proto.CompactTextString(m) }
func (*QueryResponse) ProtoMessage() {} func (*QueryResponse) ProtoMessage() {}
func (*QueryResponse) Descriptor() ([]byte, []int) { func (*QueryResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{5} return fileDescriptor_367072455c71aedc, []int{6}
} }
func (m *QueryResponse) XXX_Unmarshal(b []byte) error { func (m *QueryResponse) XXX_Unmarshal(b []byte) error {
@ -318,7 +350,7 @@ func (m *WatchRequest) Reset() { *m = WatchRequest{} }
func (m *WatchRequest) String() string { return proto.CompactTextString(m) } func (m *WatchRequest) String() string { return proto.CompactTextString(m) }
func (*WatchRequest) ProtoMessage() {} func (*WatchRequest) ProtoMessage() {}
func (*WatchRequest) Descriptor() ([]byte, []int) { func (*WatchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{6} return fileDescriptor_367072455c71aedc, []int{7}
} }
func (m *WatchRequest) XXX_Unmarshal(b []byte) error { func (m *WatchRequest) XXX_Unmarshal(b []byte) error {
@ -360,7 +392,7 @@ func (m *Advert) Reset() { *m = Advert{} }
func (m *Advert) String() string { return proto.CompactTextString(m) } func (m *Advert) String() string { return proto.CompactTextString(m) }
func (*Advert) ProtoMessage() {} func (*Advert) ProtoMessage() {}
func (*Advert) Descriptor() ([]byte, []int) { func (*Advert) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{7} return fileDescriptor_367072455c71aedc, []int{8}
} }
func (m *Advert) XXX_Unmarshal(b []byte) error { func (m *Advert) XXX_Unmarshal(b []byte) error {
@ -416,6 +448,47 @@ func (m *Advert) GetEvents() []*Event {
return nil return nil
} }
// Solicit solicits routes
type Solicit struct {
// id of the soliciting router
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Solicit) Reset() { *m = Solicit{} }
func (m *Solicit) String() string { return proto.CompactTextString(m) }
func (*Solicit) ProtoMessage() {}
func (*Solicit) Descriptor() ([]byte, []int) {
return fileDescriptor_367072455c71aedc, []int{9}
}
func (m *Solicit) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Solicit.Unmarshal(m, b)
}
func (m *Solicit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Solicit.Marshal(b, m, deterministic)
}
func (m *Solicit) XXX_Merge(src proto.Message) {
xxx_messageInfo_Solicit.Merge(m, src)
}
func (m *Solicit) XXX_Size() int {
return xxx_messageInfo_Solicit.Size(m)
}
func (m *Solicit) XXX_DiscardUnknown() {
xxx_messageInfo_Solicit.DiscardUnknown(m)
}
var xxx_messageInfo_Solicit proto.InternalMessageInfo
func (m *Solicit) GetId() string {
if m != nil {
return m.Id
}
return ""
}
// ProcessResponse is returned by Process // ProcessResponse is returned by Process
type ProcessResponse struct { type ProcessResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -427,7 +500,7 @@ func (m *ProcessResponse) Reset() { *m = ProcessResponse{} }
func (m *ProcessResponse) String() string { return proto.CompactTextString(m) } func (m *ProcessResponse) String() string { return proto.CompactTextString(m) }
func (*ProcessResponse) ProtoMessage() {} func (*ProcessResponse) ProtoMessage() {}
func (*ProcessResponse) Descriptor() ([]byte, []int) { func (*ProcessResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{8} return fileDescriptor_367072455c71aedc, []int{10}
} }
func (m *ProcessResponse) XXX_Unmarshal(b []byte) error { func (m *ProcessResponse) XXX_Unmarshal(b []byte) error {
@ -459,7 +532,7 @@ func (m *CreateResponse) Reset() { *m = CreateResponse{} }
func (m *CreateResponse) String() string { return proto.CompactTextString(m) } func (m *CreateResponse) String() string { return proto.CompactTextString(m) }
func (*CreateResponse) ProtoMessage() {} func (*CreateResponse) ProtoMessage() {}
func (*CreateResponse) Descriptor() ([]byte, []int) { func (*CreateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{9} return fileDescriptor_367072455c71aedc, []int{11}
} }
func (m *CreateResponse) XXX_Unmarshal(b []byte) error { func (m *CreateResponse) XXX_Unmarshal(b []byte) error {
@ -491,7 +564,7 @@ func (m *DeleteResponse) Reset() { *m = DeleteResponse{} }
func (m *DeleteResponse) String() string { return proto.CompactTextString(m) } func (m *DeleteResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteResponse) ProtoMessage() {} func (*DeleteResponse) ProtoMessage() {}
func (*DeleteResponse) Descriptor() ([]byte, []int) { func (*DeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{10} return fileDescriptor_367072455c71aedc, []int{12}
} }
func (m *DeleteResponse) XXX_Unmarshal(b []byte) error { func (m *DeleteResponse) XXX_Unmarshal(b []byte) error {
@ -523,7 +596,7 @@ func (m *UpdateResponse) Reset() { *m = UpdateResponse{} }
func (m *UpdateResponse) String() string { return proto.CompactTextString(m) } func (m *UpdateResponse) String() string { return proto.CompactTextString(m) }
func (*UpdateResponse) ProtoMessage() {} func (*UpdateResponse) ProtoMessage() {}
func (*UpdateResponse) Descriptor() ([]byte, []int) { func (*UpdateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{11} return fileDescriptor_367072455c71aedc, []int{13}
} }
func (m *UpdateResponse) XXX_Unmarshal(b []byte) error { func (m *UpdateResponse) XXX_Unmarshal(b []byte) error {
@ -561,7 +634,7 @@ func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) } func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {} func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) { func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{12} return fileDescriptor_367072455c71aedc, []int{14}
} }
func (m *Event) XXX_Unmarshal(b []byte) error { func (m *Event) XXX_Unmarshal(b []byte) error {
@ -620,7 +693,7 @@ func (m *Query) Reset() { *m = Query{} }
func (m *Query) String() string { return proto.CompactTextString(m) } func (m *Query) String() string { return proto.CompactTextString(m) }
func (*Query) ProtoMessage() {} func (*Query) ProtoMessage() {}
func (*Query) Descriptor() ([]byte, []int) { func (*Query) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{13} return fileDescriptor_367072455c71aedc, []int{15}
} }
func (m *Query) XXX_Unmarshal(b []byte) error { func (m *Query) XXX_Unmarshal(b []byte) error {
@ -672,10 +745,12 @@ type Route struct {
Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"` Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"`
// the network for this destination // the network for this destination
Network string `protobuf:"bytes,4,opt,name=network,proto3" json:"network,omitempty"` Network string `protobuf:"bytes,4,opt,name=network,proto3" json:"network,omitempty"`
// router if the router id
Router string `protobuf:"bytes,5,opt,name=router,proto3" json:"router,omitempty"`
// the network link // the network link
Link string `protobuf:"bytes,5,opt,name=link,proto3" json:"link,omitempty"` Link string `protobuf:"bytes,6,opt,name=link,proto3" json:"link,omitempty"`
// the metric / score of this route // the metric / score of this route
Metric int64 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"` Metric int64 `protobuf:"varint,7,opt,name=metric,proto3" json:"metric,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -685,7 +760,7 @@ func (m *Route) Reset() { *m = Route{} }
func (m *Route) String() string { return proto.CompactTextString(m) } func (m *Route) String() string { return proto.CompactTextString(m) }
func (*Route) ProtoMessage() {} func (*Route) ProtoMessage() {}
func (*Route) Descriptor() ([]byte, []int) { func (*Route) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{14} return fileDescriptor_367072455c71aedc, []int{16}
} }
func (m *Route) XXX_Unmarshal(b []byte) error { func (m *Route) XXX_Unmarshal(b []byte) error {
@ -734,6 +809,13 @@ func (m *Route) GetNetwork() string {
return "" return ""
} }
func (m *Route) GetRouter() string {
if m != nil {
return m.Router
}
return ""
}
func (m *Route) GetLink() string { func (m *Route) GetLink() string {
if m != nil { if m != nil {
return m.Link return m.Link
@ -760,7 +842,7 @@ func (m *Status) Reset() { *m = Status{} }
func (m *Status) String() string { return proto.CompactTextString(m) } func (m *Status) String() string { return proto.CompactTextString(m) }
func (*Status) ProtoMessage() {} func (*Status) ProtoMessage() {}
func (*Status) Descriptor() ([]byte, []int) { func (*Status) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{15} return fileDescriptor_367072455c71aedc, []int{17}
} }
func (m *Status) XXX_Unmarshal(b []byte) error { func (m *Status) XXX_Unmarshal(b []byte) error {
@ -806,7 +888,7 @@ func (m *StatusResponse) Reset() { *m = StatusResponse{} }
func (m *StatusResponse) String() string { return proto.CompactTextString(m) } func (m *StatusResponse) String() string { return proto.CompactTextString(m) }
func (*StatusResponse) ProtoMessage() {} func (*StatusResponse) ProtoMessage() {}
func (*StatusResponse) Descriptor() ([]byte, []int) { func (*StatusResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_6a36eee0b1adf739, []int{16} return fileDescriptor_367072455c71aedc, []int{18}
} }
func (m *StatusResponse) XXX_Unmarshal(b []byte) error { func (m *StatusResponse) XXX_Unmarshal(b []byte) error {
@ -838,6 +920,7 @@ func init() {
proto.RegisterEnum("go.micro.router.AdvertType", AdvertType_name, AdvertType_value) proto.RegisterEnum("go.micro.router.AdvertType", AdvertType_name, AdvertType_value)
proto.RegisterEnum("go.micro.router.EventType", EventType_name, EventType_value) proto.RegisterEnum("go.micro.router.EventType", EventType_name, EventType_value)
proto.RegisterType((*Request)(nil), "go.micro.router.Request") proto.RegisterType((*Request)(nil), "go.micro.router.Request")
proto.RegisterType((*Response)(nil), "go.micro.router.Response")
proto.RegisterType((*ListResponse)(nil), "go.micro.router.ListResponse") proto.RegisterType((*ListResponse)(nil), "go.micro.router.ListResponse")
proto.RegisterType((*LookupRequest)(nil), "go.micro.router.LookupRequest") proto.RegisterType((*LookupRequest)(nil), "go.micro.router.LookupRequest")
proto.RegisterType((*LookupResponse)(nil), "go.micro.router.LookupResponse") proto.RegisterType((*LookupResponse)(nil), "go.micro.router.LookupResponse")
@ -845,6 +928,7 @@ func init() {
proto.RegisterType((*QueryResponse)(nil), "go.micro.router.QueryResponse") proto.RegisterType((*QueryResponse)(nil), "go.micro.router.QueryResponse")
proto.RegisterType((*WatchRequest)(nil), "go.micro.router.WatchRequest") proto.RegisterType((*WatchRequest)(nil), "go.micro.router.WatchRequest")
proto.RegisterType((*Advert)(nil), "go.micro.router.Advert") proto.RegisterType((*Advert)(nil), "go.micro.router.Advert")
proto.RegisterType((*Solicit)(nil), "go.micro.router.Solicit")
proto.RegisterType((*ProcessResponse)(nil), "go.micro.router.ProcessResponse") proto.RegisterType((*ProcessResponse)(nil), "go.micro.router.ProcessResponse")
proto.RegisterType((*CreateResponse)(nil), "go.micro.router.CreateResponse") proto.RegisterType((*CreateResponse)(nil), "go.micro.router.CreateResponse")
proto.RegisterType((*DeleteResponse)(nil), "go.micro.router.DeleteResponse") proto.RegisterType((*DeleteResponse)(nil), "go.micro.router.DeleteResponse")
@ -856,509 +940,53 @@ func init() {
proto.RegisterType((*StatusResponse)(nil), "go.micro.router.StatusResponse") proto.RegisterType((*StatusResponse)(nil), "go.micro.router.StatusResponse")
} }
func init() { func init() { proto.RegisterFile("router.proto", fileDescriptor_367072455c71aedc) }
proto.RegisterFile("micro/go-micro/router/proto/router.proto", fileDescriptor_6a36eee0b1adf739)
} var fileDescriptor_367072455c71aedc = []byte{
// 714 bytes of a gzipped FileDescriptorProto
var fileDescriptor_6a36eee0b1adf739 = []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcd, 0x4e, 0xdb, 0x40,
// 689 bytes of a gzipped FileDescriptorProto 0x10, 0xb6, 0x93, 0xd8, 0x69, 0xa6, 0x21, 0xa4, 0xa3, 0x0a, 0x4c, 0x5a, 0x20, 0xf2, 0x09, 0x21,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x4e, 0xdb, 0x40, 0x64, 0xaa, 0xf4, 0xda, 0x1f, 0x02, 0xa5, 0xaa, 0x54, 0x0e, 0xad, 0x01, 0xf5, 0x6c, 0xec, 0x15,
0x10, 0xb6, 0x93, 0xd8, 0x28, 0xd3, 0x10, 0xdc, 0x51, 0x05, 0x56, 0x5a, 0x20, 0xf2, 0x29, 0x42, 0xb5, 0x48, 0xbc, 0x66, 0x77, 0x03, 0xca, 0xb9, 0x4f, 0xd3, 0x4b, 0x2f, 0x7d, 0xa4, 0xbe, 0x48,
0xd4, 0xa9, 0xd2, 0x6b, 0xff, 0x02, 0xa5, 0xaa, 0x54, 0x0e, 0xad, 0x0b, 0xea, 0xd9, 0xd8, 0x23, 0xe5, 0xdd, 0x75, 0x08, 0x71, 0x16, 0x09, 0x4e, 0xd9, 0xf9, 0xfb, 0x66, 0x66, 0xf7, 0x9b, 0x71,
0x6a, 0x91, 0xd8, 0x66, 0x77, 0x03, 0xca, 0xb9, 0x8f, 0xd1, 0x27, 0xe8, 0x73, 0xf5, 0xda, 0x87, 0xa0, 0xcd, 0xe8, 0x44, 0x10, 0x16, 0xe4, 0x8c, 0x0a, 0x8a, 0xab, 0x97, 0x34, 0x18, 0xa7, 0x31,
0xa8, 0xbc, 0xbb, 0x0e, 0x21, 0xc6, 0x48, 0x70, 0xf2, 0xce, 0xdf, 0x37, 0xff, 0x63, 0x18, 0x4c, 0xa3, 0x81, 0x52, 0xfb, 0x2d, 0x68, 0x86, 0xe4, 0x7a, 0x42, 0xb8, 0xf0, 0x01, 0x9e, 0x85, 0x84,
0x93, 0x88, 0x65, 0xc3, 0xf3, 0xec, 0xa5, 0x7a, 0xb0, 0x6c, 0x26, 0x88, 0x0d, 0x73, 0x96, 0x89, 0xe7, 0x34, 0xe3, 0xc4, 0xff, 0x00, 0xed, 0x93, 0x94, 0x8b, 0x52, 0xc6, 0x00, 0x5c, 0x19, 0xc0,
0x92, 0xf0, 0x25, 0x81, 0x1b, 0xe7, 0x99, 0x2f, 0x75, 0x7c, 0xc5, 0xf6, 0xda, 0xb0, 0x16, 0xd0, 0x3d, 0xbb, 0x5f, 0xdf, 0x79, 0x3e, 0x58, 0x0b, 0x16, 0x80, 0x82, 0xb0, 0xf8, 0x09, 0xb5, 0x97,
0xe5, 0x8c, 0xb8, 0xf0, 0xde, 0x41, 0xe7, 0x38, 0xe1, 0x22, 0x20, 0x9e, 0x67, 0x29, 0x27, 0xf4, 0xff, 0x1e, 0x56, 0x4e, 0x28, 0xbd, 0x9a, 0xe4, 0x1a, 0x1c, 0xf7, 0xc0, 0xb9, 0x9e, 0x10, 0x36,
0xc1, 0x96, 0x4a, 0xdc, 0x35, 0xfb, 0xcd, 0xc1, 0x93, 0xd1, 0xa6, 0xbf, 0x62, 0xec, 0x07, 0xc5, 0xf5, 0xec, 0xbe, 0xbd, 0x34, 0xfe, 0x7b, 0x61, 0x0d, 0x95, 0x93, 0x7f, 0x00, 0x9d, 0x32, 0xfc,
0x27, 0xd0, 0x5a, 0xde, 0x5b, 0x58, 0x3f, 0xce, 0xb2, 0x8b, 0x59, 0xae, 0x01, 0x71, 0x1f, 0xac, 0x89, 0x05, 0xbc, 0x83, 0xb6, 0x42, 0x7c, 0x52, 0xfe, 0x8f, 0xb0, 0xa2, 0xa3, 0x9f, 0x98, 0xbe,
0xcb, 0x19, 0xb1, 0xb9, 0x6b, 0xf6, 0xcd, 0x3b, 0xed, 0xbf, 0x15, 0xd2, 0x40, 0x29, 0x79, 0x1f, 0x03, 0xed, 0x1f, 0x91, 0x88, 0x7f, 0x96, 0x77, 0xfb, 0xdb, 0x06, 0x77, 0x98, 0xdc, 0x10, 0x26,
0xa0, 0x5b, 0x9a, 0x3f, 0x32, 0x80, 0x37, 0xd0, 0x51, 0x88, 0x8f, 0xf2, 0xff, 0x1e, 0xd6, 0xb5, 0xb0, 0x03, 0xb5, 0x34, 0x91, 0x65, 0xb4, 0xc2, 0x5a, 0x9a, 0xe0, 0x3e, 0x34, 0xc4, 0x34, 0x27,
0xf5, 0x23, 0xdd, 0x77, 0xa1, 0xf3, 0x23, 0x14, 0xd1, 0xcf, 0xb2, 0x9e, 0x7f, 0x4c, 0xb0, 0xc7, 0x5e, 0xad, 0x6f, 0xef, 0x74, 0x06, 0xaf, 0x2a, 0xc0, 0x2a, 0xec, 0x6c, 0x9a, 0x93, 0x50, 0x3a,
0xf1, 0x15, 0x31, 0x81, 0x5d, 0x68, 0x24, 0xb1, 0x0c, 0xa3, 0x1d, 0x34, 0x92, 0x18, 0x87, 0xd0, 0xe2, 0x6b, 0x68, 0x89, 0x74, 0x4c, 0xb8, 0x88, 0xc6, 0xb9, 0x57, 0xef, 0xdb, 0x3b, 0xf5, 0xf0,
0x12, 0xf3, 0x9c, 0xdc, 0x46, 0xdf, 0x1c, 0x74, 0x47, 0xcf, 0x2b, 0xc0, 0xca, 0xec, 0x64, 0x9e, 0x4e, 0x81, 0x5d, 0xa8, 0x0b, 0x31, 0xf2, 0x1a, 0x52, 0x5f, 0x1c, 0x8b, 0xda, 0xc9, 0x0d, 0xc9,
0x53, 0x20, 0x15, 0xf1, 0x05, 0xb4, 0x45, 0x32, 0x25, 0x2e, 0xc2, 0x69, 0xee, 0x36, 0xfb, 0xe6, 0x04, 0xf7, 0x1c, 0x43, 0xed, 0xc7, 0x85, 0x39, 0xd4, 0x5e, 0xfe, 0x06, 0x34, 0x4f, 0xe9, 0x28,
0xa0, 0x19, 0xdc, 0x30, 0xd0, 0x81, 0xa6, 0x10, 0x13, 0xb7, 0x25, 0xf9, 0xc5, 0xb3, 0x88, 0x9d, 0x8d, 0xd3, 0x4a, 0xad, 0xfe, 0x0b, 0x58, 0xfd, 0xc6, 0x68, 0x4c, 0x38, 0x9f, 0x31, 0xa5, 0x0b,
0xae, 0x28, 0x15, 0xdc, 0xb5, 0x6a, 0x62, 0x3f, 0x2a, 0xc4, 0x81, 0xd6, 0xf2, 0x9e, 0xc2, 0xc6, 0x9d, 0x23, 0x46, 0x22, 0x41, 0xe6, 0x35, 0x9f, 0xc8, 0x88, 0xdc, 0xd7, 0x9c, 0xe7, 0xc9, 0xbc,
0x57, 0x96, 0x45, 0xc4, 0x79, 0x99, 0xbe, 0xe7, 0x40, 0xf7, 0x90, 0x51, 0x28, 0x68, 0x99, 0xf3, 0xcf, 0x2f, 0x1b, 0x1c, 0x99, 0x15, 0x03, 0xdd, 0xbe, 0x2d, 0xdb, 0xef, 0x2d, 0xaf, 0xcd, 0xd4,
0x91, 0x26, 0x74, 0x9b, 0x73, 0x9a, 0xc7, 0xcb, 0x3a, 0xbf, 0x4c, 0xb0, 0x24, 0x34, 0xfa, 0x3a, 0x7d, 0x6d, 0xb1, 0xfb, 0x3d, 0x70, 0x64, 0x9c, 0xbc, 0x17, 0xf3, 0x33, 0x29, 0x27, 0xff, 0x1c,
0x47, 0x53, 0xe6, 0xd8, 0xbb, 0x3b, 0x80, 0xba, 0x14, 0x1b, 0xab, 0x29, 0xee, 0x83, 0x25, 0xed, 0x1c, 0xf9, 0xcc, 0xe8, 0x41, 0x93, 0x13, 0x76, 0x93, 0xc6, 0x44, 0x37, 0x5b, 0x8a, 0x85, 0xe5,
0x64, 0xf2, 0xf5, 0xbd, 0x50, 0x4a, 0xde, 0x29, 0x58, 0xb2, 0x97, 0xe8, 0xc2, 0x1a, 0x27, 0x76, 0x32, 0x12, 0xe4, 0x36, 0x9a, 0xca, 0x64, 0xad, 0xb0, 0x14, 0x0b, 0x4b, 0x46, 0xc4, 0x2d, 0x65,
0x95, 0x44, 0xa4, 0xab, 0x5f, 0x92, 0x85, 0xe4, 0x3c, 0x14, 0x74, 0x1d, 0xce, 0xa5, 0xb3, 0x76, 0x57, 0x32, 0x59, 0x2b, 0x2c, 0x45, 0xff, 0xaf, 0x0d, 0x8e, 0xcc, 0xf3, 0x30, 0x6e, 0x94, 0x24,
0x50, 0x92, 0x85, 0x24, 0x25, 0x71, 0x9d, 0xb1, 0x0b, 0xe9, 0xac, 0x1d, 0x94, 0xa4, 0xf7, 0xdb, 0x8c, 0x70, 0x5e, 0xe2, 0x6a, 0x71, 0x3e, 0x63, 0xdd, 0x98, 0xb1, 0x71, 0x2f, 0x23, 0xae, 0x69,
0x04, 0x4b, 0xfa, 0xb9, 0x1f, 0x37, 0x8c, 0x63, 0x46, 0x9c, 0x97, 0xb8, 0x9a, 0x5c, 0xf6, 0xd8, 0x7a, 0x32, 0xcf, 0x91, 0x06, 0x2d, 0x21, 0x42, 0x63, 0x94, 0x66, 0x57, 0x9e, 0x2b, 0xb5, 0xf2,
0xac, 0xf5, 0xd8, 0xba, 0xe5, 0x11, 0x11, 0x5a, 0x93, 0x24, 0xbd, 0x70, 0x2d, 0xc9, 0x96, 0x6f, 0x5c, 0xf8, 0x8e, 0x89, 0x60, 0x69, 0xec, 0x35, 0xe5, 0xed, 0x69, 0xc9, 0x1f, 0x80, 0x7b, 0x2a,
0xdc, 0x04, 0x7b, 0x4a, 0x82, 0x25, 0x91, 0x6b, 0xcb, 0x2a, 0x69, 0xca, 0x1b, 0x81, 0xfd, 0x5d, 0x22, 0x31, 0xe1, 0x45, 0x54, 0x4c, 0x93, 0xb2, 0x64, 0x79, 0xc6, 0x97, 0xe0, 0x10, 0xc6, 0x28,
0x84, 0x62, 0xc6, 0x0b, 0xab, 0x28, 0x8b, 0xcb, 0xd0, 0xe4, 0x1b, 0x9f, 0x81, 0x45, 0x8c, 0x65, 0xd3, 0xd5, 0x2a, 0xc1, 0x1f, 0x42, 0x47, 0xc5, 0xcc, 0x06, 0x65, 0x1f, 0x5c, 0x2e, 0x35, 0x7a,
0x4c, 0x47, 0xa5, 0x08, 0x6f, 0x0c, 0x5d, 0x65, 0xb3, 0x98, 0xfa, 0x21, 0xd8, 0x5c, 0x72, 0xf4, 0xd0, 0xd6, 0x2b, 0x2f, 0xa0, 0x03, 0xb4, 0xdb, 0xee, 0x00, 0xe0, 0x8e, 0xe1, 0x88, 0xd0, 0x51,
0xd6, 0x6c, 0x55, 0x2a, 0xad, 0x0d, 0xb4, 0xda, 0xde, 0x08, 0xe0, 0x66, 0x5c, 0x11, 0xa1, 0xab, 0xd2, 0x30, 0xcb, 0xe8, 0x24, 0x8b, 0x49, 0xd7, 0xc2, 0x2e, 0xb4, 0x95, 0x4e, 0x71, 0xa8, 0x6b,
0xa8, 0x71, 0x9a, 0x66, 0xb3, 0x34, 0x22, 0xc7, 0x40, 0x07, 0x3a, 0x8a, 0xa7, 0x66, 0xc5, 0x31, 0xef, 0xee, 0x43, 0x6b, 0x46, 0x0b, 0x04, 0x70, 0x15, 0x01, 0xbb, 0x56, 0x71, 0x56, 0xd4, 0xeb,
0xf7, 0x86, 0xd0, 0x5e, 0xb4, 0x1f, 0x01, 0x6c, 0x35, 0x68, 0x8e, 0x51, 0xbc, 0xd5, 0x88, 0x39, 0xda, 0xc5, 0x59, 0x07, 0xd4, 0x06, 0x7f, 0xea, 0xe0, 0x86, 0xea, 0x4a, 0xbe, 0x82, 0xab, 0x56,
0x66, 0xf1, 0xd6, 0x06, 0x8d, 0xd1, 0xbf, 0x06, 0xd8, 0xb2, 0xf2, 0x0c, 0xbf, 0x80, 0xad, 0xee, 0x0b, 0x6e, 0x55, 0x4a, 0xbb, 0xb7, 0xb2, 0x7a, 0xdb, 0x46, 0xbb, 0x26, 0xb1, 0x85, 0x87, 0xe0,
0x04, 0xee, 0x54, 0x42, 0xbb, 0x75, 0x7f, 0x7a, 0xbb, 0xb5, 0x72, 0x3d, 0xac, 0x06, 0x1e, 0x80, 0xc8, 0x31, 0xc7, 0xcd, 0x8a, 0xef, 0xfc, 0xf8, 0xf7, 0x0c, 0x23, 0xe7, 0x5b, 0x6f, 0x6c, 0x3c,
0x25, 0x77, 0x16, 0xb7, 0x2b, 0xba, 0xcb, 0xbb, 0xdc, 0xab, 0xd9, 0x1f, 0xcf, 0x78, 0x65, 0xe2, 0x84, 0x96, 0x6a, 0x2f, 0xe5, 0x04, 0xbd, 0x2a, 0x61, 0x35, 0xc4, 0xba, 0x61, 0x31, 0x48, 0x8c,
0x01, 0xb4, 0x55, 0x7a, 0x09, 0x27, 0x74, 0xab, 0x83, 0xa9, 0x21, 0xb6, 0x6a, 0xb6, 0x5c, 0x62, 0x83, 0xbb, 0x91, 0x35, 0x23, 0x6c, 0x2c, 0xb1, 0xcc, 0x3a, 0xf9, 0x0c, 0x4d, 0x3d, 0xd9, 0x68,
0x7c, 0x82, 0x35, 0xbd, 0x7f, 0x58, 0xa7, 0xd7, 0xeb, 0x57, 0x04, 0xab, 0x2b, 0x6b, 0xe0, 0xd1, 0xca, 0xd4, 0xeb, 0x57, 0x0c, 0x8b, 0xcb, 0xc0, 0xc2, 0xe3, 0x19, 0x8b, 0xcc, 0x85, 0x6c, 0x9b,
0x62, 0x06, 0xea, 0x03, 0xd9, 0xad, 0xeb, 0xe8, 0x02, 0x66, 0xf4, 0xb7, 0x01, 0xd6, 0x49, 0x78, 0x38, 0x31, 0x83, 0x19, 0xfc, 0xab, 0x81, 0x73, 0x16, 0x5d, 0x8c, 0x08, 0x1e, 0x95, 0xcf, 0x8b,
0x36, 0x21, 0x3c, 0x2c, 0x9b, 0x83, 0x35, 0x2b, 0x77, 0x07, 0xdc, 0xca, 0xd9, 0x30, 0x0a, 0x10, 0x86, 0x61, 0x5e, 0x02, 0xb7, 0xb0, 0x90, 0xac, 0x02, 0x44, 0xf1, 0xe2, 0x11, 0x20, 0x0b, 0x3b,
0xd5, 0xd5, 0x07, 0x80, 0xac, 0x5c, 0x1a, 0x09, 0xa2, 0xc6, 0xe1, 0x01, 0x20, 0x2b, 0xc7, 0xc9, 0x4c, 0x82, 0x28, 0x42, 0x3d, 0x02, 0x64, 0x61, 0xed, 0x59, 0x38, 0x84, 0x46, 0xf1, 0x61, 0x7d,
0xc0, 0x31, 0xb4, 0x8a, 0x7f, 0xdc, 0x3d, 0xd5, 0xa9, 0x0e, 0xc2, 0xf2, 0x4f, 0xd1, 0x33, 0xf0, 0xe0, 0x76, 0xaa, 0x54, 0x9a, 0xff, 0x12, 0xfb, 0x16, 0x7e, 0x29, 0xb7, 0xd6, 0xa6, 0xe1, 0x23,
0x73, 0x79, 0x5b, 0xb6, 0x6b, 0xfe, 0x27, 0x1a, 0x68, 0xa7, 0x4e, 0x5c, 0x22, 0x9d, 0xd9, 0xf2, 0xa6, 0x81, 0xb6, 0x4c, 0xe6, 0x12, 0xe9, 0xc2, 0x95, 0x7f, 0x0a, 0xde, 0xfe, 0x0f, 0x00, 0x00,
0x9f, 0xfc, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8c, 0xd0, 0xc0, 0x27, 0xbf, 0x07, 0x00, 0xff, 0xff, 0xb7, 0x25, 0xac, 0xac, 0x24, 0x08, 0x00, 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
// RouterClient is the client API for Router service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RouterClient interface {
Lookup(ctx context.Context, in *LookupRequest, opts ...grpc.CallOption) (*LookupResponse, error)
Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Router_WatchClient, error)
Advertise(ctx context.Context, in *Request, opts ...grpc.CallOption) (Router_AdvertiseClient, error)
Process(ctx context.Context, in *Advert, opts ...grpc.CallOption) (*ProcessResponse, error)
Status(ctx context.Context, in *Request, opts ...grpc.CallOption) (*StatusResponse, error)
}
type routerClient struct {
cc *grpc.ClientConn
}
func NewRouterClient(cc *grpc.ClientConn) RouterClient {
return &routerClient{cc}
}
func (c *routerClient) Lookup(ctx context.Context, in *LookupRequest, opts ...grpc.CallOption) (*LookupResponse, error) {
out := new(LookupResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Router/Lookup", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *routerClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Router_WatchClient, error) {
stream, err := c.cc.NewStream(ctx, &_Router_serviceDesc.Streams[0], "/go.micro.router.Router/Watch", opts...)
if err != nil {
return nil, err
}
x := &routerWatchClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Router_WatchClient interface {
Recv() (*Event, error)
grpc.ClientStream
}
type routerWatchClient struct {
grpc.ClientStream
}
func (x *routerWatchClient) Recv() (*Event, error) {
m := new(Event)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *routerClient) Advertise(ctx context.Context, in *Request, opts ...grpc.CallOption) (Router_AdvertiseClient, error) {
stream, err := c.cc.NewStream(ctx, &_Router_serviceDesc.Streams[1], "/go.micro.router.Router/Advertise", opts...)
if err != nil {
return nil, err
}
x := &routerAdvertiseClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Router_AdvertiseClient interface {
Recv() (*Advert, error)
grpc.ClientStream
}
type routerAdvertiseClient struct {
grpc.ClientStream
}
func (x *routerAdvertiseClient) Recv() (*Advert, error) {
m := new(Advert)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *routerClient) Process(ctx context.Context, in *Advert, opts ...grpc.CallOption) (*ProcessResponse, error) {
out := new(ProcessResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Router/Process", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *routerClient) Status(ctx context.Context, in *Request, opts ...grpc.CallOption) (*StatusResponse, error) {
out := new(StatusResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Router/Status", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RouterServer is the server API for Router service.
type RouterServer interface {
Lookup(context.Context, *LookupRequest) (*LookupResponse, error)
Watch(*WatchRequest, Router_WatchServer) error
Advertise(*Request, Router_AdvertiseServer) error
Process(context.Context, *Advert) (*ProcessResponse, error)
Status(context.Context, *Request) (*StatusResponse, error)
}
func RegisterRouterServer(s *grpc.Server, srv RouterServer) {
s.RegisterService(&_Router_serviceDesc, srv)
}
func _Router_Lookup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LookupRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RouterServer).Lookup(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Router/Lookup",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RouterServer).Lookup(ctx, req.(*LookupRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Router_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(WatchRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RouterServer).Watch(m, &routerWatchServer{stream})
}
type Router_WatchServer interface {
Send(*Event) error
grpc.ServerStream
}
type routerWatchServer struct {
grpc.ServerStream
}
func (x *routerWatchServer) Send(m *Event) error {
return x.ServerStream.SendMsg(m)
}
func _Router_Advertise_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Request)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RouterServer).Advertise(m, &routerAdvertiseServer{stream})
}
type Router_AdvertiseServer interface {
Send(*Advert) error
grpc.ServerStream
}
type routerAdvertiseServer struct {
grpc.ServerStream
}
func (x *routerAdvertiseServer) Send(m *Advert) error {
return x.ServerStream.SendMsg(m)
}
func _Router_Process_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Advert)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RouterServer).Process(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Router/Process",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RouterServer).Process(ctx, req.(*Advert))
}
return interceptor(ctx, in, info, handler)
}
func _Router_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RouterServer).Status(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Router/Status",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RouterServer).Status(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
var _Router_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.router.Router",
HandlerType: (*RouterServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Lookup",
Handler: _Router_Lookup_Handler,
},
{
MethodName: "Process",
Handler: _Router_Process_Handler,
},
{
MethodName: "Status",
Handler: _Router_Status_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Watch",
Handler: _Router_Watch_Handler,
ServerStreams: true,
},
{
StreamName: "Advertise",
Handler: _Router_Advertise_Handler,
ServerStreams: true,
},
},
Metadata: "micro/go-micro/router/proto/router.proto",
}
// TableClient is the client API for Table service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type TableClient interface {
Create(ctx context.Context, in *Route, opts ...grpc.CallOption) (*CreateResponse, error)
Delete(ctx context.Context, in *Route, opts ...grpc.CallOption) (*DeleteResponse, error)
Update(ctx context.Context, in *Route, opts ...grpc.CallOption) (*UpdateResponse, error)
List(ctx context.Context, in *Request, opts ...grpc.CallOption) (*ListResponse, error)
Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
}
type tableClient struct {
cc *grpc.ClientConn
}
func NewTableClient(cc *grpc.ClientConn) TableClient {
return &tableClient{cc}
}
func (c *tableClient) Create(ctx context.Context, in *Route, opts ...grpc.CallOption) (*CreateResponse, error) {
out := new(CreateResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Table/Create", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *tableClient) Delete(ctx context.Context, in *Route, opts ...grpc.CallOption) (*DeleteResponse, error) {
out := new(DeleteResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Table/Delete", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *tableClient) Update(ctx context.Context, in *Route, opts ...grpc.CallOption) (*UpdateResponse, error) {
out := new(UpdateResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Table/Update", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *tableClient) List(ctx context.Context, in *Request, opts ...grpc.CallOption) (*ListResponse, error) {
out := new(ListResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Table/List", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *tableClient) Query(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, "/go.micro.router.Table/Query", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// TableServer is the server API for Table service.
type TableServer interface {
Create(context.Context, *Route) (*CreateResponse, error)
Delete(context.Context, *Route) (*DeleteResponse, error)
Update(context.Context, *Route) (*UpdateResponse, error)
List(context.Context, *Request) (*ListResponse, error)
Query(context.Context, *QueryRequest) (*QueryResponse, error)
}
func RegisterTableServer(s *grpc.Server, srv TableServer) {
s.RegisterService(&_Table_serviceDesc, srv)
}
func _Table_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Route)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TableServer).Create(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Table/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TableServer).Create(ctx, req.(*Route))
}
return interceptor(ctx, in, info, handler)
}
func _Table_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Route)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TableServer).Delete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Table/Delete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TableServer).Delete(ctx, req.(*Route))
}
return interceptor(ctx, in, info, handler)
}
func _Table_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Route)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TableServer).Update(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Table/Update",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TableServer).Update(ctx, req.(*Route))
}
return interceptor(ctx, in, info, handler)
}
func _Table_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TableServer).List(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Table/List",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TableServer).List(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
func _Table_Query_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TableServer).Query(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.router.Table/Query",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TableServer).Query(ctx, req.(*QueryRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Table_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.router.Table",
HandlerType: (*TableServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Create",
Handler: _Table_Create_Handler,
},
{
MethodName: "Delete",
Handler: _Table_Delete_Handler,
},
{
MethodName: "Update",
Handler: _Table_Update_Handler,
},
{
MethodName: "List",
Handler: _Table_List_Handler,
},
{
MethodName: "Query",
Handler: _Table_Query_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "micro/go-micro/router/proto/router.proto",
} }

View File

@ -7,6 +7,7 @@ service Router {
rpc Lookup(LookupRequest) returns (LookupResponse) {}; rpc Lookup(LookupRequest) returns (LookupResponse) {};
rpc Watch(WatchRequest) returns (stream Event) {}; rpc Watch(WatchRequest) returns (stream Event) {};
rpc Advertise(Request) returns (stream Advert) {}; rpc Advertise(Request) returns (stream Advert) {};
rpc Solicit(Request) returns (Response) {};
rpc Process(Advert) returns (ProcessResponse) {}; rpc Process(Advert) returns (ProcessResponse) {};
rpc Status(Request) returns (StatusResponse) {}; rpc Status(Request) returns (StatusResponse) {};
} }
@ -22,6 +23,9 @@ service Table {
// Empty request // Empty request
message Request {} message Request {}
// Empty response
message Response {}
// ListResponse is returned by List // ListResponse is returned by List
message ListResponse { message ListResponse {
repeated Route routes = 1; repeated Route routes = 1;
@ -37,10 +41,12 @@ message LookupResponse {
repeated Route routes = 1; repeated Route routes = 1;
} }
// QueryRequest queries Table for Routes
message QueryRequest{ message QueryRequest{
Query query = 1; Query query = 1;
} }
// QueryResponse is returned by Query
message QueryResponse { message QueryResponse {
repeated Route routes = 1; repeated Route routes = 1;
} }
@ -68,6 +74,12 @@ message Advert {
repeated Event events = 5; repeated Event events = 5;
} }
// Solicit solicits routes
message Solicit {
// id of the soliciting router
string id = 1;
}
// ProcessResponse is returned by Process // ProcessResponse is returned by Process
message ProcessResponse {} message ProcessResponse {}
@ -117,10 +129,12 @@ message Route {
string gateway = 3; string gateway = 3;
// the network for this destination // the network for this destination
string network = 4; string network = 4;
// router if the router id
string router = 5;
// the network link // the network link
string link = 5; string link = 6;
// the metric / score of this route // the metric / score of this route
int64 metric = 6; int64 metric = 7;
} }
message Status { message Status {

View File

@ -11,29 +11,38 @@ type QueryOptions struct {
Gateway string Gateway string
// Network is network address // Network is network address
Network string Network string
// Router is router id
Router string
} }
// QueryService sets destination address // QueryService sets service to query
func QueryService(s string) QueryOption { func QueryService(s string) QueryOption {
return func(o *QueryOptions) { return func(o *QueryOptions) {
o.Service = s o.Service = s
} }
} }
// QueryGateway sets route gateway // QueryGateway sets gateway address to query
func QueryGateway(g string) QueryOption { func QueryGateway(g string) QueryOption {
return func(o *QueryOptions) { return func(o *QueryOptions) {
o.Gateway = g o.Gateway = g
} }
} }
// QueryNetwork sets route network address // QueryNetwork sets network name to query
func QueryNetwork(n string) QueryOption { func QueryNetwork(n string) QueryOption {
return func(o *QueryOptions) { return func(o *QueryOptions) {
o.Network = n o.Network = n
} }
} }
// QueryRouter sets router id to query
func QueryRouter(r string) QueryOption {
return func(o *QueryOptions) {
o.Router = r
}
}
// Query is routing table query // Query is routing table query
type Query interface { type Query interface {
// Options returns query options // Options returns query options
@ -52,6 +61,7 @@ func NewQuery(opts ...QueryOption) Query {
Service: "*", Service: "*",
Gateway: "*", Gateway: "*",
Network: "*", Network: "*",
Router: "*",
} }
for _, o := range opts { for _, o := range opts {
@ -67,8 +77,3 @@ func NewQuery(opts ...QueryOption) Query {
func (q *query) Options() QueryOptions { func (q *query) Options() QueryOptions {
return q.opts return q.opts
} }
// String prints routing table query in human readable form
func (q query) String() string {
return "query"
}

View File

@ -7,9 +7,9 @@ import (
var ( var (
// DefaultLink is default network link // DefaultLink is default network link
DefaultLink = "local" DefaultLink = "local"
// DefaultLocalMetric is default route cost metric for the local network // DefaultLocalMetric is default route cost for a local route
DefaultLocalMetric = 1 DefaultLocalMetric = 1
// DefaultNetworkMetric is default route cost metric for the micro network // DefaultNetworkMetric is default route cost for a network route
DefaultNetworkMetric = 10 DefaultNetworkMetric = 10
) )
@ -23,6 +23,8 @@ type Route struct {
Gateway string Gateway string
// Network is network address // Network is network address
Network string Network string
// Router is router id
Router string
// Link is network link // Link is network link
Link string Link string
// Metric is the route cost metric // Metric is the route cost metric
@ -33,6 +35,6 @@ type Route struct {
func (r *Route) Hash() uint64 { func (r *Route) Hash() uint64 {
h := fnv.New64() h := fnv.New64()
h.Reset() h.Reset()
h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Link)) h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Router + r.Link))
return h.Sum64() return h.Sum64()
} }

View File

@ -5,6 +5,17 @@ import (
"time" "time"
) )
var (
// DefaultAddress is default router address
DefaultAddress = ":9093"
// DefaultName is default router service name
DefaultName = "go.micro.router"
// DefaultNetwork is default micro network
DefaultNetwork = "go.micro"
// DefaultRouter is default network router
DefaultRouter = NewRouter()
)
// Router is an interface for a routing control plane // Router is an interface for a routing control plane
type Router interface { type Router interface {
// Init initializes the router with options // Init initializes the router with options
@ -17,10 +28,14 @@ type Router interface {
Advertise() (<-chan *Advert, error) Advertise() (<-chan *Advert, error)
// Process processes incoming adverts // Process processes incoming adverts
Process(*Advert) error Process(*Advert) error
// Solicit advertises the whole routing table to the network
Solicit() error
// Lookup queries routes in the routing table // Lookup queries routes in the routing table
Lookup(Query) ([]Route, error) Lookup(Query) ([]Route, error)
// Watch returns a watcher which tracks updates to the routing table // Watch returns a watcher which tracks updates to the routing table
Watch(opts ...WatchOption) (Watcher, error) Watch(opts ...WatchOption) (Watcher, error)
// Start starts the router
Start() error
// Status returns router status // Status returns router status
Status() Status Status() Status
// Stop stops the router // Stop stops the router
@ -29,16 +44,17 @@ type Router interface {
String() string String() string
} }
// Table is an interface for routing table
type Table interface { type Table interface {
// Create new route in the routing table // Create new route in the routing table
Create(Route) error Create(Route) error
// Delete deletes existing route from the routing table // Delete existing route from the routing table
Delete(Route) error Delete(Route) error
// Update updates route in the routing table // Update route in the routing table
Update(Route) error Update(Route) error
// List returns the list of all routes in the table // List all routes in the table
List() ([]Route, error) List() ([]Route, error)
// Query queries routes in the routing table // Query routes in the routing table
Query(Query) ([]Route, error) Query(Query) ([]Route, error)
} }
@ -76,10 +92,15 @@ func (s StatusCode) String() string {
// Status is router status // Status is router status
type Status struct { type Status struct {
// Error is router error
Error error
// Code defines router status // Code defines router status
Code StatusCode 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 // AdvertType is route advertisement type
@ -118,17 +139,6 @@ type Advert struct {
Events []*Event Events []*Event
} }
var (
// DefaultAddress is default router address
DefaultAddress = ":9093"
// DefaultName is default router service name
DefaultName = "go.micro.router"
// DefaultNetwork is default micro network
DefaultNetwork = "go.micro"
// DefaultRouter is default network router
DefaultRouter = NewRouter()
)
// NewRouter creates new Router and returns it // NewRouter creates new Router and returns it
func NewRouter(opts ...Option) Router { func NewRouter(opts ...Option) Router {
return newRouter(opts...) return newRouter(opts...)

View File

@ -43,9 +43,16 @@ func NewRouter(opts ...router.Option) router.Router {
cli = options.Client 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? // NOTE: should we have Client/Service option in router.Options?
s := &svc{ s := &svc{
opts: options, opts: options,
status: status,
router: pb.NewRouterService(router.DefaultName, cli), router: pb.NewRouterService(router.DefaultName, cli),
} }
@ -63,21 +70,43 @@ func NewRouter(opts ...router.Option) router.Router {
// Init initializes router with given options // Init initializes router with given options
func (s *svc) Init(opts ...router.Option) error { func (s *svc) Init(opts ...router.Option) error {
s.Lock()
defer s.Unlock()
for _, o := range opts { for _, o := range opts {
o(&s.opts) o(&s.opts)
} }
return nil return nil
} }
// Options returns router options // Options returns router options
func (s *svc) Options() 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 { func (s *svc) Table() router.Table {
return s.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 { func (s *svc) advertiseEvents(advertChan chan *router.Advert, stream pb.Router_AdvertiseService) error {
go func() { go func() {
<-s.exit <-s.exit
@ -140,10 +169,7 @@ func (s *svc) Advertise() (<-chan *router.Advert, error) {
s.Lock() s.Lock()
defer s.Unlock() defer s.Unlock()
// get the status switch s.status.Code {
status := s.Status()
switch status.Code {
case router.Running, router.Advertising: case router.Running, router.Advertising:
stream, err := s.router.Advertise(context.Background(), &pb.Request{}, s.callOpts...) stream, err := s.router.Advertise(context.Background(), &pb.Request{}, s.callOpts...)
if err != nil { if err != nil {
@ -154,16 +180,8 @@ func (s *svc) Advertise() (<-chan *router.Advert, error) {
go s.advertiseEvents(advertChan, stream) go s.advertiseEvents(advertChan, stream)
return advertChan, nil return advertChan, nil
case router.Stopped: 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) return nil, fmt.Errorf("error: %s", s.status.Error)
} }
@ -202,6 +220,42 @@ func (s *svc) Process(advert *router.Advert) error {
return nil return nil
} }
// Solicit advertise all routes
func (s *svc) Solicit() error {
// list all the routes
routes, err := s.table.List()
if err != nil {
return err
}
// build events to advertise
events := make([]*router.Event, len(routes))
for i, _ := range events {
events[i] = &router.Event{
Type: router.Update,
Timestamp: time.Now(),
Route: routes[i],
}
}
advert := &router.Advert{
Id: s.opts.Id,
Type: router.RouteUpdate,
Timestamp: time.Now(),
TTL: time.Duration(router.DefaultAdvertTTL),
Events: events,
}
select {
case s.advertChan <- advert:
case <-s.exit:
close(s.advertChan)
return nil
}
return nil
}
// Status returns router status // Status returns router status
func (s *svc) Status() router.Status { func (s *svc) Status() router.Status {
s.Lock() s.Lock()
@ -303,7 +357,9 @@ func (s *svc) Watch(opts ...router.WatchOption) (router.Watcher, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var options router.WatchOptions options := router.WatchOptions{
Service: "*",
}
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }

View File

@ -70,13 +70,11 @@ func (w *watcher) watch(stream pb.Router_WatchService) error {
Route: route, Route: route,
} }
for {
select { select {
case w.resChan <- event: case w.resChan <- event:
case <-w.done: case <-w.done:
} }
} }
}
return watchErr return watchErr
} }

View File

@ -6,9 +6,17 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/micro/go-micro/util/log"
) )
// table is an in memory routing table var (
// ErrRouteNotFound is returned when no route was found in the routing table
ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is returned when the route already exists
ErrDuplicateRoute = errors.New("duplicate route")
)
// table is an in-memory routing table
type table struct { type table struct {
sync.RWMutex sync.RWMutex
// routes stores service routes // routes stores service routes
@ -25,6 +33,19 @@ func newTable(opts ...Option) *table {
} }
} }
// sendEvent sends events to all subscribed watchers
func (t *table) sendEvent(e *Event) {
t.RLock()
defer t.RUnlock()
for _, w := range t.watchers {
select {
case w.resChan <- e:
case <-w.done:
}
}
}
// Create creates new route in the routing table // Create creates new route in the routing table
func (t *table) Create(r Route) error { func (t *table) Create(r Route) error {
service := r.Service service := r.Service
@ -36,14 +57,12 @@ func (t *table) Create(r Route) error {
// check if there are any routes in the table for the route destination // check if there are any routes in the table for the route destination
if _, ok := t.routes[service]; !ok { if _, ok := t.routes[service]; !ok {
t.routes[service] = make(map[uint64]Route) t.routes[service] = make(map[uint64]Route)
t.routes[service][sum] = r
go t.sendEvent(&Event{Type: Create, Timestamp: time.Now(), Route: r})
return nil
} }
// add new route to the table for the route destination // add new route to the table for the route destination
if _, ok := t.routes[service][sum]; !ok { if _, ok := t.routes[service][sum]; !ok {
t.routes[service][sum] = r t.routes[service][sum] = r
log.Debugf("Router emitting %s for route: %s", Create, r.Address)
go t.sendEvent(&Event{Type: Create, Timestamp: time.Now(), Route: r}) go t.sendEvent(&Event{Type: Create, Timestamp: time.Now(), Route: r})
return nil return nil
} }
@ -63,7 +82,12 @@ func (t *table) Delete(r Route) error {
return ErrRouteNotFound return ErrRouteNotFound
} }
if _, ok := t.routes[service][sum]; !ok {
return ErrRouteNotFound
}
delete(t.routes[service], sum) delete(t.routes[service], sum)
log.Debugf("Router emitting %s for route: %s", Delete, r.Address)
go t.sendEvent(&Event{Type: Delete, Timestamp: time.Now(), Route: r}) go t.sendEvent(&Event{Type: Delete, Timestamp: time.Now(), Route: r})
return nil return nil
@ -80,13 +104,17 @@ func (t *table) Update(r Route) error {
// check if the route destination has any routes in the table // check if the route destination has any routes in the table
if _, ok := t.routes[service]; !ok { if _, ok := t.routes[service]; !ok {
t.routes[service] = make(map[uint64]Route) t.routes[service] = make(map[uint64]Route)
}
if _, ok := t.routes[service][sum]; !ok {
t.routes[service][sum] = r t.routes[service][sum] = r
go t.sendEvent(&Event{Type: Create, Timestamp: time.Now(), Route: r}) log.Debugf("Router emitting %s for route: %s", Update, r.Address)
go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r})
return nil return nil
} }
// just update the route, but dont emit Update event
t.routes[service][sum] = r t.routes[service][sum] = r
go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r})
return nil return nil
} }
@ -106,21 +134,23 @@ func (t *table) List() ([]Route, error) {
return routes, nil return routes, nil
} }
// isMatch checks if the route matches given network and router // isMatch checks if the route matches given query options
func isMatch(route Route, network, router string) bool { func isMatch(route Route, gateway, network, router string) bool {
if gateway == "*" || gateway == route.Gateway {
if network == "*" || network == route.Network { if network == "*" || network == route.Network {
if router == "*" || router == route.Gateway { if router == "*" || router == route.Router {
return true return true
} }
} }
}
return false return false
} }
// findRoutes finds all the routes for given network and router and returns them // findRoutes finds all the routes for given network and router and returns them
func findRoutes(routes map[uint64]Route, network, router string) []Route { func findRoutes(routes map[uint64]Route, gateway, network, router string) []Route {
var results []Route var results []Route
for _, route := range routes { for _, route := range routes {
if isMatch(route, network, router) { if isMatch(route, gateway, network, router) {
results = append(results, route) results = append(results, route)
} }
} }
@ -136,13 +166,13 @@ func (t *table) Query(q Query) ([]Route, error) {
if _, ok := t.routes[q.Options().Service]; !ok { if _, ok := t.routes[q.Options().Service]; !ok {
return nil, ErrRouteNotFound return nil, ErrRouteNotFound
} }
return findRoutes(t.routes[q.Options().Service], q.Options().Network, q.Options().Gateway), nil return findRoutes(t.routes[q.Options().Service], q.Options().Gateway, q.Options().Network, q.Options().Router), nil
} }
var results []Route var results []Route
// search through all destinations // search through all destinations
for _, routes := range t.routes { for _, routes := range t.routes {
results = append(results, findRoutes(routes, q.Options().Network, q.Options().Gateway)...) results = append(results, findRoutes(routes, q.Options().Gateway, q.Options().Network, q.Options().Router)...)
} }
return results, nil return results, nil
@ -181,23 +211,3 @@ func (t *table) Watch(opts ...WatchOption) (Watcher, error) {
return w, nil return w, nil
} }
// sendEvent sends events to all subscribed watchers
func (t *table) sendEvent(e *Event) {
t.RLock()
defer t.RUnlock()
for _, w := range t.watchers {
select {
case w.resChan <- e:
case <-w.done:
}
}
}
var (
// ErrRouteNotFound is returned when no route was found in the routing table
ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is returned when the route already exists
ErrDuplicateRoute = errors.New("duplicate route")
)

View File

@ -9,6 +9,7 @@ func testSetup() (*table, Route) {
Service: "dest.svc", Service: "dest.svc",
Gateway: "dest.gw", Gateway: "dest.gw",
Network: "dest.network", Network: "dest.network",
Router: "src.router",
Link: "det.link", Link: "det.link",
Metric: 10, Metric: 10,
} }
@ -109,11 +110,13 @@ func TestQuery(t *testing.T) {
svc := []string{"svc1", "svc2", "svc3"} svc := []string{"svc1", "svc2", "svc3"}
net := []string{"net1", "net2", "net1"} net := []string{"net1", "net2", "net1"}
gw := []string{"gw1", "gw2", "gw3"} gw := []string{"gw1", "gw2", "gw3"}
rtr := []string{"rtr1", "rt2", "rt3"}
for i := 0; i < len(svc); i++ { for i := 0; i < len(svc); i++ {
route.Service = svc[i] route.Service = svc[i]
route.Network = net[i] route.Network = net[i]
route.Gateway = gw[i] route.Gateway = gw[i]
route.Router = rtr[i]
if err := table.Create(route); err != nil { if err := table.Create(route); err != nil {
t.Errorf("error adding route: %s", err) t.Errorf("error adding route: %s", err)
} }
@ -127,8 +130,9 @@ func TestQuery(t *testing.T) {
t.Errorf("error looking up routes: %s", err) t.Errorf("error looking up routes: %s", err)
} }
// query particular net // query routes particular network
query = NewQuery(QueryNetwork("net1")) network := "net1"
query = NewQuery(QueryNetwork(network))
routes, err = table.Query(query) routes, err = table.Query(query)
if err != nil { if err != nil {
@ -139,7 +143,13 @@ func TestQuery(t *testing.T) {
t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 2, len(routes)) t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 2, len(routes))
} }
// query particular gateway for _, route := range routes {
if route.Network != network {
t.Errorf("incorrect route returned. Expected network: %s, found: %s", network, route.Network)
}
}
// query routes for particular gateway
gateway := "gw1" gateway := "gw1"
query = NewQuery(QueryGateway(gateway)) query = NewQuery(QueryGateway(gateway))
@ -156,11 +166,28 @@ func TestQuery(t *testing.T) {
t.Errorf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway) t.Errorf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway)
} }
// query particular route // query routes for particular router
network := "net1" router := "rtr1"
query = NewQuery(QueryRouter(router))
routes, err = table.Query(query)
if err != nil {
t.Errorf("error looking up routes: %s", err)
}
if len(routes) != 1 {
t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes))
}
if routes[0].Router != router {
t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router)
}
// query particular gateway and network
query = NewQuery( query = NewQuery(
QueryGateway(gateway), QueryGateway(gateway),
QueryNetwork(network), QueryNetwork(network),
QueryRouter(router),
) )
routes, err = table.Query(query) routes, err = table.Query(query)
@ -180,7 +207,11 @@ func TestQuery(t *testing.T) {
t.Errorf("incorrect network returned. Expected network: %s, found: %s", network, routes[0].Network) t.Errorf("incorrect network returned. Expected network: %s, found: %s", network, routes[0].Network)
} }
// bullshit route query if routes[0].Router != router {
t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router)
}
// non-existen route query
query = NewQuery(QueryService("foobar")) query = NewQuery(QueryService("foobar"))
routes, err = table.Query(query) routes, err = table.Query(query)

View File

@ -6,6 +6,11 @@ import (
"time" "time"
) )
var (
// ErrWatcherStopped is returned when routing table watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// EventType defines routing table event // EventType defines routing table event
type EventType int type EventType int
@ -42,9 +47,6 @@ type Event struct {
Route Route Route Route
} }
// WatchOption is used to define what routes to watch in the table
type WatchOption func(*WatchOptions)
// Watcher defines routing table watcher interface // Watcher defines routing table watcher interface
// Watcher returns updates to the routing table // Watcher returns updates to the routing table
type Watcher interface { type Watcher interface {
@ -56,7 +58,11 @@ type Watcher interface {
Stop() Stop()
} }
// WatchOption is used to define what routes to watch in the table
type WatchOption func(*WatchOptions)
// WatchOptions are table watcher options // WatchOptions are table watcher options
// TODO: expand the options to watch based on other criteria
type WatchOptions struct { type WatchOptions struct {
// Service allows to watch specific service routes // Service allows to watch specific service routes
Service string Service string
@ -70,6 +76,7 @@ func WatchService(s string) WatchOption {
} }
} }
// tableWatcher implements routing table Watcher
type tableWatcher struct { type tableWatcher struct {
sync.RWMutex sync.RWMutex
id string id string
@ -113,8 +120,3 @@ func (w *tableWatcher) Stop() {
close(w.done) close(w.done)
} }
} }
var (
// ErrWatcherStopped is returned when routing table watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped")
)

298
runtime/default.go Normal file
View File

@ -0,0 +1,298 @@
package runtime
import (
"errors"
"io"
"strings"
"sync"
"time"
"github.com/micro/go-micro/runtime/package"
"github.com/micro/go-micro/runtime/process"
proc "github.com/micro/go-micro/runtime/process/os"
"github.com/micro/go-micro/util/log"
)
type runtime struct {
sync.RWMutex
// used to stop the runtime
closed chan bool
// used to start new services
start chan *service
// indicates if we're running
running bool
// the service map
services map[string]*service
}
type service struct {
sync.RWMutex
running bool
closed chan bool
err error
// output for logs
output io.Writer
// service to manage
*Service
// process creator
Process *proc.Process
// Exec
Exec *process.Executable
// process pid
PID *process.PID
}
func newRuntime() *runtime {
return &runtime{
closed: make(chan bool),
start: make(chan *service, 128),
services: make(map[string]*service),
}
}
func newService(s *Service, c CreateOptions) *service {
var exec string
var args []string
if len(s.Exec) > 0 {
parts := strings.Split(s.Exec, " ")
exec = parts[0]
args = []string{}
if len(parts) > 1 {
args = parts[1:]
}
} else {
// set command
exec = c.Command[0]
// set args
if len(c.Command) > 1 {
args = c.Command[1:]
}
}
return &service{
Service: s,
Process: new(proc.Process),
Exec: &process.Executable{
Binary: &packager.Binary{
Name: s.Name,
Path: exec,
},
Env: c.Env,
Args: args,
},
output: c.Output,
}
}
func (s *service) streamOutput() {
go io.Copy(s.output, s.PID.Output)
go io.Copy(s.output, s.PID.Error)
}
func (s *service) Running() bool {
s.RLock()
defer s.RUnlock()
return s.running
}
func (s *service) Start() error {
s.Lock()
defer s.Unlock()
if s.running {
return nil
}
// reset
s.err = nil
s.closed = make(chan bool)
// TODO: pull source & build binary
log.Debugf("Runtime service %s forking new process\n")
p, err := s.Process.Fork(s.Exec)
if err != nil {
return err
}
// set the pid
s.PID = p
// set to running
s.running = true
if s.output != nil {
s.streamOutput()
}
// wait and watch
go s.Wait()
return nil
}
func (s *service) Stop() error {
s.Lock()
defer s.Unlock()
select {
case <-s.closed:
return nil
default:
close(s.closed)
s.running = false
return s.Process.Kill(s.PID)
}
return nil
}
func (s *service) Error() error {
s.RLock()
defer s.RUnlock()
return s.err
}
func (s *service) Wait() {
// wait for process to exit
err := s.Process.Wait(s.PID)
s.Lock()
defer s.Unlock()
// save the error
if err != nil {
s.err = err
}
// no longer running
s.running = false
}
func (r *runtime) run() {
r.RLock()
closed := r.closed
r.RUnlock()
t := time.NewTicker(time.Second * 5)
defer t.Stop()
for {
select {
case <-t.C:
// check running services
r.RLock()
for _, service := range r.services {
if service.Running() {
continue
}
// TODO: check service error
log.Debugf("Runtime starting %s", service.Name)
if err := service.Start(); err != nil {
log.Debugf("Runtime error starting %s: %v", service.Name, err)
}
}
r.RUnlock()
case service := <-r.start:
if service.Running() {
continue
}
// TODO: check service error
log.Debugf("Starting %s", service.Name)
if err := service.Start(); err != nil {
log.Debugf("Runtime error starting %s: %v", service.Name, err)
}
case <-closed:
// TODO: stop all the things
return
}
}
}
func (r *runtime) Create(s *Service, opts ...CreateOption) error {
r.Lock()
defer r.Unlock()
if _, ok := r.services[s.Name]; ok {
return errors.New("service already registered")
}
var options CreateOptions
for _, o := range opts {
o(&options)
}
if len(s.Exec) == 0 && len(options.Command) == 0 {
return errors.New("missing exec command")
}
// save service
r.services[s.Name] = newService(s, options)
// push into start queue
r.start <- r.services[s.Name]
return nil
}
func (r *runtime) Delete(s *Service) error {
r.Lock()
defer r.Unlock()
if s, ok := r.services[s.Name]; ok {
delete(r.services, s.Name)
return s.Stop()
}
return nil
}
func (r *runtime) Start() error {
r.Lock()
defer r.Unlock()
// already running
if r.running {
return nil
}
// set running
r.running = true
r.closed = make(chan bool)
go r.run()
return nil
}
func (r *runtime) Stop() error {
r.Lock()
defer r.Unlock()
if !r.running {
return nil
}
select {
case <-r.closed:
return nil
default:
close(r.closed)
// set not running
r.running = false
// stop all the services
for _, service := range r.services {
log.Debugf("Runtime stopping %s", service.Name)
service.Stop()
}
}
return nil
}

40
runtime/options.go Normal file
View File

@ -0,0 +1,40 @@
package runtime
import (
"io"
)
type CreateOption func(o *CreateOptions)
type CreateOptions struct {
// command to execute including args
Command []string
// Environment to configure
Env []string
// Log output
Output io.Writer
}
// WithCommand specifies the command to execute
func WithCommand(c string, args ...string) CreateOption {
return func(o *CreateOptions) {
// set command
o.Command = []string{c}
// set args
o.Command = append(o.Command, args...)
}
}
// WithEnv sets the created service env
func WithEnv(env []string) CreateOption {
return func(o *CreateOptions) {
o.Env = env
}
}
// WithOutput sets the arg output
func WithOutput(out io.Writer) CreateOption {
return func(o *CreateOptions) {
o.Output = out
}
}

View File

@ -19,10 +19,11 @@ func (p *Process) Exec(exe *process.Executable) error {
} }
func (p *Process) Fork(exe *process.Executable) (*process.PID, error) { func (p *Process) Fork(exe *process.Executable) (*process.PID, error) {
cmd := exec.Command(exe.Binary.Path) // create command
if err := cmd.Start(); err != nil { cmd := exec.Command(exe.Binary.Path, exe.Args...)
return nil, err // set env vars
} cmd.Env = append(cmd.Env, exe.Env...)
in, err := cmd.StdinPipe() in, err := cmd.StdinPipe()
if err != nil { if err != nil {
return nil, err return nil, err
@ -36,6 +37,11 @@ func (p *Process) Fork(exe *process.Executable) (*process.PID, error) {
return nil, err return nil, err
} }
// start the process
if err := cmd.Start(); err != nil {
return nil, err
}
return &process.PID{ return &process.PID{
ID: fmt.Sprintf("%d", cmd.Process.Pid), ID: fmt.Sprintf("%d", cmd.Process.Pid),
Input: in, Input: in,

View File

@ -22,6 +22,10 @@ type Process interface {
type Executable struct { type Executable struct {
// The executable binary // The executable binary
Binary *packager.Binary Binary *packager.Binary
// The env variables
Env []string
// Args to pass
Args []string
} }
// PID is the running process // PID is the running process

View File

@ -0,0 +1,108 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/runtime/proto/runtime.proto
package go_micro_runtime
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 Runtime service
type RuntimeService interface {
Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error)
Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error)
}
type runtimeService struct {
c client.Client
name string
}
func NewRuntimeService(name string, c client.Client) RuntimeService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.runtime"
}
return &runtimeService{
c: c,
name: name,
}
}
func (c *runtimeService) Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) {
req := c.c.NewRequest(c.name, "Runtime.Create", in)
out := new(CreateResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runtimeService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) {
req := c.c.NewRequest(c.name, "Runtime.Delete", in)
out := new(DeleteResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Runtime service
type RuntimeHandler interface {
Create(context.Context, *CreateRequest, *CreateResponse) error
Delete(context.Context, *DeleteRequest, *DeleteResponse) error
}
func RegisterRuntimeHandler(s server.Server, hdlr RuntimeHandler, opts ...server.HandlerOption) error {
type runtime interface {
Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error
Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error
}
type Runtime struct {
runtime
}
h := &runtimeHandler{hdlr}
return s.Handle(s.NewHandler(&Runtime{h}, opts...))
}
type runtimeHandler struct {
RuntimeHandler
}
func (h *runtimeHandler) Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error {
return h.RuntimeHandler.Create(ctx, in, out)
}
func (h *runtimeHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error {
return h.RuntimeHandler.Delete(ctx, in, out)
}

366
runtime/proto/runtime.pb.go Normal file
View File

@ -0,0 +1,366 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/runtime/proto/runtime.proto
package go_micro_runtime
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 Service struct {
// name of the service
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// git url of the source
Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"`
// local path of the source
Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"`
// command to execute
Exec string `protobuf:"bytes,4,opt,name=exec,proto3" json:"exec,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Service) Reset() { *m = Service{} }
func (m *Service) String() string { return proto.CompactTextString(m) }
func (*Service) ProtoMessage() {}
func (*Service) Descriptor() ([]byte, []int) {
return fileDescriptor_efcf18966add108e, []int{0}
}
func (m *Service) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Service.Unmarshal(m, b)
}
func (m *Service) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Service.Marshal(b, m, deterministic)
}
func (m *Service) XXX_Merge(src proto.Message) {
xxx_messageInfo_Service.Merge(m, src)
}
func (m *Service) XXX_Size() int {
return xxx_messageInfo_Service.Size(m)
}
func (m *Service) XXX_DiscardUnknown() {
xxx_messageInfo_Service.DiscardUnknown(m)
}
var xxx_messageInfo_Service proto.InternalMessageInfo
func (m *Service) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Service) GetSource() string {
if m != nil {
return m.Source
}
return ""
}
func (m *Service) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
func (m *Service) GetExec() string {
if m != nil {
return m.Exec
}
return ""
}
type CreateRequest struct {
Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateRequest) Reset() { *m = CreateRequest{} }
func (m *CreateRequest) String() string { return proto.CompactTextString(m) }
func (*CreateRequest) ProtoMessage() {}
func (*CreateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_efcf18966add108e, []int{1}
}
func (m *CreateRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateRequest.Unmarshal(m, b)
}
func (m *CreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateRequest.Marshal(b, m, deterministic)
}
func (m *CreateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateRequest.Merge(m, src)
}
func (m *CreateRequest) XXX_Size() int {
return xxx_messageInfo_CreateRequest.Size(m)
}
func (m *CreateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CreateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CreateRequest proto.InternalMessageInfo
func (m *CreateRequest) GetService() *Service {
if m != nil {
return m.Service
}
return nil
}
type CreateResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateResponse) Reset() { *m = CreateResponse{} }
func (m *CreateResponse) String() string { return proto.CompactTextString(m) }
func (*CreateResponse) ProtoMessage() {}
func (*CreateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_efcf18966add108e, []int{2}
}
func (m *CreateResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateResponse.Unmarshal(m, b)
}
func (m *CreateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateResponse.Marshal(b, m, deterministic)
}
func (m *CreateResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateResponse.Merge(m, src)
}
func (m *CreateResponse) XXX_Size() int {
return xxx_messageInfo_CreateResponse.Size(m)
}
func (m *CreateResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CreateResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CreateResponse proto.InternalMessageInfo
type DeleteRequest struct {
Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DeleteRequest) Reset() { *m = DeleteRequest{} }
func (m *DeleteRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteRequest) ProtoMessage() {}
func (*DeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_efcf18966add108e, []int{3}
}
func (m *DeleteRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteRequest.Unmarshal(m, b)
}
func (m *DeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DeleteRequest.Marshal(b, m, deterministic)
}
func (m *DeleteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_DeleteRequest.Merge(m, src)
}
func (m *DeleteRequest) XXX_Size() int {
return xxx_messageInfo_DeleteRequest.Size(m)
}
func (m *DeleteRequest) XXX_DiscardUnknown() {
xxx_messageInfo_DeleteRequest.DiscardUnknown(m)
}
var xxx_messageInfo_DeleteRequest proto.InternalMessageInfo
func (m *DeleteRequest) GetService() *Service {
if m != nil {
return m.Service
}
return nil
}
type DeleteResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DeleteResponse) Reset() { *m = DeleteResponse{} }
func (m *DeleteResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteResponse) ProtoMessage() {}
func (*DeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_efcf18966add108e, []int{4}
}
func (m *DeleteResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteResponse.Unmarshal(m, b)
}
func (m *DeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DeleteResponse.Marshal(b, m, deterministic)
}
func (m *DeleteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_DeleteResponse.Merge(m, src)
}
func (m *DeleteResponse) XXX_Size() int {
return xxx_messageInfo_DeleteResponse.Size(m)
}
func (m *DeleteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_DeleteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*Service)(nil), "go.micro.runtime.Service")
proto.RegisterType((*CreateRequest)(nil), "go.micro.runtime.CreateRequest")
proto.RegisterType((*CreateResponse)(nil), "go.micro.runtime.CreateResponse")
proto.RegisterType((*DeleteRequest)(nil), "go.micro.runtime.DeleteRequest")
proto.RegisterType((*DeleteResponse)(nil), "go.micro.runtime.DeleteResponse")
}
func init() {
proto.RegisterFile("micro/go-micro/runtime/proto/runtime.proto", fileDescriptor_efcf18966add108e)
}
var fileDescriptor_efcf18966add108e = []byte{
// 240 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x91, 0x41, 0x4b, 0xc4, 0x30,
0x14, 0x84, 0xb7, 0xba, 0xb4, 0xf8, 0x44, 0x59, 0x72, 0x90, 0xe8, 0xc5, 0x25, 0x27, 0x11, 0xcc,
0x42, 0xfb, 0x13, 0xec, 0xd5, 0x4b, 0x3c, 0x7b, 0xa8, 0xe1, 0x51, 0x0b, 0xb6, 0xa9, 0x49, 0x2a,
0xfe, 0x23, 0xff, 0xa6, 0x24, 0x2f, 0x15, 0xaa, 0xf5, 0xe4, 0x6d, 0xde, 0x30, 0x7c, 0x33, 0x21,
0x70, 0xdb, 0x77, 0xda, 0x9a, 0x43, 0x6b, 0xee, 0x48, 0xd8, 0x69, 0xf0, 0x5d, 0x8f, 0x87, 0xd1,
0x1a, 0xff, 0x7d, 0xc9, 0x78, 0xb1, 0x5d, 0x6b, 0x64, 0x4c, 0xc9, 0xe4, 0x8b, 0x27, 0x28, 0x1e,
0xd1, 0xbe, 0x77, 0x1a, 0x19, 0x83, 0xed, 0xd0, 0xf4, 0xc8, 0xb3, 0x7d, 0x76, 0x73, 0xa2, 0xa2,
0x66, 0x17, 0x90, 0x3b, 0x33, 0x59, 0x8d, 0xfc, 0x28, 0xba, 0xe9, 0x0a, 0xd9, 0xb1, 0xf1, 0x2f,
0xfc, 0x98, 0xb2, 0x41, 0x07, 0x0f, 0x3f, 0x50, 0xf3, 0x2d, 0x79, 0x41, 0x8b, 0x1a, 0xce, 0xee,
0x2d, 0x36, 0x1e, 0x15, 0xbe, 0x4d, 0xe8, 0x3c, 0xab, 0xa0, 0x70, 0xd4, 0x17, 0x7b, 0x4e, 0xcb,
0x4b, 0xf9, 0x73, 0x93, 0x4c, 0x83, 0xd4, 0x9c, 0x14, 0x3b, 0x38, 0x9f, 0x29, 0x6e, 0x34, 0x83,
0xc3, 0xc0, 0xad, 0xf1, 0x15, 0xff, 0xcf, 0x9d, 0x29, 0xc4, 0x2d, 0x3f, 0x33, 0x28, 0x14, 0xc5,
0xd9, 0x03, 0xe4, 0xd4, 0xca, 0xae, 0x7f, 0xb3, 0x16, 0xaf, 0xba, 0xda, 0xff, 0x1d, 0x48, 0x83,
0x37, 0x01, 0x47, 0x65, 0x6b, 0xb8, 0xc5, 0x63, 0xd6, 0x70, 0xcb, 0x9d, 0x62, 0xf3, 0x9c, 0xc7,
0x1f, 0xad, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x9a, 0xb6, 0x5a, 0xe7, 0xff, 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
// RuntimeClient is the client API for Runtime service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RuntimeClient interface {
Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error)
Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error)
}
type runtimeClient struct {
cc *grpc.ClientConn
}
func NewRuntimeClient(cc *grpc.ClientConn) RuntimeClient {
return &runtimeClient{cc}
}
func (c *runtimeClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
out := new(CreateResponse)
err := c.cc.Invoke(ctx, "/go.micro.runtime.Runtime/Create", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *runtimeClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) {
out := new(DeleteResponse)
err := c.cc.Invoke(ctx, "/go.micro.runtime.Runtime/Delete", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RuntimeServer is the server API for Runtime service.
type RuntimeServer interface {
Create(context.Context, *CreateRequest) (*CreateResponse, error)
Delete(context.Context, *DeleteRequest) (*DeleteResponse, error)
}
func RegisterRuntimeServer(s *grpc.Server, srv RuntimeServer) {
s.RegisterService(&_Runtime_serviceDesc, srv)
}
func _Runtime_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RuntimeServer).Create(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.runtime.Runtime/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RuntimeServer).Create(ctx, req.(*CreateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Runtime_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RuntimeServer).Delete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.runtime.Runtime/Delete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RuntimeServer).Delete(ctx, req.(*DeleteRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Runtime_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.runtime.Runtime",
HandlerType: (*RuntimeServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Create",
Handler: _Runtime_Create_Handler,
},
{
MethodName: "Delete",
Handler: _Runtime_Delete_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "micro/go-micro/runtime/proto/runtime.proto",
}

View File

@ -0,0 +1,31 @@
syntax = "proto3";
package go.micro.runtime;
service Runtime {
rpc Create(CreateRequest) returns (CreateResponse) {};
rpc Delete(DeleteRequest) returns (DeleteResponse) {};
}
message Service {
// name of the service
string name = 1;
// git url of the source
string source = 2;
// local path of the source
string path = 3;
// command to execute
string exec = 4;
}
message CreateRequest {
Service service = 1;
}
message CreateResponse {}
message DeleteRequest {
Service service = 1;
}
message DeleteResponse {}

45
runtime/runtime.go Normal file
View File

@ -0,0 +1,45 @@
// Package runtime is a service runtime manager
package runtime
// Runtime is a service runtime manager
type Runtime interface {
// Registers a service
Create(*Service, ...CreateOption) error
// Remove a service
Delete(*Service) error
// starts the runtime
Start() error
// Shutdown the runtime
Stop() error
}
type Service struct {
// name of the service
Name string
// url location of source
Source string
// path to store source
Path string
// exec command
Exec string
}
var (
DefaultRuntime = newRuntime()
)
func Create(s *Service, opts ...CreateOption) error {
return DefaultRuntime.Create(s, opts...)
}
func Delete(s *Service) error {
return DefaultRuntime.Delete(s)
}
func Start() error {
return DefaultRuntime.Start()
}
func Stop() error {
return DefaultRuntime.Stop()
}

View File

@ -53,6 +53,8 @@ type grpcServer struct {
opts server.Options opts server.Options
handlers map[string]server.Handler handlers map[string]server.Handler
subscribers map[*subscriber][]broker.Subscriber subscribers map[*subscriber][]broker.Subscriber
// marks the serve as started
started bool
// used for first registration // used for first registration
registered bool registered bool
} }
@ -271,12 +273,12 @@ func (g *grpcServer) handler(srv interface{}, stream grpc.ServerStream) error {
g.rpc.mu.Unlock() g.rpc.mu.Unlock()
if service == nil { 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] mtype := service.method[methodName]
if mtype == nil { 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 // process unary
@ -336,6 +338,11 @@ func (g *grpcServer) processRequest(stream grpc.ServerStream, service *service,
// define the handler func // define the handler func
fn := func(ctx context.Context, req server.Request, rsp interface{}) error { fn := func(ctx context.Context, req server.Request, rsp interface{}) error {
defer func() {
if r := recover(); r != nil {
log.Logf("handler %s panic recovered, err: %s", mtype.method.Name, r)
}
}()
returnValues = function.Call([]reflect.Value{service.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)}) returnValues = function.Call([]reflect.Value{service.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)})
// The return value for the method is an error. // The return value for the method is an error.
@ -454,7 +461,10 @@ func (g *grpcServer) newCodec(contentType string) (codec.NewCodec, error) {
} }
func (g *grpcServer) Options() server.Options { func (g *grpcServer) Options() server.Options {
g.RLock()
opts := g.opts opts := g.opts
g.RUnlock()
return opts return opts
} }
@ -700,7 +710,14 @@ func (g *grpcServer) Deregister() error {
} }
func (g *grpcServer) Start() error { func (g *grpcServer) Start() error {
config := g.opts g.RLock()
if g.started {
g.RUnlock()
return nil
}
g.RUnlock()
config := g.Options()
// micro: config.Transport.Listen(config.Address) // micro: config.Transport.Listen(config.Address)
ts, err := net.Listen("tcp", config.Address) ts, err := net.Listen("tcp", config.Address)
@ -781,13 +798,34 @@ func (g *grpcServer) Start() error {
config.Broker.Disconnect() config.Broker.Disconnect()
}() }()
// mark the server as started
g.Lock()
g.started = true
g.Unlock()
return nil return nil
} }
func (g *grpcServer) Stop() error { func (g *grpcServer) Stop() error {
g.RLock()
if !g.started {
g.RUnlock()
return nil
}
g.RUnlock()
ch := make(chan error) ch := make(chan error)
g.exit <- ch g.exit <- ch
return <-ch
var err error
select {
case err = <-ch:
g.Lock()
g.started = false
g.Unlock()
}
return err
} }
func (g *grpcServer) String() string { func (g *grpcServer) String() string {

View File

@ -1,18 +1,15 @@
package grpc package grpc
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
"github.com/micro/go-micro/broker" "github.com/micro/go-micro/broker"
"github.com/micro/go-micro/codec"
"github.com/micro/go-micro/metadata" "github.com/micro/go-micro/metadata"
"github.com/micro/go-micro/registry" "github.com/micro/go-micro/registry"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
"github.com/micro/go-micro/util/buf"
) )
const ( const (
@ -175,7 +172,7 @@ func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broke
msg.Header["Content-Type"] = defaultContentType msg.Header["Content-Type"] = defaultContentType
ct = defaultContentType ct = defaultContentType
} }
cf, err := g.newCodec(ct) cf, err := g.newGRPCCodec(ct)
if err != nil { if err != nil {
return err return err
} }
@ -205,15 +202,7 @@ func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broke
req = req.Elem() req = req.Elem()
} }
b := buf.New(bytes.NewBuffer(msg.Body)) if err := cf.Unmarshal(msg.Body, req.Interface()); err != nil {
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 {
return err return err
} }

View File

@ -44,6 +44,8 @@ func newOptions(opt ...Option) Options {
opts := Options{ opts := Options{
Codecs: make(map[string]codec.NewCodec), Codecs: make(map[string]codec.NewCodec),
Metadata: map[string]string{}, Metadata: map[string]string{},
RegisterInterval: DefaultRegisterInterval,
RegisterTTL: DefaultRegisterTTL,
} }
for _, o := range opt { for _, o := range opt {

View File

@ -0,0 +1,108 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/server/proto/server.proto
package go_micro_server
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 Server service
type ServerService interface {
Handle(ctx context.Context, in *HandleRequest, opts ...client.CallOption) (*HandleResponse, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (*SubscribeResponse, error)
}
type serverService struct {
c client.Client
name string
}
func NewServerService(name string, c client.Client) ServerService {
if c == nil {
c = client.NewClient()
}
if len(name) == 0 {
name = "go.micro.server"
}
return &serverService{
c: c,
name: name,
}
}
func (c *serverService) Handle(ctx context.Context, in *HandleRequest, opts ...client.CallOption) (*HandleResponse, error) {
req := c.c.NewRequest(c.name, "Server.Handle", in)
out := new(HandleResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *serverService) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...client.CallOption) (*SubscribeResponse, error) {
req := c.c.NewRequest(c.name, "Server.Subscribe", in)
out := new(SubscribeResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Server service
type ServerHandler interface {
Handle(context.Context, *HandleRequest, *HandleResponse) error
Subscribe(context.Context, *SubscribeRequest, *SubscribeResponse) error
}
func RegisterServerHandler(s server.Server, hdlr ServerHandler, opts ...server.HandlerOption) error {
type server interface {
Handle(ctx context.Context, in *HandleRequest, out *HandleResponse) error
Subscribe(ctx context.Context, in *SubscribeRequest, out *SubscribeResponse) error
}
type Server struct {
server
}
h := &serverHandler{hdlr}
return s.Handle(s.NewHandler(&Server{h}, opts...))
}
type serverHandler struct {
ServerHandler
}
func (h *serverHandler) Handle(ctx context.Context, in *HandleRequest, out *HandleResponse) error {
return h.ServerHandler.Handle(ctx, in, out)
}
func (h *serverHandler) Subscribe(ctx context.Context, in *SubscribeRequest, out *SubscribeResponse) error {
return h.ServerHandler.Subscribe(ctx, in, out)
}

314
server/proto/server.pb.go Normal file
View File

@ -0,0 +1,314 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/server/proto/server.proto
package go_micro_server
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 HandleRequest struct {
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HandleRequest) Reset() { *m = HandleRequest{} }
func (m *HandleRequest) String() string { return proto.CompactTextString(m) }
func (*HandleRequest) ProtoMessage() {}
func (*HandleRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_aabeb7f1c6b4fe84, []int{0}
}
func (m *HandleRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HandleRequest.Unmarshal(m, b)
}
func (m *HandleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HandleRequest.Marshal(b, m, deterministic)
}
func (m *HandleRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_HandleRequest.Merge(m, src)
}
func (m *HandleRequest) XXX_Size() int {
return xxx_messageInfo_HandleRequest.Size(m)
}
func (m *HandleRequest) XXX_DiscardUnknown() {
xxx_messageInfo_HandleRequest.DiscardUnknown(m)
}
var xxx_messageInfo_HandleRequest proto.InternalMessageInfo
func (m *HandleRequest) GetService() string {
if m != nil {
return m.Service
}
return ""
}
func (m *HandleRequest) GetEndpoint() string {
if m != nil {
return m.Endpoint
}
return ""
}
func (m *HandleRequest) GetProtocol() string {
if m != nil {
return m.Protocol
}
return ""
}
type HandleResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HandleResponse) Reset() { *m = HandleResponse{} }
func (m *HandleResponse) String() string { return proto.CompactTextString(m) }
func (*HandleResponse) ProtoMessage() {}
func (*HandleResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_aabeb7f1c6b4fe84, []int{1}
}
func (m *HandleResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HandleResponse.Unmarshal(m, b)
}
func (m *HandleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HandleResponse.Marshal(b, m, deterministic)
}
func (m *HandleResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_HandleResponse.Merge(m, src)
}
func (m *HandleResponse) XXX_Size() int {
return xxx_messageInfo_HandleResponse.Size(m)
}
func (m *HandleResponse) XXX_DiscardUnknown() {
xxx_messageInfo_HandleResponse.DiscardUnknown(m)
}
var xxx_messageInfo_HandleResponse proto.InternalMessageInfo
type SubscribeRequest struct {
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} }
func (m *SubscribeRequest) String() string { return proto.CompactTextString(m) }
func (*SubscribeRequest) ProtoMessage() {}
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_aabeb7f1c6b4fe84, []int{2}
}
func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SubscribeRequest.Unmarshal(m, b)
}
func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic)
}
func (m *SubscribeRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SubscribeRequest.Merge(m, src)
}
func (m *SubscribeRequest) XXX_Size() int {
return xxx_messageInfo_SubscribeRequest.Size(m)
}
func (m *SubscribeRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SubscribeRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo
func (m *SubscribeRequest) GetTopic() string {
if m != nil {
return m.Topic
}
return ""
}
type SubscribeResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SubscribeResponse) Reset() { *m = SubscribeResponse{} }
func (m *SubscribeResponse) String() string { return proto.CompactTextString(m) }
func (*SubscribeResponse) ProtoMessage() {}
func (*SubscribeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_aabeb7f1c6b4fe84, []int{3}
}
func (m *SubscribeResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SubscribeResponse.Unmarshal(m, b)
}
func (m *SubscribeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SubscribeResponse.Marshal(b, m, deterministic)
}
func (m *SubscribeResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SubscribeResponse.Merge(m, src)
}
func (m *SubscribeResponse) XXX_Size() int {
return xxx_messageInfo_SubscribeResponse.Size(m)
}
func (m *SubscribeResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SubscribeResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SubscribeResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*HandleRequest)(nil), "go.micro.server.HandleRequest")
proto.RegisterType((*HandleResponse)(nil), "go.micro.server.HandleResponse")
proto.RegisterType((*SubscribeRequest)(nil), "go.micro.server.SubscribeRequest")
proto.RegisterType((*SubscribeResponse)(nil), "go.micro.server.SubscribeResponse")
}
func init() {
proto.RegisterFile("micro/go-micro/server/proto/server.proto", fileDescriptor_aabeb7f1c6b4fe84)
}
var fileDescriptor_aabeb7f1c6b4fe84 = []byte{
// 228 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xcd, 0x4e, 0xc4, 0x20,
0x14, 0x85, 0xad, 0xc6, 0xea, 0xdc, 0x44, 0x1d, 0xd1, 0x05, 0xe9, 0xc2, 0x1f, 0x56, 0xdd, 0xc8,
0x24, 0xfa, 0x12, 0x26, 0xee, 0x66, 0x7c, 0x81, 0x29, 0xbd, 0x69, 0x48, 0x2a, 0x17, 0x81, 0xfa,
0x52, 0xbe, 0xa4, 0x29, 0x94, 0x46, 0x6b, 0x74, 0xc7, 0xc7, 0x39, 0xdc, 0x73, 0x2e, 0x50, 0xbf,
0x69, 0xe5, 0x68, 0xd3, 0xd1, 0x43, 0x3a, 0x78, 0x74, 0x1f, 0xe8, 0x36, 0xd6, 0x51, 0xc8, 0x20,
0x23, 0xb0, 0x8b, 0x8e, 0x64, 0xf4, 0xc8, 0x74, 0x2d, 0xf6, 0x70, 0xf6, 0xbc, 0x37, 0x6d, 0x8f,
0x5b, 0x7c, 0x1f, 0xd0, 0x07, 0xc6, 0xe1, 0x64, 0x94, 0xb4, 0x42, 0x5e, 0xdc, 0x15, 0xf5, 0x6a,
0x9b, 0x91, 0x55, 0x70, 0x8a, 0xa6, 0xb5, 0xa4, 0x4d, 0xe0, 0x87, 0x51, 0x9a, 0x79, 0xd4, 0x62,
0x80, 0xa2, 0x9e, 0x1f, 0x25, 0x2d, 0xb3, 0x58, 0xc3, 0x79, 0x8e, 0xf0, 0x96, 0x8c, 0x47, 0x51,
0xc3, 0x7a, 0x37, 0x34, 0x5e, 0x39, 0xdd, 0xcc, 0xb9, 0xd7, 0x70, 0x1c, 0xc8, 0x6a, 0x35, 0xa5,
0x26, 0x10, 0x57, 0x70, 0xf9, 0xcd, 0x99, 0x9e, 0x3f, 0x7e, 0x16, 0x50, 0xee, 0x62, 0x7d, 0xf6,
0x02, 0x65, 0x9a, 0xcd, 0x6e, 0xe4, 0x62, 0x35, 0xf9, 0x63, 0xaf, 0xea, 0xf6, 0x4f, 0x7d, 0x2a,
0x75, 0xc0, 0x5e, 0x61, 0x35, 0x87, 0xb1, 0xfb, 0x5f, 0xfe, 0x65, 0xe5, 0x4a, 0xfc, 0x67, 0xc9,
0x53, 0x9b, 0x32, 0x7e, 0xc4, 0xd3, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf9, 0xb8, 0x5c, 0x69,
0xa5, 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
// ServerClient is the client API for Server service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ServerClient interface {
Handle(ctx context.Context, in *HandleRequest, opts ...grpc.CallOption) (*HandleResponse, error)
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error)
}
type serverClient struct {
cc *grpc.ClientConn
}
func NewServerClient(cc *grpc.ClientConn) ServerClient {
return &serverClient{cc}
}
func (c *serverClient) Handle(ctx context.Context, in *HandleRequest, opts ...grpc.CallOption) (*HandleResponse, error) {
out := new(HandleResponse)
err := c.cc.Invoke(ctx, "/go.micro.server.Server/Handle", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *serverClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (*SubscribeResponse, error) {
out := new(SubscribeResponse)
err := c.cc.Invoke(ctx, "/go.micro.server.Server/Subscribe", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ServerServer is the server API for Server service.
type ServerServer interface {
Handle(context.Context, *HandleRequest) (*HandleResponse, error)
Subscribe(context.Context, *SubscribeRequest) (*SubscribeResponse, error)
}
func RegisterServerServer(s *grpc.Server, srv ServerServer) {
s.RegisterService(&_Server_serviceDesc, srv)
}
func _Server_Handle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HandleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ServerServer).Handle(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.server.Server/Handle",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ServerServer).Handle(ctx, req.(*HandleRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Server_Subscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SubscribeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ServerServer).Subscribe(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.server.Server/Subscribe",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ServerServer).Subscribe(ctx, req.(*SubscribeRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Server_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.server.Server",
HandlerType: (*ServerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Handle",
Handler: _Server_Handle_Handler,
},
{
MethodName: "Subscribe",
Handler: _Server_Subscribe_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "micro/go-micro/server/proto/server.proto",
}

22
server/proto/server.proto Normal file
View File

@ -0,0 +1,22 @@
syntax = "proto3";
package go.micro.server;
service Server {
rpc Handle(HandleRequest) returns (HandleResponse) {};
rpc Subscribe(SubscribeRequest) returns (SubscribeResponse) {};
}
message HandleRequest {
string service = 1;
string endpoint = 2;
string protocol = 3;
}
message HandleResponse {}
message SubscribeRequest {
string topic = 1;
}
message SubscribeResponse {}

View File

@ -18,6 +18,7 @@ type rpcCodec struct {
socket transport.Socket socket transport.Socket
codec codec.Codec codec codec.Codec
first bool first bool
protocol string
req *transport.Message req *transport.Message
buf *readWriteCloser buf *readWriteCloser
@ -154,16 +155,30 @@ func setupProtocol(msg *transport.Message) codec.NewCodec {
func newRpcCodec(req *transport.Message, socket transport.Socket, c codec.NewCodec) codec.Codec { func newRpcCodec(req *transport.Message, socket transport.Socket, c codec.NewCodec) codec.Codec {
rwc := &readWriteCloser{ rwc := &readWriteCloser{
rbuf: bytes.NewBuffer(req.Body), rbuf: bytes.NewBuffer(nil),
wbuf: bytes.NewBuffer(nil), wbuf: bytes.NewBuffer(nil),
} }
r := &rpcCodec{ r := &rpcCodec{
first: true,
buf: rwc, buf: rwc,
codec: c(rwc), codec: c(rwc),
req: req, req: req,
socket: socket, socket: socket,
protocol: "mucp",
} }
// if grpc pre-load the buffer
// TODO: remove this terrible hack
switch r.codec.String() {
case "grpc":
// set as first
r.first = true
// write the body
rwc.rbuf.Write(req.Body)
// set the protocol
r.protocol = "grpc"
}
return r return r
} }
@ -174,7 +189,7 @@ func (c *rpcCodec) ReadHeader(r *codec.Message, t codec.MessageType) error {
Body: c.req.Body, Body: c.req.Body,
} }
// if its a follow on request read it // first message could be pre-loaded
if !c.first { if !c.first {
var tm transport.Message var tm transport.Message
@ -199,7 +214,7 @@ func (c *rpcCodec) ReadHeader(r *codec.Message, t codec.MessageType) error {
c.req = &tm c.req = &tm
} }
// no longer first read // disable first
c.first = false c.first = false
// set some internal things // set some internal things
@ -300,5 +315,5 @@ func (c *rpcCodec) Close() error {
} }
func (c *rpcCodec) String() string { func (c *rpcCodec) String() string {
return "rpc" return c.protocol
} }

View File

@ -16,6 +16,7 @@ type rpcRequest struct {
body []byte body []byte
rawBody interface{} rawBody interface{}
stream bool stream bool
first bool
} }
type rpcMessage struct { type rpcMessage struct {
@ -54,9 +55,9 @@ func (r *rpcRequest) Body() interface{} {
func (r *rpcRequest) Read() ([]byte, error) { func (r *rpcRequest) Read() ([]byte, error) {
// got a body // got a body
if r.body != nil { if r.first {
b := r.body b := r.body
r.body = nil r.first = false
return b, nil return b, nil
} }

Some files were not shown because too many files have changed in this diff Show More