dashboard, p2p: different handling of the peers that disconnect before handshake

This commit is contained in:
Kurkó Mihály 2018-09-13 15:41:14 +03:00
parent 80135738a6
commit f2e52e2fee
4 changed files with 193 additions and 166 deletions

View file

@ -83,8 +83,9 @@ func (m *NetworkMessage) getOrInitPeer(ip, id string) *Peer {
// PeerBundle contains information about the peers pertaining to an IP address. // PeerBundle contains information about the peers pertaining to an IP address.
type PeerBundle struct { type PeerBundle struct {
Location *GeoLocation `json:"location,omitempty"` // geographical information based on IP Location *GeoLocation `json:"location,omitempty"` // geographical information based on IP
Peers map[string]*Peer `json:"peers,omitempty"` // the peers' node id is used as key Peers map[string]*Peer `json:"peers,omitempty"` // the peers' node id is used as key
FailedPeers []*Peer `json:"failedPeers,omitempty"`
} }
// GeoLocation contains geographical information. // GeoLocation contains geographical information.

View file

@ -17,6 +17,8 @@
package dashboard package dashboard
import ( import (
"fmt"
"net"
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -24,7 +26,8 @@ import (
"github.com/mohae/deepcopy" "github.com/mohae/deepcopy"
) )
const eventBufferLimit = 128 // Maximum number of buffered peer events for each event type const eventBufferLimit = 128 // Maximum number of buffered peer events
const trafficEventBufferLimit = p2p.MeteredPeerLimit
// collectPeerData gathers data about the peers and sends it to the clients. // collectPeerData gathers data about the peers and sends it to the clients.
func (db *Dashboard) collectPeerData() { func (db *Dashboard) collectPeerData() {
@ -42,29 +45,27 @@ func (db *Dashboard) collectPeerData() {
var ( var (
// Peer event channels. // Peer event channels.
connectCh = make(chan p2p.PeerConnectEvent, eventBufferLimit) connectCh = make(chan p2p.PeerConnectEvent, eventBufferLimit)
handshakeCh = make(chan p2p.PeerHandshakeEvent, eventBufferLimit) failedCh = make(chan p2p.PeerFailedEvent, eventBufferLimit)
disconnectCh = make(chan p2p.PeerDisconnectEvent, eventBufferLimit) disconnectCh = make(chan p2p.PeerDisconnectEvent, eventBufferLimit)
//readCh = make(chan p2p.PeerReadEvent, eventBufferLimit) ingressCh = make(chan p2p.PeerTrafficEvent, trafficEventBufferLimit)
//writeCh = make(chan p2p.PeerWriteEvent, eventBufferLimit) egressCh = make(chan p2p.PeerTrafficEvent, trafficEventBufferLimit)
// Subscribe to peer events. // Subscribe to peer events.
subConnect = p2p.SubscribePeerConnectEvent(connectCh) subConnect = p2p.SubscribePeerConnectEvent(connectCh)
subHandshake = p2p.SubscribePeerHandshakeEvent(handshakeCh) subFailed = p2p.SubscribePeerFailedEvent(failedCh)
subDisconnect = p2p.SubscribePeerDisconnectEvent(disconnectCh) subDisconnect = p2p.SubscribePeerDisconnectEvent(disconnectCh)
//subRead = p2p.SubscribePeerReadEvent(readCh) subIngress = p2p.SubscribePeerIngressEvent(ingressCh)
//subWrite = p2p.SubscribePeerWriteEvent(writeCh) subEgress = p2p.SubscribePeerEgressEvent(egressCh)
) )
defer func() { defer func() {
// Unsubscribe at the end. // Unsubscribe at the end.
subConnect.Unsubscribe() subConnect.Unsubscribe()
subHandshake.Unsubscribe() subFailed.Unsubscribe()
subDisconnect.Unsubscribe() subDisconnect.Unsubscribe()
//subRead.Unsubscribe() subIngress.Unsubscribe()
//subWrite.Unsubscribe() subEgress.Unsubscribe()
}() }()
//go db.keepPeerHistoryClean(quit)
ticker := time.NewTicker(db.config.Refresh) ticker := time.NewTicker(db.config.Refresh)
defer ticker.Stop() defer ticker.Stop()
@ -74,30 +75,30 @@ func (db *Dashboard) collectPeerData() {
} }
for { for {
select { select {
//case event := <-connectCh: case event := <-connectCh:
// ip := event.IP.String() ip := event.IP
// p := diff.getOrInitPeer(ip, event.ID) p := diff.getOrInitPeer(ip, event.ID)
// if diff.PeerBundles[ip].Location == nil { if diff.PeerBundles[ip].Location == nil {
// db.peerLock.RLock() db.peerLock.RLock()
// lookup := db.history.Network.PeerBundles[ip] == nil || db.history.Network.PeerBundles[ip].Location == nil lookup := db.history.Network.PeerBundles[ip] == nil || db.history.Network.PeerBundles[ip].Location == nil
// db.peerLock.RUnlock() db.peerLock.RUnlock()
// if lookup { if lookup {
// location := db.geodb.Lookup(event.IP) location := db.geodb.Lookup(net.ParseIP(event.IP))
// diff.PeerBundles[ip].Location = &GeoLocation{ diff.PeerBundles[ip].Location = &GeoLocation{
// Country: location.Country.Names.English, Country: location.Country.Names.English,
// City: location.City.Names.English, City: location.City.Names.English,
// Latitude: location.Location.Latitude, Latitude: location.Location.Latitude,
// Longitude: location.Location.Longitude, Longitude: location.Location.Longitude,
// } }
// } }
// } }
// if p.Connected == nil { if p.Connected == nil {
// p.Connected = []time.Time{event.Connected} p.Connected = []time.Time{event.Connected}
// } else { } else {
// p.Connected = append(p.Connected, event.Connected) p.Connected = append(p.Connected, event.Connected)
// } }
//case event := <-handshakeCh: //case event := <-failedCh:
// ip := event.IP.String() // ip := event.IP
// p := diff.getOrInitPeer(ip, event.AutoID) // p := diff.getOrInitPeer(ip, event.AutoID)
// p.DefaultID = event.AutoID // p.DefaultID = event.AutoID
// if p.Handshake == nil { // if p.Handshake == nil {
@ -120,29 +121,31 @@ func (db *Dashboard) collectPeerData() {
// db.history.Network.PeerBundles[ip].Peers[event.ID] = hp // TODO (kurkomisi): Merge. // db.history.Network.PeerBundles[ip].Peers[event.ID] = hp // TODO (kurkomisi): Merge.
// db.peerLock.Unlock() // db.peerLock.Unlock()
// } // }
//case event := <-disconnectCh: case event := <-disconnectCh:
// p := diff.getOrInitPeer(event.IP.String(), event.ID) p := diff.getOrInitPeer(event.IP, event.ID)
// if p.Disconnected == nil { if p.Disconnected == nil {
// p.Disconnected = []time.Time{event.Disconnected} p.Disconnected = []time.Time{event.Disconnected}
// } else { } else {
// p.Disconnected = append(p.Disconnected, event.Disconnected) p.Disconnected = append(p.Disconnected, event.Disconnected)
// } }
//case event := <-readCh: case event := <-ingressCh:
// // Sum up the ingress between two updates. fmt.Println("ingress", event.IP, event.Amount)
// p := diff.getOrInitPeer(event.IP.String(), event.ID) // Sum up the ingress between two updates.
// if len(p.Ingress) <= 0 { p := diff.getOrInitPeer(event.IP, event.ID)
// p.Ingress = ChartEntries{&ChartEntry{Value: float64(event.Ingress)}} if len(p.Ingress) <= 0 {
// } else { p.Ingress = ChartEntries{&ChartEntry{Value: float64(event.Amount)}}
// p.Ingress[0].Value += float64(event.Ingress) } else {
// } p.Ingress[0].Value += float64(event.Amount)
//case event := <-writeCh: }
// // Sum up the egress between two updates. case event := <-egressCh:
// p := diff.getOrInitPeer(event.IP.String(), event.ID) fmt.Println("egress ", event.IP, event.Amount)
// if len(p.Egress) <= 0 { // Sum up the egress between two updates.
// p.Egress = ChartEntries{&ChartEntry{Value: float64(event.Egress)}} p := diff.getOrInitPeer(event.IP, event.ID)
// } else { if len(p.Egress) <= 0 {
// p.Egress[0].Value += float64(event.Egress) p.Egress = ChartEntries{&ChartEntry{Value: float64(event.Amount)}}
// } } else {
p.Egress[0].Value += float64(event.Amount)
}
case <-ticker.C: case <-ticker.C:
now := time.Now() now := time.Now()
// Merge the diff with the history. // Merge the diff with the history.
@ -206,18 +209,18 @@ func (db *Dashboard) collectPeerData() {
case err := <-subConnect.Err(): case err := <-subConnect.Err():
log.Warn("Peer connect subscription error", "err", err) log.Warn("Peer connect subscription error", "err", err)
return return
case err := <-subHandshake.Err(): case err := <-subFailed.Err():
log.Warn("Peer handshake subscription error", "err", err) log.Warn("Peer failed subscription error", "err", err)
return return
case err := <-subDisconnect.Err(): case err := <-subDisconnect.Err():
log.Warn("Peer disconnect subscription error", "err", err) log.Warn("Peer disconnect subscription error", "err", err)
return return
//case err := <-subRead.Err(): case err := <-subIngress.Err():
// log.Warn("Peer read subscription error", "err", err) log.Warn("Peer ingress subscription error", "err", err)
// return return
//case err := <-subWrite.Err(): case err := <-subEgress.Err():
// log.Warn("Peer write subscription error", "err", err) log.Warn("Peer egress subscription error", "err", err)
// return return
case errc := <-db.quit: case errc := <-db.quit:
errc <- nil errc <- nil
return return

View file

@ -56,17 +56,16 @@ var (
metricsFeed = new(peerMetricsFeed) // Peer event feed for metrics metricsFeed = new(peerMetricsFeed) // Peer event feed for metrics
meteredPeerAutoID uint64 // Used to create unique id for the metered connection before the handshake meteredPeerCount uint64
meteredPeerCount uint64
) )
// peerMetricsFeed delivers the peer metrics to the subscribed channels. // peerMetricsFeed delivers the peer metrics to the subscribed channels.
type peerMetricsFeed struct { type peerMetricsFeed struct {
connect event.Feed // Event feed to notify the connection of a peer connect event.Feed // Event feed to notify the connection and the successful handshake of a peer
handshake event.Feed // Event feed to notify the handshake with a peer ingress event.Feed // Event feed to notify the amount of read bytes of a peer
egress event.Feed // Event feed to notify the amount of written bytes of a peer
disconnect event.Feed // Event feed to notify the disconnection of a peer disconnect event.Feed // Event feed to notify the disconnection of a peer
read event.Feed // Event feed to notify the amount of read bytes of a peer failed event.Feed // Event feed to notify the connection of a peer and its disconnection before the handshake
write event.Feed // Event feed to notify the amount of written bytes of a peer
scope event.SubscriptionScope // Facility to unsubscribe all the subscriptions at once scope event.SubscriptionScope // Facility to unsubscribe all the subscriptions at once
@ -75,79 +74,86 @@ type peerMetricsFeed struct {
// PeerConnectEvent contains information about the connection of a peer. // PeerConnectEvent contains information about the connection of a peer.
type PeerConnectEvent struct { type PeerConnectEvent struct {
Key string IP string
ID string
Connected time.Time Connected time.Time
}
// PeerHandshakeEvent contains information about the handshake with a peer.
type PeerHandshakeEvent struct {
AutoKey string
Key string
Ingress int64
Egress int64
Handshake time.Time Handshake time.Time
} }
// PeerDisconnectEvent contains information about the disconnection of a peer. // PeerDisconnectEvent contains information about the disconnection of a peer.
type PeerDisconnectEvent struct { type PeerDisconnectEvent struct {
Key string IP string
Ingress int64 ID string
Egress int64
Disconnected time.Time Disconnected time.Time
} }
// PeerReadEvent contains information about the read operation of a peer. type PeerTrafficEvent struct {
type PeerTrafficEvent map[string]int64 IP string
ID string
Amount int64
}
type PeerFailedEvent struct {
IP string
Connected time.Time
Disconnected time.Time
}
// SubscribePeerConnectEvent registers a subscription of PeerConnectEvent // SubscribePeerConnectEvent registers a subscription of PeerConnectEvent
func SubscribePeerConnectEvent(ch chan<- PeerConnectEvent) event.Subscription { func SubscribePeerConnectEvent(ch chan<- PeerConnectEvent) event.Subscription {
return metricsFeed.scope.Track(metricsFeed.connect.Subscribe(ch)) return metricsFeed.scope.Track(metricsFeed.connect.Subscribe(ch))
} }
// SubscribePeerHandshakeEvent registers a subscription of PeerHandshakeEvent
func SubscribePeerHandshakeEvent(ch chan<- PeerHandshakeEvent) event.Subscription {
return metricsFeed.scope.Track(metricsFeed.handshake.Subscribe(ch))
}
// SubscribePeerDisconnectEvent registers a subscription of PeerDisconnectEvent // SubscribePeerDisconnectEvent registers a subscription of PeerDisconnectEvent
func SubscribePeerDisconnectEvent(ch chan<- PeerDisconnectEvent) event.Subscription { func SubscribePeerDisconnectEvent(ch chan<- PeerDisconnectEvent) event.Subscription {
return metricsFeed.scope.Track(metricsFeed.disconnect.Subscribe(ch)) return metricsFeed.scope.Track(metricsFeed.disconnect.Subscribe(ch))
} }
// SubscribePeerReadEvent registers a subscription of PeerReadEvent // SubscribePeerTrafficEvent registers a subscription of PeerTrafficEvent
func SubscribePeerReadEvent(ch chan<- PeerTrafficEvent) event.Subscription {
return metricsFeed.scope.Track(metricsFeed.read.Subscribe(ch)) func SubscribePeerIngressEvent(ch chan<- PeerTrafficEvent) event.Subscription {
return metricsFeed.scope.Track(metricsFeed.ingress.Subscribe(ch))
} }
// SubscribePeerWriteEvent registers a subscription of PeerWriteEvent func SubscribePeerEgressEvent(ch chan<- PeerTrafficEvent) event.Subscription {
func SubscribePeerWriteEvent(ch chan<- PeerTrafficEvent) event.Subscription { return metricsFeed.scope.Track(metricsFeed.egress.Subscribe(ch))
return metricsFeed.scope.Track(metricsFeed.write.Subscribe(ch))
} }
func startTrafficNotifier(refresh time.Duration) { // SubscribePeerFailedEvent registers a subscription of PeerFailedEvent
func SubscribePeerFailedEvent(ch chan<- PeerFailedEvent) event.Subscription {
return metricsFeed.scope.Track(metricsFeed.failed.Subscribe(ch))
}
func runMetricsFeedHelper(refresh time.Duration) {
metricsFeed.quit = make(chan chan error) metricsFeed.quit = make(chan chan error)
ticker := time.NewTicker(refresh) ticker := time.NewTicker(refresh)
defer ticker.Stop() defer ticker.Stop()
// It is possible to send all of the traffic events together, but it is risky to use pointers in the events.
trafficEventSender := func(prefix string, feed *event.Feed) func(name string, i interface{}) {
return func(name string, i interface{}) {
if m, ok := i.(metrics.Meter); ok {
// Trim the common prefix and split the peer specific part in order to get the ip and the node id.
if key := strings.Split(strings.TrimPrefix(name, prefix), "/"); len(key) == 2 {
feed.Send(PeerTrafficEvent{
IP: key[0],
ID: key[1],
Amount: m.Count(),
})
} else {
log.Warn("Invalid peer metrics name", "name", name)
}
}
}
}
sendIngress := trafficEventSender(MetricsRegistryIngressPrefix, &metricsFeed.ingress)
sendEgress := trafficEventSender(MetricsRegistryEgressPrefix, &metricsFeed.egress)
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
// send read and write PeerIngressRegistry.Each(sendIngress)
ingressEvents, egressEvents := make(PeerTrafficEvent), make(PeerTrafficEvent) PeerEgressRegistry.Each(sendEgress)
PeerIngressRegistry.Each(func(name string, i interface{}) {
if m, ok := i.(metrics.Meter); ok {
ingressEvents[strings.TrimPrefix(name, MetricsRegistryIngressPrefix)] = m.Count()
}
})
PeerEgressRegistry.Each(func(name string, i interface{}) {
if m, ok := i.(metrics.Meter); ok {
egressEvents[strings.TrimPrefix(name, MetricsRegistryEgressPrefix)] = m.Count()
}
})
metricsFeed.read.Send(ingressEvents)
metricsFeed.write.Send(egressEvents)
//fmt.Println(ingressEvents)
//fmt.Println(egressEvents)
//fmt.Println()
case errc := <-metricsFeed.quit: case errc := <-metricsFeed.quit:
errc <- nil errc <- nil
return return
@ -170,10 +176,11 @@ func closeMetricsFeed() {
// meteredConn is a wrapper around a net.Conn that meters both the // meteredConn is a wrapper around a net.Conn that meters both the
// inbound and outbound network traffic. // inbound and outbound network traffic.
type meteredConn struct { type meteredConn struct {
net.Conn // Network connection to wrap with metering net.Conn // Network connection to wrap with metering
ip string // The IP address of the peer
key string connected time.Time
ip string // The IP address of the peer
id string // The NodeID of the peer
ingressMeter metrics.Meter ingressMeter metrics.Meter
egressMeter metrics.Meter egressMeter metrics.Meter
@ -196,24 +203,18 @@ func newMeteredConn(conn net.Conn, ingress bool, ip net.IP) net.Conn {
log.Warn("Metered peer count reached the limit") log.Warn("Metered peer count reached the limit")
return conn return conn
} }
// Increment the metered peer count
atomic.AddUint64(&meteredPeerCount, 1) atomic.AddUint64(&meteredPeerCount, 1)
// Otherwise bump the connection counters and wrap the connection // Bump the connection counters and wrap the connection
if ingress { if ingress {
ingressConnectMeter.Mark(1) ingressConnectMeter.Mark(1)
} else { } else {
egressConnectMeter.Mark(1) egressConnectMeter.Mark(1)
} }
key := fmt.Sprintf("%s/%s", ip.String(), fmt.Sprintf("peer_%d", atomic.AddUint64(&meteredPeerAutoID, 1)))
metricsFeed.connect.Send(PeerConnectEvent{
Key: key,
Connected: time.Now(),
})
return &meteredConn{ return &meteredConn{
Conn: conn, Conn: conn,
key: key, ip: ip.String(),
ip: ip.String(), connected: time.Now(),
ingressMeter: metrics.NewRegisteredMeter(key, PeerIngressRegistry),
egressMeter: metrics.NewRegisteredMeter(key, PeerEgressRegistry),
} }
} }
@ -222,7 +223,11 @@ func newMeteredConn(conn net.Conn, ingress bool, ip net.IP) net.Conn {
func (c *meteredConn) Read(b []byte) (n int, err error) { func (c *meteredConn) Read(b []byte) (n int, err error) {
n, err = c.Conn.Read(b) n, err = c.Conn.Read(b)
ingressTrafficMeter.Mark(int64(n)) ingressTrafficMeter.Mark(int64(n))
c.ingressMeter.Mark(int64(n)) c.lock.RLock()
if c.ingressMeter != nil {
c.ingressMeter.Mark(int64(n))
}
c.lock.RUnlock()
return n, err return n, err
} }
@ -231,55 +236,73 @@ func (c *meteredConn) Read(b []byte) (n int, err error) {
func (c *meteredConn) Write(b []byte) (n int, err error) { func (c *meteredConn) Write(b []byte) (n int, err error) {
n, err = c.Conn.Write(b) n, err = c.Conn.Write(b)
egressTrafficMeter.Mark(int64(n)) egressTrafficMeter.Mark(int64(n))
c.egressMeter.Mark(int64(n)) c.lock.RLock()
if c.egressMeter != nil {
c.egressMeter.Mark(int64(n))
}
c.lock.RUnlock()
return n, err return n, err
} }
// Close closes the underlying connection. // Close closes the underlying connection.
func (c *meteredConn) Close() error { func (c *meteredConn) Close() error {
// Decrement the metered peer count. // Decrement the metered peer count
atomic.AddUint64(&meteredPeerCount, ^uint64(0)) atomic.AddUint64(&meteredPeerCount, ^uint64(0))
c.ingressMeter.Stop() err, now := c.Conn.Close(), time.Now()
c.egressMeter.Stop()
c.lock.RLock() c.lock.RLock()
key := c.key ip, id := c.ip, c.id
metricsFeed.disconnect.Send(PeerDisconnectEvent{
Key: key,
Ingress: c.ingressMeter.Count(),
Egress: c.egressMeter.Count(),
Disconnected: time.Now(),
})
c.lock.RUnlock() c.lock.RUnlock()
// If the peer disconnects before the handshake
if id == "" {
metricsFeed.failed.Send(PeerFailedEvent{
IP: ip,
Connected: c.connected,
Disconnected: now,
})
return err
}
c.lock.RLock()
//ingress, egress := c.ingressMeter.Count(), c.egressMeter.Count()
c.lock.RUnlock()
// Unregister the peer from the metrics registry
key := fmt.Sprintf("%s/%s", ip, id)
PeerIngressRegistry.Unregister(key) PeerIngressRegistry.Unregister(key)
PeerEgressRegistry.Unregister(key) PeerEgressRegistry.Unregister(key)
return c.Conn.Close()
//metricsFeed.ingress.Send(PeerTrafficEvent{
// IP: ip,
// ID: id,
// Amount: ingress,
//})
//metricsFeed.egress.Send(PeerTrafficEvent{
// IP: ip,
// ID: id,
// Amount: egress,
//})
metricsFeed.disconnect.Send(PeerDisconnectEvent{
IP: ip,
ID: id,
Disconnected: now,
})
return err
} }
// handshakeDone changes the default id to the peer's node id. // handshakeDone changes the default id to the peer's node id.
func (c *meteredConn) handshakeDone(id discover.NodeID) { func (c *meteredConn) handshakeDone(id discover.NodeID) {
c.ingressMeter.Stop()
c.egressMeter.Stop()
c.lock.Lock() c.lock.Lock()
c.id = id.String()
autoKey := c.key key := fmt.Sprintf("%s/%s", c.ip, c.id)
key := fmt.Sprintf("%s/%s", c.ip, id.String()) c.ingressMeter = metrics.NewRegisteredMeter(key, PeerIngressRegistry)
ingressMeter := metrics.NewRegisteredMeter(key, PeerIngressRegistry) c.egressMeter = metrics.NewRegisteredMeter(key, PeerEgressRegistry)
egressMeter := metrics.NewRegisteredMeter(key, PeerEgressRegistry)
ingressMeter.Mark(c.ingressMeter.Count())
egressMeter.Mark(c.egressMeter.Count())
PeerIngressRegistry.Unregister(c.key)
PeerEgressRegistry.Unregister(c.key)
c.key = key
c.ingressMeter = ingressMeter
c.egressMeter = egressMeter
c.lock.Unlock() c.lock.Unlock()
metricsFeed.handshake.Send(PeerHandshakeEvent{ metricsFeed.connect.Send(PeerConnectEvent{
AutoKey: autoKey, IP: c.ip,
Key: key, ID: id.String(),
//Ingress: nil, Connected: c.connected,
//Egress: nil,
Handshake: time.Now(), Handshake: time.Now(),
}) })
} }

View file

@ -542,7 +542,7 @@ func (srv *Server) Start() (err error) {
srv.loopWG.Add(1) srv.loopWG.Add(1)
go srv.run(dialer) go srv.run(dialer)
go startTrafficNotifier(2 * time.Second) go runMetricsFeedHelper(5 * time.Second)
srv.running = true srv.running = true
return nil return nil
} }