2019-09-10 02:01:41 +03:00
|
|
|
package network
|
|
|
|
|
|
|
|
import (
|
2019-09-10 03:14:23 +03:00
|
|
|
"container/list"
|
2019-09-10 02:01:41 +03:00
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2019-09-10 20:32:25 +03:00
|
|
|
pb "github.com/micro/go-micro/network/proto"
|
2019-09-10 02:01:41 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// MaxDepth defines max depth of peer topology
|
2019-09-10 20:32:25 +03:00
|
|
|
MaxDepth uint = 3
|
2019-09-10 02:01:41 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// node is network node
|
|
|
|
type node struct {
|
|
|
|
sync.RWMutex
|
|
|
|
// id is node id
|
|
|
|
id string
|
|
|
|
// address is node address
|
|
|
|
address string
|
2019-09-10 03:14:23 +03:00
|
|
|
// peers are nodes with direct link to this node
|
|
|
|
peers map[string]*node
|
2019-09-10 02:01:41 +03:00
|
|
|
// network returns the node network
|
|
|
|
network Network
|
2019-09-10 03:14:23 +03:00
|
|
|
// lastSeen keeps track of node lifetime and updates
|
2019-09-10 02:01:41 +03:00
|
|
|
lastSeen time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// Id is node ide
|
|
|
|
func (n *node) Id() string {
|
|
|
|
return n.id
|
|
|
|
}
|
|
|
|
|
|
|
|
// Address returns node address
|
|
|
|
func (n *node) Address() string {
|
|
|
|
return n.address
|
|
|
|
}
|
|
|
|
|
|
|
|
// Network returns node network
|
|
|
|
func (n *node) Network() Network {
|
|
|
|
return n.network
|
|
|
|
}
|
|
|
|
|
2019-09-10 03:14:23 +03:00
|
|
|
// Nodes returns a slice if all nodes in node topology
|
|
|
|
func (n *node) Nodes() []Node {
|
|
|
|
// we need to freeze the network graph here
|
2019-09-12 01:03:27 +03:00
|
|
|
// otherwise we might get inconsisten results
|
2019-09-10 03:14:23 +03:00
|
|
|
n.RLock()
|
|
|
|
defer n.RUnlock()
|
|
|
|
|
2019-09-12 01:03:27 +03:00
|
|
|
// track the visited nodes
|
2019-09-10 15:31:02 +03:00
|
|
|
visited := make(map[string]*node)
|
|
|
|
// queue of the nodes to visit
|
|
|
|
queue := list.New()
|
|
|
|
|
2019-09-10 03:14:23 +03:00
|
|
|
// push node to the back of queue
|
|
|
|
queue.PushBack(n)
|
|
|
|
// mark the node as visited
|
|
|
|
visited[n.id] = n
|
|
|
|
|
|
|
|
// keep iterating over the queue until its empty
|
|
|
|
for queue.Len() > 0 {
|
|
|
|
// pop the node from the front of the queue
|
|
|
|
qnode := queue.Front()
|
|
|
|
// iterate through all of the node peers
|
|
|
|
// mark the visited nodes; enqueue the non-visted
|
|
|
|
for id, node := range qnode.Value.(*node).peers {
|
|
|
|
if _, ok := visited[id]; !ok {
|
|
|
|
visited[id] = node
|
|
|
|
queue.PushBack(node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// remove the node from the queue
|
|
|
|
queue.Remove(qnode)
|
|
|
|
}
|
|
|
|
|
2019-09-10 02:01:41 +03:00
|
|
|
var nodes []Node
|
2019-09-10 03:14:23 +03:00
|
|
|
// collect all the nodes and return them
|
|
|
|
for _, node := range visited {
|
|
|
|
nodes = append(nodes, node)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodes
|
|
|
|
}
|
|
|
|
|
2019-09-12 01:03:27 +03:00
|
|
|
// topology returns node topology down to given depth
|
|
|
|
func (n *node) topology(depth uint) *node {
|
2019-09-11 02:23:37 +03:00
|
|
|
// make a copy of yourself
|
|
|
|
node := &node{
|
|
|
|
id: n.id,
|
|
|
|
address: n.address,
|
|
|
|
peers: make(map[string]*node),
|
|
|
|
network: n.network,
|
|
|
|
}
|
|
|
|
|
|
|
|
// return if we reach requested depth or we have no more peers
|
|
|
|
if depth == 0 || len(n.peers) == 0 {
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
2019-09-12 01:03:27 +03:00
|
|
|
// decrement the depth
|
2019-09-11 02:23:37 +03:00
|
|
|
depth--
|
|
|
|
|
2019-09-12 01:03:27 +03:00
|
|
|
// iterate through our peers and update the node peers
|
2019-09-11 02:23:37 +03:00
|
|
|
for _, peer := range n.peers {
|
2019-09-12 01:03:27 +03:00
|
|
|
nodePeer := peer.topology(depth)
|
|
|
|
if _, ok := node.peers[nodePeer.id]; !ok {
|
|
|
|
node.peers[nodePeer.id] = nodePeer
|
|
|
|
}
|
2019-09-10 20:32:25 +03:00
|
|
|
}
|
|
|
|
|
2019-09-11 02:23:37 +03:00
|
|
|
return node
|
2019-09-10 20:32:25 +03:00
|
|
|
}
|
|
|
|
|
2019-09-12 01:03:27 +03:00
|
|
|
// Peers returns node peers
|
|
|
|
func (n *node) Peers() []Node {
|
|
|
|
n.RLock()
|
|
|
|
var peers []Node
|
|
|
|
for _, nodePeer := range n.peers {
|
|
|
|
peer := nodePeer.topology(MaxDepth)
|
|
|
|
peers = append(peers, peer)
|
|
|
|
}
|
|
|
|
n.RUnlock()
|
|
|
|
|
|
|
|
return peers
|
|
|
|
}
|
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
// updateTopology updates node peer topology down to given depth
|
|
|
|
func (n *node) updatePeerTopology(pbPeer *pb.Peer, depth uint) error {
|
|
|
|
n.Lock()
|
|
|
|
defer n.Unlock()
|
2019-09-10 02:01:41 +03:00
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
if pbPeer == nil {
|
|
|
|
return errors.New("peer not initialized")
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
// unpack Peer topology into *node
|
|
|
|
peer := unpackPeer(pbPeer, depth)
|
2019-09-10 02:01:41 +03:00
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
// update node peers with new topology
|
|
|
|
n.peers[pbPeer.Node.Id] = peer
|
2019-09-10 02:01:41 +03:00
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
return nil
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|
|
|
|
|
2019-09-10 20:32:25 +03:00
|
|
|
// unpackPeer unpacks pb.Peer into node topology of given depth
|
2019-09-11 20:56:28 +03:00
|
|
|
// NOTE: this function is not thread-safe
|
2019-09-10 20:32:25 +03:00
|
|
|
func unpackPeer(pbPeer *pb.Peer, depth uint) *node {
|
2019-09-10 03:14:23 +03:00
|
|
|
peerNode := &node{
|
|
|
|
id: pbPeer.Node.Id,
|
|
|
|
address: pbPeer.Node.Address,
|
|
|
|
peers: make(map[string]*node),
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|
|
|
|
|
2019-09-10 03:14:23 +03:00
|
|
|
// return if have either reached the depth or have no more peers
|
|
|
|
if depth == 0 || len(pbPeer.Peers) == 0 {
|
|
|
|
return peerNode
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// decrement the depth
|
|
|
|
depth--
|
|
|
|
|
2019-09-10 03:14:23 +03:00
|
|
|
peers := make(map[string]*node)
|
|
|
|
for _, pbPeer := range pbPeer.Peers {
|
|
|
|
peer := unpackPeer(pbPeer, depth)
|
|
|
|
peers[pbPeer.Node.Id] = peer
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|
|
|
|
|
2019-09-10 03:14:23 +03:00
|
|
|
peerNode.peers = peers
|
2019-09-10 02:01:41 +03:00
|
|
|
|
2019-09-10 03:14:23 +03:00
|
|
|
return peerNode
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
func peerTopology(peer Node, depth uint) *pb.Peer {
|
|
|
|
node := &pb.Node{
|
|
|
|
Id: peer.Id(),
|
|
|
|
Address: peer.Address(),
|
|
|
|
}
|
2019-09-11 20:56:28 +03:00
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
pbPeers := &pb.Peer{
|
|
|
|
Node: node,
|
|
|
|
Peers: make([]*pb.Peer, 0),
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
// return if we reached the end of topology or depth
|
|
|
|
if depth == 0 || len(peer.Peers()) == 0 {
|
|
|
|
return pbPeers
|
|
|
|
}
|
2019-09-10 03:14:23 +03:00
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
// decrement the depth
|
|
|
|
depth--
|
2019-09-10 02:01:41 +03:00
|
|
|
|
2019-09-12 01:56:57 +03:00
|
|
|
// iterate through peers of peers aka pops
|
|
|
|
for _, pop := range peer.Peers() {
|
|
|
|
peer := peerTopology(pop, depth)
|
|
|
|
pbPeers.Peers = append(pbPeers.Peers, peer)
|
|
|
|
}
|
|
|
|
|
|
|
|
return pbPeers
|
|
|
|
}
|
|
|
|
|
|
|
|
// PeersToProto returns node peers graph encoded into protobuf
|
|
|
|
func PeersToProto(root Node, peers []Node, depth uint) *pb.Peer {
|
|
|
|
// network node aka root node
|
|
|
|
node := &pb.Node{
|
|
|
|
Id: root.Id(),
|
|
|
|
Address: root.Address(),
|
|
|
|
}
|
|
|
|
// we will build proto topology into this
|
|
|
|
pbPeers := &pb.Peer{
|
|
|
|
Node: node,
|
|
|
|
Peers: make([]*pb.Peer, 0),
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, peer := range peers {
|
|
|
|
pbPeer := peerTopology(peer, depth)
|
|
|
|
pbPeers.Peers = append(pbPeers.Peers, pbPeer)
|
|
|
|
}
|
|
|
|
|
|
|
|
return pbPeers
|
2019-09-10 02:01:41 +03:00
|
|
|
}
|