forked from cerc-io/plugeth
p2p: improve peer selection logic
This commit introduces a new (temporary) peer selection strategy based on random lookups. While we're here, also implement the TODOs in dialLoop.
This commit is contained in:
parent
7be05b4b9d
commit
22d1f0faf1
117
p2p/server.go
117
p2p/server.go
@ -3,6 +3,7 @@ package p2p
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -276,46 +277,58 @@ func (srv *Server) listenLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) dialLoop() {
|
func (srv *Server) dialLoop() {
|
||||||
|
var (
|
||||||
|
dialed = make(chan *discover.Node)
|
||||||
|
dialing = make(map[discover.NodeID]bool)
|
||||||
|
findresults = make(chan []*discover.Node)
|
||||||
|
refresh = time.NewTimer(0)
|
||||||
|
)
|
||||||
defer srv.loopWG.Done()
|
defer srv.loopWG.Done()
|
||||||
refresh := time.NewTicker(refreshPeersInterval)
|
|
||||||
defer refresh.Stop()
|
defer refresh.Stop()
|
||||||
|
|
||||||
|
// TODO: maybe limit number of active dials
|
||||||
|
dial := func(dest *discover.Node) {
|
||||||
|
srv.lock.Lock()
|
||||||
|
ok, _ := srv.checkPeer(dest.ID)
|
||||||
|
srv.lock.Unlock()
|
||||||
|
// Don't dial nodes that would fail the checks in addPeer.
|
||||||
|
// This is important because the connection handshake is a lot
|
||||||
|
// of work and we'd rather avoid doing that work for peers
|
||||||
|
// that can't be added.
|
||||||
|
if !ok || dialing[dest.ID] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialing[dest.ID] = true
|
||||||
|
srv.peerWG.Add(1)
|
||||||
|
go func() {
|
||||||
|
srv.dialNode(dest)
|
||||||
|
dialed <- dest
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
srv.ntab.Bootstrap(srv.BootstrapNodes)
|
srv.ntab.Bootstrap(srv.BootstrapNodes)
|
||||||
go srv.findPeers()
|
|
||||||
|
|
||||||
dialed := make(chan *discover.Node)
|
|
||||||
dialing := make(map[discover.NodeID]bool)
|
|
||||||
|
|
||||||
// TODO: limit number of active dials
|
|
||||||
// TODO: ensure only one findPeers goroutine is running
|
|
||||||
// TODO: pause findPeers when we're at capacity
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-refresh.C:
|
case <-refresh.C:
|
||||||
|
|
||||||
go srv.findPeers()
|
|
||||||
|
|
||||||
case dest := <-srv.peerConnect:
|
|
||||||
// avoid dialing nodes that are already connected.
|
|
||||||
// there is another check for this in addPeer,
|
|
||||||
// which runs after the handshake.
|
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
_, isconnected := srv.peers[dest.ID]
|
needpeers := len(srv.peers) < srv.MaxPeers
|
||||||
srv.lock.Unlock()
|
srv.lock.Unlock()
|
||||||
if isconnected || dialing[dest.ID] || dest.ID == srv.Self().ID {
|
if needpeers {
|
||||||
continue
|
go func() {
|
||||||
|
var target discover.NodeID
|
||||||
|
rand.Read(target[:])
|
||||||
|
findresults <- srv.ntab.Lookup(target)
|
||||||
|
}()
|
||||||
|
refresh.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
dialing[dest.ID] = true
|
case dest := <-srv.peerConnect:
|
||||||
srv.peerWG.Add(1)
|
dial(dest)
|
||||||
go func() {
|
case dests := <-findresults:
|
||||||
srv.dialNode(dest)
|
for _, dest := range dests {
|
||||||
// at this point, the peer has been added
|
dial(dest)
|
||||||
// or discarded. either way, we're not dialing it anymore.
|
}
|
||||||
dialed <- dest
|
refresh.Reset(refreshPeersInterval)
|
||||||
}()
|
|
||||||
|
|
||||||
case dest := <-dialed:
|
case dest := <-dialed:
|
||||||
delete(dialing, dest.ID)
|
delete(dialing, dest.ID)
|
||||||
|
|
||||||
@ -341,24 +354,6 @@ func (srv *Server) Self() *discover.Node {
|
|||||||
return srv.ntab.Self()
|
return srv.ntab.Self()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) findPeers() {
|
|
||||||
far := srv.Self().ID
|
|
||||||
for i := range far {
|
|
||||||
far[i] = ^far[i]
|
|
||||||
}
|
|
||||||
closeToSelf := srv.ntab.Lookup(srv.Self().ID)
|
|
||||||
farFromSelf := srv.ntab.Lookup(far)
|
|
||||||
|
|
||||||
for i := 0; i < len(closeToSelf) || i < len(farFromSelf); i++ {
|
|
||||||
if i < len(closeToSelf) {
|
|
||||||
srv.peerConnect <- closeToSelf[i]
|
|
||||||
}
|
|
||||||
if i < len(farFromSelf) {
|
|
||||||
srv.peerConnect <- farFromSelf[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) startPeer(fd net.Conn, dest *discover.Node) {
|
func (srv *Server) startPeer(fd net.Conn, dest *discover.Node) {
|
||||||
// TODO: handle/store session token
|
// TODO: handle/store session token
|
||||||
fd.SetDeadline(time.Now().Add(handshakeTimeout))
|
fd.SetDeadline(time.Now().Add(handshakeTimeout))
|
||||||
@ -368,7 +363,6 @@ func (srv *Server) startPeer(fd net.Conn, dest *discover.Node) {
|
|||||||
glog.V(logger.Debug).Infof("Handshake with %v failed: %v", fd.RemoteAddr(), err)
|
glog.V(logger.Debug).Infof("Handshake with %v failed: %v", fd.RemoteAddr(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.MsgReadWriter = &netWrapper{
|
conn.MsgReadWriter = &netWrapper{
|
||||||
wrapped: conn.MsgReadWriter,
|
wrapped: conn.MsgReadWriter,
|
||||||
conn: fd, rtimeout: frameReadTimeout, wtimeout: frameWriteTimeout,
|
conn: fd, rtimeout: frameReadTimeout, wtimeout: frameWriteTimeout,
|
||||||
@ -379,24 +373,27 @@ func (srv *Server) startPeer(fd net.Conn, dest *discover.Node) {
|
|||||||
p.politeDisconnect(reason)
|
p.politeDisconnect(reason)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// The handshakes are done and it passed all checks.
|
||||||
|
// Spawn the Peer loops.
|
||||||
|
go srv.runPeer(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) runPeer(p *Peer) {
|
||||||
glog.V(logger.Debug).Infof("Added %v\n", p)
|
glog.V(logger.Debug).Infof("Added %v\n", p)
|
||||||
srvjslog.LogJson(&logger.P2PConnected{
|
srvjslog.LogJson(&logger.P2PConnected{
|
||||||
RemoteId: fmt.Sprintf("%x", conn.ID[:]),
|
RemoteId: p.ID().String(),
|
||||||
RemoteAddress: fd.RemoteAddr().String(),
|
RemoteAddress: p.RemoteAddr().String(),
|
||||||
RemoteVersionString: conn.Name,
|
RemoteVersionString: p.Name(),
|
||||||
NumConnections: srv.PeerCount(),
|
NumConnections: srv.PeerCount(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if srv.newPeerHook != nil {
|
if srv.newPeerHook != nil {
|
||||||
srv.newPeerHook(p)
|
srv.newPeerHook(p)
|
||||||
}
|
}
|
||||||
discreason := p.run()
|
discreason := p.run()
|
||||||
srv.removePeer(p)
|
srv.removePeer(p)
|
||||||
|
|
||||||
glog.V(logger.Debug).Infof("Removed %v (%v)\n", p, discreason)
|
glog.V(logger.Debug).Infof("Removed %v (%v)\n", p, discreason)
|
||||||
srvjslog.LogJson(&logger.P2PDisconnected{
|
srvjslog.LogJson(&logger.P2PDisconnected{
|
||||||
RemoteId: fmt.Sprintf("%x", conn.ID[:]),
|
RemoteId: p.ID().String(),
|
||||||
NumConnections: srv.PeerCount(),
|
NumConnections: srv.PeerCount(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -404,6 +401,14 @@ func (srv *Server) startPeer(fd net.Conn, dest *discover.Node) {
|
|||||||
func (srv *Server) addPeer(id discover.NodeID, p *Peer) (bool, DiscReason) {
|
func (srv *Server) addPeer(id discover.NodeID, p *Peer) (bool, DiscReason) {
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
defer srv.lock.Unlock()
|
defer srv.lock.Unlock()
|
||||||
|
if ok, reason := srv.checkPeer(id); !ok {
|
||||||
|
return false, reason
|
||||||
|
}
|
||||||
|
srv.peers[id] = p
|
||||||
|
return true, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) checkPeer(id discover.NodeID) (bool, DiscReason) {
|
||||||
switch {
|
switch {
|
||||||
case !srv.running:
|
case !srv.running:
|
||||||
return false, DiscQuitting
|
return false, DiscQuitting
|
||||||
@ -413,9 +418,9 @@ func (srv *Server) addPeer(id discover.NodeID, p *Peer) (bool, DiscReason) {
|
|||||||
return false, DiscAlreadyConnected
|
return false, DiscAlreadyConnected
|
||||||
case id == srv.Self().ID:
|
case id == srv.Self().ID:
|
||||||
return false, DiscSelf
|
return false, DiscSelf
|
||||||
|
default:
|
||||||
|
return true, 0
|
||||||
}
|
}
|
||||||
srv.peers[id] = p
|
|
||||||
return true, 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) removePeer(p *Peer) {
|
func (srv *Server) removePeer(p *Peer) {
|
||||||
|
Loading…
Reference in New Issue
Block a user