Merge branch 'master' of github.com:micro/go-micro

This commit is contained in:
Shu Xian 2019-09-11 00:39:19 +08:00
commit ce18de2647
60 changed files with 5272 additions and 526 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

@ -96,7 +96,15 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request,
} }
} }
c, err := r.pool.Get(address, transport.WithTimeout(opts.DialTimeout)) dOpts := []transport.DialOption{
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)
} }

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

@ -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

@ -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

@ -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)
``` ```

1005
network/default.go Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,111 @@
// Package handler implements network RPC handler
package handler
import (
"context"
"sort"
"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
}
// ListRoutes returns a list of routing table routes
func (n *Network) ListRoutes(ctx context.Context, req *pbRtr.Request, resp *pbRtr.ListResponse) 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
}
// ListNodes returns a list of all accessible nodes in the network
func (n *Network) ListNodes(ctx context.Context, req *pbNet.ListRequest, resp *pbNet.ListResponse) error {
nodes := n.Network.Nodes()
var respNodes []*pbNet.Node
for _, node := range nodes {
respNode := &pbNet.Node{
Id: node.Id(),
Address: node.Address(),
}
respNodes = append(respNodes, respNode)
}
resp.Nodes = respNodes
return nil
}
// Neighbourhood returns a list of immediate neighbours
func (n *Network) Neighbourhood(ctx context.Context, req *pbNet.NeighbourhoodRequest, resp *pbNet.NeighbourhoodResponse) error {
// extract the id of the node to query
id := req.Id
// if no id is passed, we assume local node
if id == "" {
id = n.Network.Id()
}
// get all the nodes in the network
nodes := n.Network.Nodes()
// sort the slice of nodes
sort.Slice(nodes, func(i, j int) bool { return nodes[i].Id() <= nodes[j].Id() })
// find a node with a given id
i := sort.Search(len(nodes), func(j int) bool { return nodes[j].Id() >= id })
var neighbours []*pbNet.Node
// collect all the nodes in the neighbourhood of the found node
if i < len(nodes) && nodes[i].Id() == id {
for _, neighbour := range nodes[i].Neighbourhood() {
// don't return yourself in response
if neighbour.Id() == n.Network.Id() {
continue
}
pbNeighbour := &pbNet.Node{
Id: neighbour.Id(),
Address: neighbour.Address(),
}
neighbours = append(neighbours, pbNeighbour)
}
}
// requested neighbourhood node
node := &pbNet.Node{
Id: nodes[i].Id(),
Address: nodes[i].Address(),
}
// creaate neighbourhood answer
neighbourhood := &pbNet.Neighbour{
Node: node,
Neighbours: neighbours,
}
resp.Neighbourhood = neighbourhood
return nil
}

View File

@ -1,2 +1,60 @@
// 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
// Neighbourhood is node neighbourhood
Neighbourhood() []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
// Nodes returns list of network nodes
Nodes() []Node
// 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...)
}

103
network/options.go Normal file
View File

@ -0,0 +1,103 @@
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
// Nodes is a list of seed nodes
Nodes []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
}
}
// Nodes is a list of seed nodes used along
// with resolved node
func Nodes(n ...string) Option {
return func(o *Options) {
o.Nodes = 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,126 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
proto1 "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 {
ListRoutes(ctx context.Context, in *proto1.Request, opts ...client.CallOption) (*proto1.ListResponse, error)
ListNodes(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, opts ...client.CallOption) (*NeighbourhoodResponse, 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) ListRoutes(ctx context.Context, in *proto1.Request, opts ...client.CallOption) (*proto1.ListResponse, error) {
req := c.c.NewRequest(c.name, "Network.ListRoutes", in)
out := new(proto1.ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) ListNodes(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
req := c.c.NewRequest(c.name, "Network.ListNodes", in)
out := new(ListResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *networkService) Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, opts ...client.CallOption) (*NeighbourhoodResponse, error) {
req := c.c.NewRequest(c.name, "Network.Neighbourhood", in)
out := new(NeighbourhoodResponse)
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 {
ListRoutes(context.Context, *proto1.Request, *proto1.ListResponse) error
ListNodes(context.Context, *ListRequest, *ListResponse) error
Neighbourhood(context.Context, *NeighbourhoodRequest, *NeighbourhoodResponse) error
}
func RegisterNetworkHandler(s server.Server, hdlr NetworkHandler, opts ...server.HandlerOption) error {
type network interface {
ListRoutes(ctx context.Context, in *proto1.Request, out *proto1.ListResponse) error
ListNodes(ctx context.Context, in *ListRequest, out *ListResponse) error
Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, out *NeighbourhoodResponse) error
}
type Network struct {
network
}
h := &networkHandler{hdlr}
return s.Handle(s.NewHandler(&Network{h}, opts...))
}
type networkHandler struct {
NetworkHandler
}
func (h *networkHandler) ListRoutes(ctx context.Context, in *proto1.Request, out *proto1.ListResponse) error {
return h.NetworkHandler.ListRoutes(ctx, in, out)
}
func (h *networkHandler) ListNodes(ctx context.Context, in *ListRequest, out *ListResponse) error {
return h.NetworkHandler.ListNodes(ctx, in, out)
}
func (h *networkHandler) Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, out *NeighbourhoodResponse) error {
return h.NetworkHandler.Neighbourhood(ctx, in, out)
}

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

@ -0,0 +1,438 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: network.proto
package go_micro_network
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
_ "github.com/micro/go-micro/router/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
// Empty request
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_8571034d60397816, []int{0}
}
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
// ListResponse is returned by ListNodes and ListNeighbours
type ListResponse struct {
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 *ListResponse) Reset() { *m = ListResponse{} }
func (m *ListResponse) String() string { return proto.CompactTextString(m) }
func (*ListResponse) ProtoMessage() {}
func (*ListResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{1}
}
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) GetNodes() []*Node {
if m != nil {
return m.Nodes
}
return nil
}
// NeighbourhoodRequest is sent to query node neighbourhood
type NeighbourhoodRequest struct {
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 *NeighbourhoodRequest) Reset() { *m = NeighbourhoodRequest{} }
func (m *NeighbourhoodRequest) String() string { return proto.CompactTextString(m) }
func (*NeighbourhoodRequest) ProtoMessage() {}
func (*NeighbourhoodRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{2}
}
func (m *NeighbourhoodRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NeighbourhoodRequest.Unmarshal(m, b)
}
func (m *NeighbourhoodRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NeighbourhoodRequest.Marshal(b, m, deterministic)
}
func (m *NeighbourhoodRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_NeighbourhoodRequest.Merge(m, src)
}
func (m *NeighbourhoodRequest) XXX_Size() int {
return xxx_messageInfo_NeighbourhoodRequest.Size(m)
}
func (m *NeighbourhoodRequest) XXX_DiscardUnknown() {
xxx_messageInfo_NeighbourhoodRequest.DiscardUnknown(m)
}
var xxx_messageInfo_NeighbourhoodRequest proto.InternalMessageInfo
func (m *NeighbourhoodRequest) GetId() string {
if m != nil {
return m.Id
}
return ""
}
// NeighbourhoodResponse contains node neighbourhood hierarchy
type NeighbourhoodResponse struct {
Neighbourhood *Neighbour `protobuf:"bytes,1,opt,name=neighbourhood,proto3" json:"neighbourhood,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NeighbourhoodResponse) Reset() { *m = NeighbourhoodResponse{} }
func (m *NeighbourhoodResponse) String() string { return proto.CompactTextString(m) }
func (*NeighbourhoodResponse) ProtoMessage() {}
func (*NeighbourhoodResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{3}
}
func (m *NeighbourhoodResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NeighbourhoodResponse.Unmarshal(m, b)
}
func (m *NeighbourhoodResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NeighbourhoodResponse.Marshal(b, m, deterministic)
}
func (m *NeighbourhoodResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_NeighbourhoodResponse.Merge(m, src)
}
func (m *NeighbourhoodResponse) XXX_Size() int {
return xxx_messageInfo_NeighbourhoodResponse.Size(m)
}
func (m *NeighbourhoodResponse) XXX_DiscardUnknown() {
xxx_messageInfo_NeighbourhoodResponse.DiscardUnknown(m)
}
var xxx_messageInfo_NeighbourhoodResponse proto.InternalMessageInfo
func (m *NeighbourhoodResponse) GetNeighbourhood() *Neighbour {
if m != nil {
return m.Neighbourhood
}
return nil
}
// Node is network node
type Node struct {
// node ide
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_8571034d60397816, []int{4}
}
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_8571034d60397816, []int{5}
}
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_8571034d60397816, []int{6}
}
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
}
// Solicit is sent when requesting route advertisement from the network nodes
type Solicit 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 *Solicit) Reset() { *m = Solicit{} }
func (m *Solicit) String() string { return proto.CompactTextString(m) }
func (*Solicit) ProtoMessage() {}
func (*Solicit) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{7}
}
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) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
// Neighbour is used to nnounce node neighbourhood
type Neighbour struct {
// network node
Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"`
// neighbours
Neighbours []*Node `protobuf:"bytes,3,rep,name=neighbours,proto3" json:"neighbours,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Neighbour) Reset() { *m = Neighbour{} }
func (m *Neighbour) String() string { return proto.CompactTextString(m) }
func (*Neighbour) ProtoMessage() {}
func (*Neighbour) Descriptor() ([]byte, []int) {
return fileDescriptor_8571034d60397816, []int{8}
}
func (m *Neighbour) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Neighbour.Unmarshal(m, b)
}
func (m *Neighbour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Neighbour.Marshal(b, m, deterministic)
}
func (m *Neighbour) XXX_Merge(src proto.Message) {
xxx_messageInfo_Neighbour.Merge(m, src)
}
func (m *Neighbour) XXX_Size() int {
return xxx_messageInfo_Neighbour.Size(m)
}
func (m *Neighbour) XXX_DiscardUnknown() {
xxx_messageInfo_Neighbour.DiscardUnknown(m)
}
var xxx_messageInfo_Neighbour proto.InternalMessageInfo
func (m *Neighbour) GetNode() *Node {
if m != nil {
return m.Node
}
return nil
}
func (m *Neighbour) GetNeighbours() []*Node {
if m != nil {
return m.Neighbours
}
return nil
}
func init() {
proto.RegisterType((*ListRequest)(nil), "go.micro.network.ListRequest")
proto.RegisterType((*ListResponse)(nil), "go.micro.network.ListResponse")
proto.RegisterType((*NeighbourhoodRequest)(nil), "go.micro.network.NeighbourhoodRequest")
proto.RegisterType((*NeighbourhoodResponse)(nil), "go.micro.network.NeighbourhoodResponse")
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((*Solicit)(nil), "go.micro.network.Solicit")
proto.RegisterType((*Neighbour)(nil), "go.micro.network.Neighbour")
}
func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) }
var fileDescriptor_8571034d60397816 = []byte{
// 360 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x41, 0x4f, 0xf2, 0x40,
0x10, 0xfd, 0x28, 0xf0, 0x35, 0x0c, 0x1f, 0x5f, 0xcc, 0x46, 0x4d, 0x53, 0x83, 0x21, 0x7b, 0x40,
0x62, 0xb4, 0x18, 0x08, 0x9e, 0xbc, 0x18, 0x0e, 0x5e, 0x08, 0x87, 0x7a, 0xf3, 0x66, 0xbb, 0x9b,
0xb2, 0x11, 0x3a, 0xb8, 0xbb, 0x8d, 0x7f, 0xc0, 0x1f, 0x6e, 0xba, 0x5d, 0xb0, 0x80, 0x60, 0xb8,
0x75, 0xe6, 0xbd, 0x37, 0x6f, 0xa7, 0xfb, 0x16, 0x5a, 0x29, 0xd7, 0x1f, 0x28, 0xdf, 0x82, 0xa5,
0x44, 0x8d, 0xe4, 0x24, 0xc1, 0x60, 0x21, 0x62, 0x89, 0x81, 0xed, 0xfb, 0xc3, 0x44, 0xe8, 0x59,
0x16, 0x05, 0x31, 0x2e, 0xfa, 0x06, 0xe9, 0x27, 0x78, 0x5b, 0x7c, 0x48, 0xcc, 0x34, 0x97, 0x7d,
0xa3, 0xb4, 0x45, 0x31, 0x86, 0xb6, 0xa0, 0x39, 0x11, 0x4a, 0x87, 0xfc, 0x3d, 0xe3, 0x4a, 0xd3,
0x07, 0xf8, 0x57, 0x94, 0x6a, 0x89, 0xa9, 0xe2, 0xe4, 0x06, 0xea, 0x29, 0x32, 0xae, 0xbc, 0x4a,
0xa7, 0xda, 0x6b, 0x0e, 0xce, 0x83, 0x6d, 0xd7, 0x60, 0x8a, 0x8c, 0x87, 0x05, 0x89, 0x76, 0xe1,
0x74, 0xca, 0x45, 0x32, 0x8b, 0x30, 0x93, 0x33, 0x44, 0x66, 0xa7, 0x92, 0xff, 0xe0, 0x08, 0xe6,
0x55, 0x3a, 0x95, 0x5e, 0x23, 0x74, 0x04, 0xa3, 0x2f, 0x70, 0xb6, 0xc5, 0xb3, 0x76, 0x8f, 0xf9,
0x96, 0x25, 0xc0, 0x68, 0x9a, 0x83, 0x8b, 0x1f, 0x6c, 0x57, 0xb4, 0x70, 0x53, 0x41, 0xef, 0xa0,
0x96, 0x1f, 0x69, 0xdb, 0x93, 0x78, 0xe0, 0xbe, 0x32, 0x26, 0xb9, 0x52, 0x9e, 0x63, 0x9a, 0xab,
0x92, 0x8e, 0xc0, 0x1d, 0x63, 0x9a, 0xf2, 0x58, 0x93, 0x6b, 0xa8, 0xe5, 0x9b, 0x58, 0xdb, 0x7d,
0xdb, 0x1a, 0x0e, 0x1d, 0x42, 0x7d, 0x3c, 0x47, 0xc5, 0x8f, 0x12, 0x8d, 0xc0, 0x7d, 0xc6, 0xb9,
0x88, 0xc5, 0x71, 0x5e, 0x08, 0x8d, 0xf5, 0xc2, 0xc7, 0x08, 0xc9, 0x3d, 0xc0, 0xfa, 0xf7, 0x28,
0xaf, 0x7a, 0xf0, 0x12, 0x4b, 0xcc, 0xc1, 0xa7, 0x03, 0xee, 0xb4, 0x00, 0xc9, 0x13, 0x80, 0xc9,
0x44, 0x1e, 0x1b, 0x45, 0xbc, 0x6f, 0xb5, 0x0d, 0x92, 0xbd, 0x65, 0xbf, 0xbd, 0x83, 0x94, 0xa3,
0x44, 0xff, 0x90, 0x09, 0x34, 0xf2, 0x4e, 0x6e, 0xa6, 0x48, 0x7b, 0xf7, 0x14, 0xa5, 0x20, 0xfa,
0x97, 0xfb, 0xe0, 0xf5, 0xb4, 0x08, 0x5a, 0x1b, 0x21, 0x22, 0xdd, 0x03, 0x29, 0x29, 0xa5, 0xd1,
0xbf, 0xfa, 0x95, 0xb7, 0xf2, 0x88, 0xfe, 0x9a, 0x47, 0x32, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff,
0x59, 0xcf, 0xab, 0xb5, 0x7c, 0x03, 0x00, 0x00,
}

View File

@ -0,0 +1,64 @@
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 {
rpc ListRoutes(go.micro.router.Request) returns (go.micro.router.ListResponse) {};
rpc ListNodes(ListRequest) returns (ListResponse) {};
rpc Neighbourhood(NeighbourhoodRequest) returns (NeighbourhoodResponse) {};
}
// Empty request
message ListRequest {}
// ListResponse is returned by ListNodes and ListNeighbours
message ListResponse {
repeated Node nodes = 1;
}
// NeighbourhoodRequest is sent to query node neighbourhood
message NeighbourhoodRequest {
string id = 1;
}
// NeighbourhoodResponse contains node neighbourhood hierarchy
message NeighbourhoodResponse {
Neighbour neighbourhood = 1;
}
// Node is network node
message Node {
// node ide
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;
}
// Solicit is sent when requesting route advertisement from the network nodes
message Solicit {
// network node
Node node = 1;
}
// Neighbour is used to nnounce node neighbourhood
message Neighbour {
// network node
Node node = 1;
// neighbours
repeated Node neighbours = 3;
}

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

@ -10,6 +10,7 @@ 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

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)
} }

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

@ -161,9 +161,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)
@ -219,6 +216,10 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server
// endpoint to call // endpoint to call
endpoint := req.Endpoint() endpoint := req.Endpoint()
if len(service) == 0 {
return errors.BadRequest("go.micro.proxy", "service name is blank")
}
// are we network routing or local routing // are we network routing or local routing
if len(p.Links) == 0 { if len(p.Links) == 0 {
local = true local = true

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

@ -14,6 +14,11 @@ import (
hash "github.com/mitchellh/hashstructure" hash "github.com/mitchellh/hashstructure"
) )
var (
// use a .micro domain rather than .local
mdnsDomain = "micro"
)
type mdnsTxt struct { type mdnsTxt struct {
Service string Service string
Version string Version string
@ -29,6 +34,8 @@ type mdnsEntry struct {
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 +43,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 +87,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")},
@ -141,7 +162,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 +235,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 +246,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 +313,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 +325,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 +361,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

@ -0,0 +1,224 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/registry/proto/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,848 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/registry/proto/registry.proto
package go_micro_registry
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
// 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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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_f287a6b809166ad2, []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 ""
}
func init() {
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")
}
func init() {
proto.RegisterFile("micro/go-micro/registry/proto/registry.proto", fileDescriptor_f287a6b809166ad2)
}
var fileDescriptor_f287a6b809166ad2 = []byte{
// 577 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x6d, 0x8b, 0xd3, 0x4c,
0x14, 0x6d, 0x92, 0xbe, 0xde, 0x6e, 0x9f, 0x47, 0x2f, 0xa2, 0x31, 0xbe, 0x95, 0x80, 0x52, 0xc1,
0xcd, 0x2e, 0x75, 0x11, 0x5f, 0x3e, 0x09, 0x5b, 0x17, 0x64, 0x57, 0x70, 0x04, 0xfd, 0x1c, 0x9b,
0x4b, 0x0d, 0x6e, 0x5e, 0x9c, 0x99, 0x16, 0xfa, 0x1f, 0x04, 0xff, 0x84, 0x3f, 0xc5, 0x1f, 0x26,
0x99, 0xcc, 0x34, 0x5d, 0x36, 0xa9, 0x1f, 0x56, 0xbf, 0xcd, 0xcd, 0x9c, 0x73, 0xe6, 0x9e, 0x73,
0x67, 0x5a, 0x78, 0x92, 0xc4, 0x73, 0x9e, 0x1d, 0x2c, 0xb2, 0xfd, 0x72, 0xc1, 0x69, 0x11, 0x0b,
0xc9, 0xd7, 0x07, 0x39, 0xcf, 0x64, 0x55, 0x06, 0xaa, 0xc4, 0xeb, 0x8b, 0x2c, 0x50, 0xb8, 0xc0,
0x6c, 0xf8, 0x3f, 0x6d, 0xe8, 0x7d, 0x20, 0xbe, 0x8a, 0xe7, 0x84, 0x08, 0xed, 0x34, 0x4c, 0xc8,
0xb5, 0xc6, 0xd6, 0x64, 0xc0, 0xd4, 0x1a, 0x5d, 0xe8, 0xad, 0x88, 0x8b, 0x38, 0x4b, 0x5d, 0x5b,
0x7d, 0x36, 0x25, 0x1e, 0x43, 0x3f, 0x21, 0x19, 0x46, 0xa1, 0x0c, 0x5d, 0x67, 0xec, 0x4c, 0x86,
0xd3, 0x49, 0x70, 0x49, 0x3f, 0xd0, 0xda, 0xc1, 0x99, 0x86, 0xce, 0x52, 0xc9, 0xd7, 0x6c, 0xc3,
0xc4, 0x17, 0x30, 0xa0, 0x34, 0xca, 0xb3, 0x38, 0x95, 0xc2, 0x6d, 0x2b, 0x99, 0x3b, 0x35, 0x32,
0x33, 0x8d, 0x61, 0x15, 0x1a, 0xf7, 0xa1, 0x93, 0x66, 0x11, 0x09, 0xb7, 0xa3, 0x68, 0xb7, 0x6a,
0x68, 0xef, 0xb2, 0x88, 0x58, 0x89, 0xf2, 0x5e, 0xc1, 0xe8, 0x42, 0x13, 0x78, 0x0d, 0x9c, 0xaf,
0xb4, 0xd6, 0x6e, 0x8b, 0x25, 0xde, 0x80, 0xce, 0x2a, 0x3c, 0x5f, 0x92, 0xb6, 0x5a, 0x16, 0x2f,
0xed, 0xe7, 0x96, 0xff, 0xcb, 0x82, 0x76, 0x21, 0x86, 0xff, 0x81, 0x1d, 0x47, 0x9a, 0x63, 0xc7,
0x51, 0x91, 0x4f, 0x18, 0x45, 0x9c, 0x84, 0x30, 0xf9, 0xe8, 0xb2, 0x48, 0x33, 0xcf, 0xb8, 0x74,
0x9d, 0xb1, 0x35, 0x71, 0x98, 0x5a, 0xe3, 0xeb, 0xad, 0xcc, 0x4a, 0xb3, 0x0f, 0x1b, 0xba, 0x6e,
0x0a, 0xec, 0x6a, 0x36, 0xbe, 0xdb, 0xd0, 0x37, 0x51, 0xd6, 0x8e, 0x7b, 0x0a, 0x3d, 0x4e, 0xdf,
0x96, 0x24, 0xa4, 0x22, 0x0f, 0xa7, 0x6e, 0x4d, 0x7f, 0x1f, 0x0b, 0x3d, 0x66, 0x80, 0x78, 0x04,
0x7d, 0x4e, 0x22, 0xcf, 0x52, 0x41, 0xca, 0xec, 0x2e, 0xd2, 0x06, 0x89, 0xb3, 0x4b, 0x51, 0x3c,
0xde, 0x31, 0xf7, 0x7f, 0x13, 0x47, 0x08, 0x1d, 0xd5, 0x56, 0x6d, 0x14, 0x08, 0x6d, 0xb9, 0xce,
0x0d, 0x4b, 0xad, 0xf1, 0x10, 0xba, 0x8a, 0x2d, 0xf4, 0x8d, 0x6f, 0x36, 0xaa, 0x71, 0xbe, 0x84,
0x2e, 0x23, 0xb1, 0x3c, 0x97, 0x78, 0x13, 0xba, 0xe1, 0x5c, 0x16, 0x0f, 0xa9, 0x3c, 0x45, 0x57,
0x78, 0x04, 0x3d, 0x51, 0x3e, 0x12, 0x1d, 0xb9, 0xd7, 0xfc, 0x8c, 0x98, 0x81, 0xe2, 0x5d, 0x18,
0xc8, 0x38, 0x21, 0x21, 0xc3, 0x24, 0xd7, 0x57, 0xac, 0xfa, 0xe0, 0xff, 0x0f, 0xa3, 0x59, 0x92,
0xcb, 0x35, 0xd3, 0x69, 0xfb, 0x8f, 0x00, 0x4e, 0x48, 0x32, 0x3d, 0x31, 0xb7, 0x3a, 0xb2, 0xec,
0xc5, 0x94, 0xfe, 0x0c, 0x86, 0x0a, 0xa7, 0x87, 0xf4, 0x0c, 0xfa, 0x7a, 0x47, 0xb8, 0x96, 0x72,
0xbc, 0xab, 0xb9, 0x0d, 0xd6, 0x1f, 0xc1, 0xf0, 0x34, 0x16, 0xe6, 0x3c, 0xff, 0x0d, 0xec, 0x95,
0xe5, 0x15, 0x65, 0x27, 0xb0, 0xf7, 0x29, 0x94, 0xf3, 0x2f, 0x7f, 0xf4, 0x31, 0xfd, 0xe1, 0x40,
0x9f, 0x69, 0x21, 0x3c, 0x53, 0xe6, 0xcd, 0xaf, 0xdc, 0xbd, 0x9a, 0xa3, 0xaa, 0x6c, 0xbc, 0xfb,
0x4d, 0xdb, 0x3a, 0xc9, 0x16, 0xbe, 0x35, 0xd2, 0xc4, 0x71, 0x47, 0xdf, 0xde, 0xb8, 0xee, 0x3e,
0x5f, 0x98, 0x4a, 0x0b, 0x4f, 0x01, 0x8e, 0x89, 0xff, 0x2d, 0xb5, 0xf7, 0x65, 0xce, 0x9a, 0x22,
0xb0, 0xce, 0xcb, 0xd6, 0x5c, 0xbc, 0x07, 0x8d, 0xfb, 0x1b, 0xc9, 0x13, 0xe8, 0xa8, 0xc8, 0xb1,
0x0e, 0xbb, 0x3d, 0x0c, 0xef, 0x76, 0x0d, 0xa0, 0xbc, 0xfa, 0x7e, 0xeb, 0xd0, 0xfa, 0xdc, 0x55,
0x7f, 0x41, 0x4f, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x0b, 0x12, 0xd6, 0xb2, 0x06, 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
// RegistryClient is the client API for Registry service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RegistryClient interface {
GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error)
Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error)
ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error)
}
type registryClient struct {
cc *grpc.ClientConn
}
func NewRegistryClient(cc *grpc.ClientConn) RegistryClient {
return &registryClient{cc}
}
func (c *registryClient) GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
out := new(GetResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/GetService", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
out := new(EmptyResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Register", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) {
out := new(EmptyResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Deregister", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
out := new(ListResponse)
err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/ListServices", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *registryClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error) {
stream, err := c.cc.NewStream(ctx, &_Registry_serviceDesc.Streams[0], "/go.micro.registry.Registry/Watch", opts...)
if err != nil {
return nil, err
}
x := &registryWatchClient{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 Registry_WatchClient interface {
Recv() (*Result, error)
grpc.ClientStream
}
type registryWatchClient struct {
grpc.ClientStream
}
func (x *registryWatchClient) Recv() (*Result, error) {
m := new(Result)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// RegistryServer is the server API for Registry service.
type RegistryServer 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(*WatchRequest, Registry_WatchServer) error
}
func RegisterRegistryServer(s *grpc.Server, srv RegistryServer) {
s.RegisterService(&_Registry_serviceDesc, srv)
}
func _Registry_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).GetService(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/GetService",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).GetService(ctx, req.(*GetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Service)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).Register(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/Register",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).Register(ctx, req.(*Service))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Service)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).Deregister(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/Deregister",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).Deregister(ctx, req.(*Service))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RegistryServer).ListServices(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/go.micro.registry.Registry/ListServices",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RegistryServer).ListServices(ctx, req.(*ListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Registry_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(WatchRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RegistryServer).Watch(m, &registryWatchServer{stream})
}
type Registry_WatchServer interface {
Send(*Result) error
grpc.ServerStream
}
type registryWatchServer struct {
grpc.ServerStream
}
func (x *registryWatchServer) Send(m *Result) error {
return x.ServerStream.SendMsg(m)
}
var _Registry_serviceDesc = grpc.ServiceDesc{
ServiceName: "go.micro.registry.Registry",
HandlerType: (*RegistryServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetService",
Handler: _Registry_GetService_Handler,
},
{
MethodName: "Register",
Handler: _Registry_Register_Handler,
},
{
MethodName: "Deregister",
Handler: _Registry_Deregister_Handler,
},
{
MethodName: "ListServices",
Handler: _Registry_ListServices_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Watch",
Handler: _Registry_Watch_Handler,
ServerStreams: true,
},
},
Metadata: "micro/go-micro/registry/proto/registry.proto",
}

View File

@ -0,0 +1,73 @@
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;
}

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

@ -0,0 +1,155 @@
// 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)
}
// use mdns to find the service registry
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: options.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

@ -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,7 +44,7 @@ var (
// router implements default router // router implements default router
type router struct { type router struct {
sync.RWMutex sync.RWMutex
opts Options options Options
status Status status Status
table *table table *table
exit chan struct{} exit chan struct{}
@ -70,7 +71,7 @@ func newRouter(opts ...Option) Router {
status := Status{Code: Stopped, Error: nil} status := Status{Code: Stopped, Error: nil}
return &router{ return &router{
opts: options, options: options,
status: status, status: status,
table: newTable(), table: newTable(),
advertWg: &sync.WaitGroup{}, advertWg: &sync.WaitGroup{},
@ -85,7 +86,7 @@ func (r *router) Init(opts ...Option) error {
defer r.Unlock() defer r.Unlock()
for _, o := range opts { for _, o := range opts {
o(&r.opts) o(&r.options)
} }
return nil return nil
@ -94,10 +95,10 @@ func (r *router) Init(opts ...Option) error {
// Options returns router options // Options returns router options
func (r *router) Options() Options { func (r *router) Options() Options {
r.Lock() r.Lock()
opts := r.opts options := r.options
r.Unlock() r.Unlock()
return opts return options
} }
// Table returns routing table // Table returns routing table
@ -120,6 +121,9 @@ func (r *router) manageRoute(route Route, action string) error {
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 "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)
} }
@ -139,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,
} }
@ -278,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
@ -529,20 +535,22 @@ func (r *router) Start() error {
} }
// 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 {
e := fmt.Errorf("failed adding registry routes: %s", err) e := fmt.Errorf("failed adding registry routes: %s", err)
r.status = Status{Code: Error, Error: e} r.status = Status{Code: Error, Error: e}
return 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 {
@ -557,7 +565,7 @@ func (r *router) Start() error {
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 {
e := fmt.Errorf("failed creating registry watcher: %v", err) e := fmt.Errorf("failed creating registry watcher: %v", err)
r.status = Status{Code: Error, Error: e} r.status = Status{Code: Error, Error: e}
@ -595,25 +603,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
@ -646,7 +643,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
@ -669,6 +666,10 @@ func (r *router) Process(a *Advert) error {
}) })
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 {
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
@ -680,6 +681,43 @@ 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 // 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)

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),
} }
@ -58,6 +59,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),
} }
@ -97,6 +99,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),
} }
@ -161,6 +164,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

@ -672,10 +672,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:"-"`
@ -734,6 +736,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
@ -861,51 +870,51 @@ func init() {
} }
var fileDescriptor_6a36eee0b1adf739 = []byte{ var fileDescriptor_6a36eee0b1adf739 = []byte{
// 689 bytes of a gzipped FileDescriptorProto // 699 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x4e, 0xdb, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x4e, 0xdb, 0x40,
0x10, 0xb6, 0x93, 0xd8, 0x28, 0xd3, 0x10, 0xdc, 0x51, 0x05, 0x56, 0x5a, 0x20, 0xf2, 0x29, 0x42, 0x10, 0xb6, 0x9d, 0xd8, 0x91, 0xa7, 0xc1, 0xb8, 0xa3, 0x0a, 0xac, 0xb4, 0x40, 0xe4, 0x53, 0x84,
0xd4, 0xa9, 0xd2, 0x6b, 0xff, 0x02, 0xa5, 0xaa, 0x54, 0x0e, 0xad, 0x0b, 0xea, 0xd9, 0xd8, 0x23, 0xa8, 0x53, 0xa5, 0xd7, 0xfe, 0x05, 0x4a, 0x55, 0xa9, 0x1c, 0x5a, 0x17, 0xd4, 0xb3, 0xb1, 0x57,
0x6a, 0x91, 0xd8, 0x66, 0x77, 0x03, 0xca, 0xb9, 0x8f, 0xd1, 0x27, 0xe8, 0x73, 0xf5, 0xda, 0x87, 0xd4, 0x22, 0xb1, 0xcd, 0xee, 0x06, 0x94, 0x73, 0x9f, 0xa6, 0xe7, 0x3e, 0x52, 0xaf, 0x7d, 0x88,
0xa8, 0xbc, 0xbb, 0x0e, 0x21, 0xc6, 0x48, 0x70, 0xf2, 0xce, 0xdf, 0x37, 0xff, 0x63, 0x18, 0x4c, 0xca, 0xbb, 0xeb, 0x10, 0x62, 0x8c, 0x44, 0x4e, 0xde, 0x99, 0xf9, 0xe6, 0x9b, 0x99, 0xdd, 0x99,
0x93, 0x88, 0x65, 0xc3, 0xf3, 0xec, 0xa5, 0x7a, 0xb0, 0x6c, 0x26, 0x88, 0x0d, 0x73, 0x96, 0x89, 0x31, 0x0c, 0xa6, 0x69, 0x4c, 0xf3, 0xe1, 0x45, 0xfe, 0x52, 0x1e, 0x68, 0x3e, 0xe3, 0x84, 0x0e,
0x92, 0xf0, 0x25, 0x81, 0x1b, 0xe7, 0x99, 0x2f, 0x75, 0x7c, 0xc5, 0xf6, 0xda, 0xb0, 0x16, 0xd0, 0x0b, 0x9a, 0xf3, 0x4a, 0x08, 0x84, 0x80, 0x9b, 0x17, 0x79, 0x20, 0x30, 0x81, 0x54, 0xfb, 0x36,
0xe5, 0x8c, 0xb8, 0xf0, 0xde, 0x41, 0xe7, 0x38, 0xe1, 0x22, 0x20, 0x9e, 0x67, 0x29, 0x27, 0xf4, 0x74, 0x42, 0x72, 0x35, 0x23, 0x8c, 0xfb, 0xef, 0xa0, 0x7b, 0x92, 0x32, 0x1e, 0x12, 0x56, 0xe4,
0xc1, 0x96, 0x4a, 0xdc, 0x35, 0xfb, 0xcd, 0xc1, 0x93, 0xd1, 0xa6, 0xbf, 0x62, 0xec, 0x07, 0xc5, 0x19, 0x23, 0x18, 0x80, 0x25, 0x40, 0xcc, 0xd3, 0xfb, 0xad, 0xc1, 0x93, 0xd1, 0x56, 0xb0, 0xe2,
0x27, 0xd0, 0x5a, 0xde, 0x5b, 0x58, 0x3f, 0xce, 0xb2, 0x8b, 0x59, 0xae, 0x01, 0x71, 0x1f, 0xac, 0x1c, 0x84, 0xe5, 0x27, 0x54, 0x28, 0xff, 0x2d, 0x6c, 0x9c, 0xe4, 0xf9, 0xe5, 0xac, 0x50, 0x84,
0xcb, 0x19, 0xb1, 0xb9, 0x6b, 0xf6, 0xcd, 0x3b, 0xed, 0xbf, 0x15, 0xd2, 0x40, 0x29, 0x79, 0x1f, 0x78, 0x00, 0xe6, 0xd5, 0x8c, 0xd0, 0xb9, 0xa7, 0xf7, 0xf5, 0x7b, 0xfd, 0xbf, 0x95, 0xd6, 0x50,
0xa0, 0x5b, 0x9a, 0x3f, 0x32, 0x80, 0x37, 0xd0, 0x51, 0x88, 0x8f, 0xf2, 0xff, 0x1e, 0xd6, 0xb5, 0x82, 0xfc, 0x0f, 0xe0, 0x54, 0xee, 0x6b, 0x26, 0xf0, 0x06, 0xba, 0x92, 0x71, 0xad, 0xf8, 0xef,
0xf5, 0x23, 0xdd, 0x77, 0xa1, 0xf3, 0x23, 0x14, 0xd1, 0xcf, 0xb2, 0x9e, 0x7f, 0x4c, 0xb0, 0xc7, 0x61, 0x43, 0x79, 0xaf, 0x19, 0xde, 0x81, 0xee, 0x8f, 0x88, 0xc7, 0x3f, 0xab, 0xfb, 0xfc, 0xad,
0xf1, 0x15, 0x31, 0x81, 0x5d, 0x68, 0x24, 0xb1, 0x0c, 0xa3, 0x1d, 0x34, 0x92, 0x18, 0x87, 0xd0, 0x83, 0x35, 0x4e, 0xae, 0x09, 0xe5, 0xe8, 0x80, 0x91, 0x26, 0x22, 0x0d, 0x3b, 0x34, 0xd2, 0x04,
0x12, 0xf3, 0x9c, 0xdc, 0x46, 0xdf, 0x1c, 0x74, 0x47, 0xcf, 0x2b, 0xc0, 0xca, 0xec, 0x64, 0x9e, 0x87, 0xd0, 0xe6, 0xf3, 0x82, 0x78, 0x46, 0x5f, 0x1f, 0x38, 0xa3, 0xe7, 0x35, 0x62, 0xe9, 0x76,
0x53, 0x20, 0x15, 0xf1, 0x05, 0xb4, 0x45, 0x32, 0x25, 0x2e, 0xc2, 0x69, 0xee, 0x36, 0xfb, 0xe6, 0x3a, 0x2f, 0x48, 0x28, 0x80, 0xf8, 0x02, 0x6c, 0x9e, 0x4e, 0x09, 0xe3, 0xd1, 0xb4, 0xf0, 0x5a,
0xa0, 0x19, 0xdc, 0x30, 0xd0, 0x81, 0xa6, 0x10, 0x13, 0xb7, 0x25, 0xf9, 0xc5, 0xb3, 0x88, 0x9d, 0x7d, 0x7d, 0xd0, 0x0a, 0x6f, 0x15, 0xe8, 0x42, 0x8b, 0xf3, 0x89, 0xd7, 0x16, 0xfa, 0xf2, 0x58,
0xae, 0x28, 0x15, 0xdc, 0xb5, 0x6a, 0x62, 0x3f, 0x2a, 0xc4, 0x81, 0xd6, 0xf2, 0x9e, 0xc2, 0xc6, 0xe6, 0x4e, 0xae, 0x49, 0xc6, 0x99, 0x67, 0x36, 0xe4, 0x7e, 0x5c, 0x9a, 0x43, 0x85, 0xf2, 0x9f,
0x57, 0x96, 0x45, 0xc4, 0x79, 0x99, 0xbe, 0xe7, 0x40, 0xf7, 0x90, 0x51, 0x28, 0x68, 0x99, 0xf3, 0xc2, 0xe6, 0x57, 0x9a, 0xc7, 0x84, 0xb1, 0xaa, 0x7c, 0xdf, 0x05, 0xe7, 0x88, 0x92, 0x88, 0x93,
0x91, 0x26, 0x74, 0x9b, 0x73, 0x9a, 0xc7, 0xcb, 0x3a, 0xbf, 0x4c, 0xb0, 0x24, 0x34, 0xfa, 0x3a, 0x65, 0xcd, 0x47, 0x32, 0x21, 0x77, 0x35, 0x67, 0x45, 0xb2, 0x8c, 0xf9, 0xa5, 0x83, 0x29, 0xa8,
0x47, 0x53, 0xe6, 0xd8, 0xbb, 0x3b, 0x80, 0xba, 0x14, 0x1b, 0xab, 0x29, 0xee, 0x83, 0x25, 0xed, 0x31, 0x50, 0x35, 0xea, 0xa2, 0xc6, 0xde, 0xfd, 0x09, 0x34, 0x95, 0x68, 0xac, 0x96, 0x78, 0x00,
0x64, 0xf2, 0xf5, 0xbd, 0x50, 0x4a, 0xde, 0x29, 0x58, 0xb2, 0x97, 0xe8, 0xc2, 0x1a, 0x27, 0x76, 0xa6, 0xf0, 0x13, 0xc5, 0x37, 0xbf, 0x85, 0x04, 0xf9, 0x67, 0x60, 0x8a, 0xb7, 0x44, 0x0f, 0x3a,
0x95, 0x44, 0xa4, 0xab, 0x5f, 0x92, 0x85, 0xe4, 0x3c, 0x14, 0x74, 0x1d, 0xce, 0xa5, 0xb3, 0x76, 0x8c, 0xd0, 0xeb, 0x34, 0x26, 0xea, 0xf6, 0x2b, 0xb1, 0xb4, 0x5c, 0x44, 0x9c, 0xdc, 0x44, 0x73,
0x50, 0x92, 0x85, 0x24, 0x25, 0x71, 0x9d, 0xb1, 0x0b, 0xe9, 0xac, 0x1d, 0x94, 0xa4, 0xf7, 0xdb, 0x11, 0xcc, 0x0e, 0x2b, 0xb1, 0xb4, 0x64, 0x84, 0xdf, 0xe4, 0xf4, 0x52, 0x04, 0xb3, 0xc3, 0x4a,
0x04, 0x4b, 0xfa, 0xb9, 0x1f, 0x37, 0x8c, 0x63, 0x46, 0x9c, 0x97, 0xb8, 0x9a, 0x5c, 0xf6, 0xd8, 0xf4, 0xff, 0xe8, 0x60, 0x8a, 0x38, 0x0f, 0xf3, 0x46, 0x49, 0x42, 0x09, 0x63, 0x15, 0xaf, 0x12,
0xac, 0xf5, 0xd8, 0xba, 0xe5, 0x11, 0x11, 0x5a, 0x93, 0x24, 0xbd, 0x70, 0x2d, 0xc9, 0x96, 0x6f, 0x97, 0x23, 0xb6, 0x1a, 0x23, 0xb6, 0xef, 0x44, 0xc4, 0x2d, 0xd5, 0x83, 0xd4, 0x33, 0x85, 0x41,
0xdc, 0x04, 0x7b, 0x4a, 0x82, 0x25, 0x91, 0x6b, 0xcb, 0x2a, 0x69, 0xca, 0x1b, 0x81, 0xfd, 0x5d, 0x49, 0x88, 0xd0, 0x9e, 0xa4, 0xd9, 0xa5, 0x67, 0x09, 0xad, 0x38, 0x97, 0xd8, 0x29, 0xe1, 0x34,
0x84, 0x62, 0xc6, 0x0b, 0xab, 0x28, 0x8b, 0xcb, 0xd0, 0xe4, 0x1b, 0x9f, 0x81, 0x45, 0x8c, 0x65, 0x8d, 0xbd, 0x8e, 0xb8, 0x3d, 0x25, 0xf9, 0x23, 0xb0, 0xbe, 0xf3, 0x88, 0xcf, 0x58, 0xe9, 0x15,
0x4c, 0x47, 0xa5, 0x08, 0x6f, 0x0c, 0x5d, 0x65, 0xb3, 0x98, 0xfa, 0x21, 0xd8, 0x5c, 0x72, 0xf4, 0xe7, 0x49, 0x95, 0xb2, 0x38, 0xe3, 0x33, 0x30, 0x09, 0xa5, 0x39, 0x55, 0xd9, 0x4a, 0xc1, 0x1f,
0xd6, 0x6c, 0x55, 0x2a, 0xad, 0x0d, 0xb4, 0xda, 0xde, 0x08, 0xe0, 0x66, 0x5c, 0x11, 0xa1, 0xab, 0x83, 0x23, 0x7d, 0x16, 0xd3, 0x30, 0x04, 0x8b, 0x09, 0x8d, 0x9a, 0xa6, 0xed, 0xda, 0x0b, 0x28,
0xa8, 0x71, 0x9a, 0x66, 0xb3, 0x34, 0x22, 0xc7, 0x40, 0x07, 0x3a, 0x8a, 0xa7, 0x66, 0xc5, 0x31, 0x07, 0x05, 0xdb, 0x1f, 0x01, 0xdc, 0xb6, 0x31, 0x22, 0x38, 0x52, 0x1a, 0x67, 0x59, 0x3e, 0xcb,
0xf7, 0x86, 0xd0, 0x5e, 0xb4, 0x1f, 0x01, 0x6c, 0x35, 0x68, 0x8e, 0x51, 0xbc, 0xd5, 0x88, 0x39, 0x62, 0xe2, 0x6a, 0xe8, 0x42, 0x57, 0xea, 0x64, 0x0f, 0xb9, 0xfa, 0xfe, 0x10, 0xec, 0x45, 0x5b,
0x66, 0xf1, 0xd6, 0x06, 0x8d, 0xd1, 0xbf, 0x06, 0xd8, 0xb2, 0xf2, 0x0c, 0xbf, 0x80, 0xad, 0xee, 0x20, 0x80, 0x25, 0x1b, 0xd0, 0xd5, 0xca, 0xb3, 0x6c, 0x3d, 0x57, 0x2f, 0xcf, 0xca, 0xc1, 0x18,
0x04, 0xee, 0x54, 0x42, 0xbb, 0x75, 0x7f, 0x7a, 0xbb, 0xb5, 0x72, 0x3d, 0xac, 0x06, 0x1e, 0x80, 0xfd, 0x33, 0xc0, 0x0a, 0xe5, 0x95, 0x7c, 0x01, 0x4b, 0xee, 0x0f, 0xdc, 0xad, 0xa5, 0x76, 0x67,
0x25, 0x77, 0x16, 0xb7, 0x2b, 0xba, 0xcb, 0xbb, 0xdc, 0xab, 0xd9, 0x1f, 0xcf, 0x78, 0x65, 0xe2, 0x2f, 0xf5, 0xf6, 0x1a, 0xed, 0xaa, 0x89, 0x35, 0x3c, 0x04, 0x53, 0xcc, 0x32, 0xee, 0xd4, 0xb0,
0x01, 0xb4, 0x55, 0x7a, 0x09, 0x27, 0x74, 0xab, 0x83, 0xa9, 0x21, 0xb6, 0x6a, 0xb6, 0x5c, 0x62, 0xcb, 0x33, 0xde, 0x6b, 0x98, 0x2b, 0x5f, 0x7b, 0xa5, 0xe3, 0x21, 0xd8, 0xb2, 0xbc, 0x94, 0x11,
0x7c, 0x82, 0x35, 0xbd, 0x7f, 0x58, 0xa7, 0xd7, 0xeb, 0x57, 0x04, 0xab, 0x2b, 0x6b, 0xe0, 0xd1, 0xf4, 0xea, 0x0d, 0xab, 0x28, 0xb6, 0x1b, 0xa6, 0x5f, 0x70, 0x7c, 0x82, 0x8e, 0x9a, 0x4b, 0x6c,
0x62, 0x06, 0xea, 0x03, 0xd9, 0xad, 0xeb, 0xe8, 0x02, 0x66, 0xf4, 0xb7, 0x01, 0xd6, 0x49, 0x78, 0xc2, 0xf5, 0xfa, 0x35, 0xc3, 0xea, 0x28, 0x6b, 0x78, 0xbc, 0xe8, 0x81, 0xe6, 0x44, 0xf6, 0x9a,
0x36, 0x21, 0x3c, 0x2c, 0x9b, 0x83, 0x35, 0x2b, 0x77, 0x07, 0xdc, 0xca, 0xd9, 0x30, 0x0a, 0x10, 0x5e, 0x74, 0x41, 0x33, 0xfa, 0x6b, 0x80, 0x79, 0x1a, 0x9d, 0x4f, 0x08, 0x1e, 0x55, 0x8f, 0x83,
0xd5, 0xd5, 0x07, 0x80, 0xac, 0x5c, 0x1a, 0x09, 0xa2, 0xc6, 0xe1, 0x01, 0x20, 0x2b, 0xc7, 0xc9, 0x0d, 0xa3, 0x78, 0x0f, 0xdd, 0xca, 0x3a, 0xd1, 0x4a, 0x12, 0xf9, 0xaa, 0x8f, 0x20, 0x59, 0xd9,
0xc0, 0x31, 0xb4, 0x8a, 0x7f, 0xdc, 0x3d, 0xd5, 0xa9, 0x0e, 0xc2, 0xf2, 0x4f, 0xd1, 0x33, 0xf0, 0x40, 0x82, 0x44, 0xb6, 0xc3, 0x23, 0x48, 0x56, 0x96, 0x96, 0x86, 0x63, 0x68, 0x97, 0xff, 0xbe,
0x73, 0x79, 0x5b, 0xb6, 0x6b, 0xfe, 0x27, 0x1a, 0x68, 0xa7, 0x4e, 0x5c, 0x22, 0x9d, 0xd9, 0xf2, 0x07, 0x6e, 0xa7, 0xde, 0x08, 0xcb, 0x3f, 0x4b, 0x5f, 0xc3, 0xcf, 0xd5, 0xce, 0xd9, 0x69, 0xf8,
0x9f, 0xfc, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8c, 0xd0, 0xc0, 0x27, 0xbf, 0x07, 0x00, 0xcf, 0x28, 0xa2, 0xdd, 0x26, 0x73, 0xc5, 0x74, 0x6e, 0x89, 0x7f, 0xf5, 0xeb, 0xff, 0x01, 0x00,
0x00, 0x00, 0xff, 0xff, 0xe2, 0xe9, 0xe2, 0x3b, 0xd7, 0x07, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.

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;
} }
@ -117,10 +123,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,6 +28,8 @@ 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
@ -31,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)
} }
@ -125,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

@ -220,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()

View File

@ -8,7 +8,14 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
// 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 +32,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
@ -63,8 +83,10 @@ func (t *table) Delete(r Route) error {
return ErrRouteNotFound return ErrRouteNotFound
} }
if _, ok := t.routes[service][sum]; ok {
delete(t.routes[service], sum) delete(t.routes[service], sum)
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
} }
@ -85,8 +107,10 @@ func (t *table) Update(r Route) error {
return nil return nil
} }
if _, ok := t.routes[service][sum]; !ok {
t.routes[service][sum] = r t.routes[service][sum] = r
go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r}) go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r})
}
return nil return nil
} }
@ -106,21 +130,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 +162,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 +207,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")
)

View File

@ -3,6 +3,7 @@ package micro
import ( import (
"os" "os"
"os/signal" "os/signal"
"strings"
"sync" "sync"
"syscall" "syscall"
@ -10,7 +11,9 @@ import (
"github.com/micro/go-micro/config/cmd" "github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/debug/handler" "github.com/micro/go-micro/debug/handler"
"github.com/micro/go-micro/metadata" "github.com/micro/go-micro/metadata"
"github.com/micro/go-micro/plugin"
"github.com/micro/go-micro/server" "github.com/micro/go-micro/server"
"github.com/micro/go-micro/util/log"
) )
type service struct { type service struct {
@ -44,6 +47,24 @@ func (s *service) Init(opts ...Option) {
} }
s.once.Do(func() { s.once.Do(func() {
// setup the plugins
for _, p := range strings.Split(os.Getenv("MICRO_PLUGIN"), ",") {
if len(p) == 0 {
continue
}
// load the plugin
c, err := plugin.Load(p)
if err != nil {
log.Fatal(err)
}
// initialise the plugin
if err := plugin.Init(c); err != nil {
log.Fatal(err)
}
}
// Initialise the command flags, overriding new service // Initialise the command flags, overriding new service
_ = s.opts.Cmd.Init( _ = s.opts.Cmd.Init(
cmd.Broker(&s.opts.Broker), cmd.Broker(&s.opts.Broker),

View File

@ -58,7 +58,7 @@ func (t *tunBroker) Disconnect() error {
func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.PublishOption) error { func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.PublishOption) error {
// TODO: this is probably inefficient, we might want to just maintain an open connection // TODO: this is probably inefficient, we might want to just maintain an open connection
// it may be easier to add broadcast to the tunnel // it may be easier to add broadcast to the tunnel
c, err := t.tunnel.Dial(topic) c, err := t.tunnel.Dial(topic, tunnel.DialMulticast())
if err != nil { if err != nil {
return err return err
} }

File diff suppressed because it is too large Load Diff

101
tunnel/link.go Normal file
View File

@ -0,0 +1,101 @@
package tunnel
import (
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/transport"
)
type link struct {
transport.Socket
sync.RWMutex
// unique id of this link e.g uuid
// which we define for ourselves
id string
// whether its a loopback connection
// this flag is used by the transport listener
// which accepts inbound quic connections
loopback bool
// whether its actually connected
// dialled side sets it to connected
// after sending the message. the
// listener waits for the connect
connected bool
// the last time we received a keepalive
// on this link from the remote side
lastKeepAlive time.Time
// channels keeps a mapping of channels and last seen
channels map[string]time.Time
// stop the link
closed chan bool
}
func newLink(s transport.Socket) *link {
l := &link{
Socket: s,
id: uuid.New().String(),
channels: make(map[string]time.Time),
closed: make(chan bool),
}
go l.run()
return l
}
func (l *link) run() {
t := time.NewTicker(time.Minute)
defer t.Stop()
for {
select {
case <-l.closed:
return
case <-t.C:
// drop any channel mappings older than 2 minutes
var kill []string
killTime := time.Minute * 2
l.RLock()
for ch, t := range l.channels {
if d := time.Since(t); d > killTime {
kill = append(kill, ch)
}
}
l.RUnlock()
// if nothing to kill don't bother with a wasted lock
if len(kill) == 0 {
continue
}
// kill the channels!
l.Lock()
for _, ch := range kill {
delete(l.channels, ch)
}
l.Unlock()
}
}
}
func (l *link) Id() string {
l.RLock()
defer l.RUnlock()
return l.id
}
func (l *link) Close() error {
select {
case <-l.closed:
return nil
default:
close(l.closed)
return nil
}
return nil
}

View File

@ -8,8 +8,13 @@ import (
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
) )
var (
// ErrLinkClosed is returned when attempting i/o operation on the closed link
ErrLinkClosed = errors.New("link closed")
)
// Link is a layer on top of a transport socket with the // Link is a layer on top of a transport socket with the
// buffering send and recv queue's with the ability to // buffering send and recv queues with the ability to
// measure the actual transport link and reconnect if // measure the actual transport link and reconnect if
// an address is specified. // an address is specified.
type Link interface { type Link interface {
@ -28,10 +33,6 @@ type Link interface {
Length() int Length() int
} }
var (
ErrLinkClosed = errors.New("link closed")
)
// NewLink creates a new link on top of a socket // NewLink creates a new link on top of a socket
func NewLink(opts ...options.Option) Link { func NewLink(opts ...options.Option) Link {
return newLink(options.NewOptions(opts...)) return newLink(options.NewOptions(opts...))

View File

@ -2,79 +2,148 @@ package tunnel
import ( import (
"io" "io"
"time"
"github.com/micro/go-micro/util/log" "github.com/micro/go-micro/util/log"
) )
type tunListener struct { type tunListener struct {
// address of the listener // address of the listener
addr string channel string
// the accept channel // the accept channel
accept chan *socket accept chan *session
// the channel to close // the channel to close
closed chan bool closed chan bool
// the tunnel closed channel // the tunnel closed channel
tunClosed chan bool tunClosed chan bool
// the connection // the listener session
conn Conn session *session
// the listener socket // del func to kill listener
socket *socket delFunc func()
}
// periodically announce self
func (t *tunListener) announce() {
tick := time.NewTicker(time.Second * 30)
defer tick.Stop()
// first announcement
t.session.Announce()
for {
select {
case <-tick.C:
t.session.Announce()
case <-t.closed:
return
}
}
} }
func (t *tunListener) process() { func (t *tunListener) process() {
// our connection map for session // our connection map for session
conns := make(map[string]*socket) conns := make(map[string]*session)
defer func() {
// close the sessions
for _, conn := range conns {
conn.Close()
}
}()
for { for {
select { select {
case <-t.closed: case <-t.closed:
return return
case <-t.tunClosed:
t.Close()
return
// receive a new message // receive a new message
case m := <-t.socket.recv: case m := <-t.session.recv:
// get a socket // get a session
sock, ok := conns[m.session] sess, ok := conns[m.session]
log.Debugf("Tunnel listener received id %s session %s exists: %t", m.id, m.session, ok) log.Debugf("Tunnel listener received channel %s session %s exists: %t", m.channel, m.session, ok)
if !ok { if !ok {
// create a new socket session switch m.typ {
sock = &socket{ case "open", "session":
// our tunnel id default:
id: m.id, continue
}
// create a new session session
sess = &session{
// the id of the remote side
tunnel: m.tunnel,
// the channel
channel: m.channel,
// the session id // the session id
session: m.session, session: m.session,
// is loopback conn
loopback: m.loopback,
// the link the message was received on
link: m.link,
// set multicast
multicast: m.multicast,
// close chan // close chan
closed: make(chan bool), closed: make(chan bool),
// recv called by the acceptor // recv called by the acceptor
recv: make(chan *message, 128), recv: make(chan *message, 128),
// use the internal send buffer // use the internal send buffer
send: t.socket.send, send: t.session.send,
// wait // wait
wait: make(chan bool), wait: make(chan bool),
// error channel
errChan: make(chan error, 1),
} }
// save the socket // save the session
conns[m.session] = sock conns[m.session] = sess
// send to accept chan
select { select {
case <-t.closed: case <-t.closed:
return return
case t.accept <- sock: // send to accept chan
case t.accept <- sess:
} }
} }
// an existing session was found
// received a close message
switch m.typ {
case "close":
select {
case <-sess.closed:
// no op
delete(conns, m.session)
default:
// close and delete session
close(sess.closed)
delete(conns, m.session)
}
// continue
continue
case "session":
// operate on this
default:
// non operational type
continue
}
// send this to the accept chan // send this to the accept chan
select { select {
case <-sock.closed: case <-sess.closed:
delete(conns, m.session) delete(conns, m.session)
case sock.recv <- m: case sess.recv <- m:
log.Debugf("Tunnel listener sent to recv chan id %s session %s", m.id, m.session) log.Debugf("Tunnel listener sent to recv chan channel %s session %s", m.channel, m.session)
} }
} }
} }
} }
func (t *tunListener) Addr() string { func (t *tunListener) Channel() string {
return t.addr return t.channel
} }
// Close closes tunnel listener // Close closes tunnel listener
@ -83,26 +152,33 @@ func (t *tunListener) Close() error {
case <-t.closed: case <-t.closed:
return nil return nil
default: default:
// close and delete
t.delFunc()
t.session.Close()
close(t.closed) close(t.closed)
} }
return nil return nil
} }
// Everytime accept is called we essentially block till we get a new connection // Everytime accept is called we essentially block till we get a new connection
func (t *tunListener) Accept() (Conn, error) { func (t *tunListener) Accept() (Session, error) {
select { select {
// if the socket is closed return // if the session is closed return
case <-t.closed: case <-t.closed:
return nil, io.EOF return nil, io.EOF
case <-t.tunClosed: case <-t.tunClosed:
// close the listener when the tunnel closes // close the listener when the tunnel closes
t.Close()
return nil, io.EOF return nil, io.EOF
// wait for a new connection // wait for a new connection
case c, ok := <-t.accept: case c, ok := <-t.accept:
// check if the accept chan is closed
if !ok { if !ok {
return nil, io.EOF return nil, io.EOF
} }
// send back the accept
if err := c.Accept(); err != nil {
return nil, err
}
return c, nil return c, nil
} }
return nil, nil return nil, nil

View File

@ -1,6 +1,8 @@
package tunnel package tunnel
import ( import (
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
"github.com/micro/go-micro/transport/quic" "github.com/micro/go-micro/transport/quic"
@ -9,6 +11,8 @@ import (
var ( var (
// DefaultAddress is default tunnel bind address // DefaultAddress is default tunnel bind address
DefaultAddress = ":0" DefaultAddress = ":0"
// The shared default token
DefaultToken = "micro"
) )
type Option func(*Options) type Option func(*Options)
@ -21,10 +25,21 @@ type Options struct {
Address string Address string
// Nodes are remote nodes // Nodes are remote nodes
Nodes []string Nodes []string
// The shared auth token
Token string
// Transport listens to incoming connections // Transport listens to incoming connections
Transport transport.Transport Transport transport.Transport
} }
type DialOption func(*DialOptions)
type DialOptions struct {
// specify a multicast connection
Multicast bool
// the dial timeout
Timeout time.Duration
}
// The tunnel id // The tunnel id
func Id(id string) Option { func Id(id string) Option {
return func(o *Options) { return func(o *Options) {
@ -46,6 +61,13 @@ func Nodes(n ...string) Option {
} }
} }
// Token sets the shared token for auth
func Token(t string) Option {
return func(o *Options) {
o.Token = t
}
}
// Transport listens for incoming connections // Transport listens for incoming connections
func Transport(t transport.Transport) Option { func Transport(t transport.Transport) Option {
return func(o *Options) { return func(o *Options) {
@ -58,6 +80,22 @@ func DefaultOptions() Options {
return Options{ return Options{
Id: uuid.New().String(), Id: uuid.New().String(),
Address: DefaultAddress, Address: DefaultAddress,
Token: DefaultToken,
Transport: quic.NewTransport(), Transport: quic.NewTransport(),
} }
} }
// Dial options
// Dial multicast sets the multicast option to send only to those mapped
func DialMulticast() DialOption {
return func(o *DialOptions) {
o.Multicast = true
}
}
func DialTimeout(t time.Duration) DialOption {
return func(o *DialOptions) {
o.Timeout = t
}
}

286
tunnel/session.go Normal file
View File

@ -0,0 +1,286 @@
package tunnel
import (
"errors"
"io"
"time"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/log"
)
// session is our pseudo session for transport.Socket
type session struct {
// the tunnel id
tunnel string
// the channel name
channel string
// the session id based on Micro.Tunnel-Session
session string
// closed
closed chan bool
// remote addr
remote string
// local addr
local string
// send chan
send chan *message
// recv chan
recv chan *message
// wait until we have a connection
wait chan bool
// if the discovery worked
discovered bool
// if the session was accepted
accepted bool
// outbound marks the session as outbound dialled connection
outbound bool
// lookback marks the session as a loopback on the inbound
loopback bool
// if the session is multicast
multicast bool
// if the session is broadcast
broadcast bool
// the timeout
timeout time.Duration
// the link on which this message was received
link string
// the error response
errChan chan error
}
// message is sent over the send channel
type message struct {
// type of message
typ string
// tunnel id
tunnel string
// channel name
channel string
// the session id
session string
// outbound marks the message as outbound
outbound bool
// loopback marks the message intended for loopback
loopback bool
// whether to send as multicast
multicast bool
// broadcast sets the broadcast type
broadcast bool
// the link to send the message on
link string
// transport data
data *transport.Message
// the error channel
errChan chan error
}
func (s *session) Remote() string {
return s.remote
}
func (s *session) Local() string {
return s.local
}
func (s *session) Id() string {
return s.session
}
func (s *session) Channel() string {
return s.channel
}
// newMessage creates a new message based on the session
func (s *session) newMessage(typ string) *message {
return &message{
typ: typ,
tunnel: s.tunnel,
channel: s.channel,
session: s.session,
outbound: s.outbound,
loopback: s.loopback,
multicast: s.multicast,
link: s.link,
errChan: s.errChan,
}
}
// Open will fire the open message for the session. This is called by the dialler.
func (s *session) Open() error {
// create a new message
msg := s.newMessage("open")
// send open message
s.send <- msg
// wait for an error response for send
select {
case err := <-msg.errChan:
if err != nil {
return err
}
case <-s.closed:
return io.EOF
}
// we don't wait on multicast
if s.multicast {
s.accepted = true
return nil
}
// now wait for the accept
select {
case msg = <-s.recv:
if msg.typ != "accept" {
log.Debugf("Received non accept message in Open %s", msg.typ)
return errors.New("failed to connect")
}
// set to accepted
s.accepted = true
// set link
s.link = msg.link
case <-time.After(s.timeout):
return ErrDialTimeout
case <-s.closed:
return io.EOF
}
return nil
}
// Accept sends the accept response to an open message from a dialled connection
func (s *session) Accept() error {
msg := s.newMessage("accept")
// send the accept message
select {
case <-s.closed:
return io.EOF
case s.send <- msg:
return nil
}
// wait for send response
select {
case err := <-s.errChan:
if err != nil {
return err
}
case <-s.closed:
return io.EOF
}
return nil
}
// Announce sends an announcement to notify that this session exists. This is primarily used by the listener.
func (s *session) Announce() error {
msg := s.newMessage("announce")
// we don't need an error back
msg.errChan = nil
// announce to all
msg.broadcast = true
// we don't need the link
msg.link = ""
select {
case s.send <- msg:
return nil
case <-s.closed:
return io.EOF
}
}
// Send is used to send a message
func (s *session) Send(m *transport.Message) error {
select {
case <-s.closed:
return io.EOF
default:
// no op
}
// make copy
data := &transport.Message{
Header: make(map[string]string),
Body: m.Body,
}
for k, v := range m.Header {
data.Header[k] = v
}
// create a new message
msg := s.newMessage("session")
// set the data
msg.data = data
// if multicast don't set the link
if s.multicast {
msg.link = ""
}
log.Debugf("Appending %+v to send backlog", msg)
// send the actual message
s.send <- msg
// wait for an error response
select {
case err := <-msg.errChan:
return err
case <-s.closed:
return io.EOF
}
return nil
}
// Recv is used to receive a message
func (s *session) Recv(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("session is closed")
default:
// no op
}
// recv from backlog
msg := <-s.recv
// check the error if one exists
select {
case err := <-msg.errChan:
return err
default:
}
log.Debugf("Received %+v from recv backlog", msg)
// set message
*m = *msg.data
// return nil
return nil
}
// Close closes the session by sending a close message
func (s *session) Close() error {
select {
case <-s.closed:
// no op
default:
close(s.closed)
// append to backlog
msg := s.newMessage("close")
// no error response on close
msg.errChan = nil
// send the close message
select {
case s.send <- msg:
default:
}
}
return nil
}

View File

@ -1,115 +0,0 @@
package tunnel
import (
"errors"
"github.com/micro/go-micro/transport"
"github.com/micro/go-micro/util/log"
)
// socket is our pseudo socket for transport.Socket
type socket struct {
// socket id based on Micro-Tunnel
id string
// the session id based on Micro.Tunnel-Session
session string
// closed
closed chan bool
// remote addr
remote string
// local addr
local string
// send chan
send chan *message
// recv chan
recv chan *message
// wait until we have a connection
wait chan bool
// outbound marks the socket as outbound
outbound bool
}
// message is sent over the send channel
type message struct {
// tunnel id
id string
// the session id
session string
// outbound marks the message as outbound
outbound bool
// transport data
data *transport.Message
}
func (s *socket) Remote() string {
return s.remote
}
func (s *socket) Local() string {
return s.local
}
func (s *socket) Id() string {
return s.id
}
func (s *socket) Session() string {
return s.session
}
func (s *socket) Send(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("socket is closed")
default:
// no op
}
// make copy
data := &transport.Message{
Header: make(map[string]string),
Body: m.Body,
}
for k, v := range m.Header {
data.Header[k] = v
}
// append to backlog
msg := &message{
id: s.id,
session: s.session,
outbound: s.outbound,
data: data,
}
log.Debugf("Appending %+v to send backlog", msg)
s.send <- msg
return nil
}
func (s *socket) Recv(m *transport.Message) error {
select {
case <-s.closed:
return errors.New("socket is closed")
default:
// no op
}
// recv from backlog
msg := <-s.recv
log.Debugf("Received %+v from recv backlog", msg)
// set message
*m = *msg.data
// return nil
return nil
}
// Close closes the socket
func (s *socket) Close() error {
select {
case <-s.closed:
// no op
default:
close(s.closed)
}
return nil
}

View File

@ -10,7 +10,7 @@ type tunListener struct {
} }
func (t *tunListener) Addr() string { func (t *tunListener) Addr() string {
return t.l.Addr() return t.l.Channel()
} }
func (t *tunListener) Close() error { func (t *tunListener) Close() error {

View File

@ -2,11 +2,23 @@
package tunnel package tunnel
import ( import (
"errors"
"time"
"github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport"
) )
// Tunnel creates a gre network tunnel on top of a link. var (
// It establishes multiple streams using the Micro-Tunnel-Id header // DefaultDialTimeout is the dial timeout if none is specified
DefaultDialTimeout = time.Second * 5
// ErrDialTimeout is returned by a call to Dial where the timeout occurs
ErrDialTimeout = errors.New("dial timeout")
// ErrDiscoverChan is returned when we failed to receive the "announce" back from a discovery
ErrDiscoverChan = errors.New("failed to discover channel")
)
// Tunnel creates a gre tunnel on top of the go-micro/transport.
// It establishes multiple streams using the Micro-Tunnel-Channel header
// and Micro-Tunnel-Session header. The tunnel id is a hash of // and Micro-Tunnel-Session header. The tunnel id is a hash of
// the address being requested. // the address being requested.
type Tunnel interface { type Tunnel interface {
@ -17,27 +29,37 @@ type Tunnel interface {
Connect() error Connect() error
// Close closes the tunnel // Close closes the tunnel
Close() error Close() error
// Dial an endpoint // Connect to a channel
Dial(addr string) (Conn, error) Dial(channel string, opts ...DialOption) (Session, error)
// Accept connections // Accept connections on a channel
Listen(addr string) (Listener, error) Listen(channel string) (Listener, error)
// All the links the tunnel is connected to
Links() []Link
// Name of the tunnel implementation // Name of the tunnel implementation
String() string String() string
} }
// The listener provides similar constructs to the transport.Listener // Link represents internal links to the tunnel
type Listener interface { type Link interface {
Addr() string // The id of the link
Close() error Id() string
Accept() (Conn, error) // honours transport socket
transport.Socket
} }
// Conn is a connection dialed or accepted which includes the tunnel id and session // The listener provides similar constructs to the transport.Listener
type Conn interface { type Listener interface {
// Specifies the tunnel id Accept() (Session, error)
Channel() string
Close() error
}
// Session is a unique session created when dialling or accepting connections on the tunnel
type Session interface {
// The unique session id
Id() string Id() string
// The session // The channel name
Session() string Channel() string
// a transport socket // a transport socket
transport.Socket transport.Socket
} }

View File

@ -187,30 +187,15 @@ func testBrokenTunAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.Wait
if err := c.Recv(m); err != nil { if err := c.Recv(m); err != nil {
t.Fatal(err) t.Fatal(err)
} }
tun.Close()
// re-start tunnel // close all the links
err = tun.Connect() for _, link := range tun.Links() {
if err != nil { link.Close()
t.Fatal(err)
}
defer tun.Close()
// listen on some virtual address
tl, err = tun.Listen("test-tunnel")
if err != nil {
t.Fatal(err)
} }
// receiver ready; notify sender // receiver ready; notify sender
wait <- true wait <- true
// accept a connection
c, err = tl.Accept()
if err != nil {
t.Fatal(err)
}
// accept the message // accept the message
m = new(transport.Message) m = new(transport.Message)
if err := c.Recv(m); err != nil { if err := c.Recv(m); err != nil {
@ -279,6 +264,7 @@ func TestReconnectTunnel(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer tunB.Close()
// we manually override the tunnel.ReconnectTime value here // we manually override the tunnel.ReconnectTime value here
// this is so that we make the reconnects faster than the default 5s // this is so that we make the reconnects faster than the default 5s
@ -289,6 +275,7 @@ func TestReconnectTunnel(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer tunA.Close()
wait := make(chan bool) wait := make(chan bool)

View File

@ -40,14 +40,20 @@ func Extract(addr string) (string, error) {
} }
var addrs []net.Addr var addrs []net.Addr
var loAddrs []net.Addr
for _, iface := range ifaces { for _, iface := range ifaces {
ifaceAddrs, err := iface.Addrs() ifaceAddrs, err := iface.Addrs()
if err != nil { if err != nil {
// ignore error, interface can dissapear from system // ignore error, interface can dissapear from system
continue continue
} }
if iface.Flags&net.FlagLoopback != 0 {
loAddrs = append(loAddrs, ifaceAddrs...)
continue
}
addrs = append(addrs, ifaceAddrs...) addrs = append(addrs, ifaceAddrs...)
} }
addrs = append(addrs, loAddrs...)
var ipAddr []byte var ipAddr []byte
var publicIP []byte var publicIP []byte

View File

@ -14,7 +14,7 @@ type Level int
const ( const (
LevelFatal Level = iota LevelFatal Level = iota
LevelInfo LevelInfo
LevelWarn LevelError
LevelDebug LevelDebug
LevelTrace LevelTrace
) )
@ -29,16 +29,16 @@ var (
func init() { func init() {
switch os.Getenv("MICRO_LOG_LEVEL") { switch os.Getenv("MICRO_LOG_LEVEL") {
case "trace":
level = LevelTrace
case "debug": case "debug":
level = LevelDebug level = LevelDebug
case "info": case "info":
level = LevelInfo level = LevelInfo
case "trace": case "error":
level = LevelTrace level = LevelError
case "fatal": case "fatal":
level = LevelFatal level = LevelFatal
case "warn":
level = LevelWarn
} }
} }
@ -98,14 +98,14 @@ func Infof(format string, v ...interface{}) {
WithLevelf(LevelInfo, format, v...) WithLevelf(LevelInfo, format, v...)
} }
// Warn provides warn level logging // Error provides warn level logging
func Warn(v ...interface{}) { func Error(v ...interface{}) {
WithLevel(LevelWarn, v...) WithLevel(LevelError, v...)
} }
// Warnf provides warn level logging // Errorf provides warn level logging
func Warnf(format string, v ...interface{}) { func Errorf(format string, v ...interface{}) {
WithLevelf(LevelWarn, format, v...) WithLevelf(LevelError, format, v...)
} }
// Fatal logs with Log and then exits with os.Exit(1) // Fatal logs with Log and then exits with os.Exit(1)