github.com/julesgoullee/go-ethereum@v1.9.7/p2p/metrics.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Contains the meters and timers used by the networking layer.
    18  
    19  package p2p
    20  
    21  import (
    22  	"fmt"
    23  	"net"
    24  	"sync"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/event"
    29  	"github.com/ethereum/go-ethereum/log"
    30  	"github.com/ethereum/go-ethereum/metrics"
    31  	"github.com/ethereum/go-ethereum/p2p/enode"
    32  )
    33  
    34  const (
    35  	MetricsInboundTraffic   = "p2p/ingress" // Name for the registered inbound traffic meter
    36  	MetricsOutboundTraffic  = "p2p/egress"  // Name for the registered outbound traffic meter
    37  	MetricsOutboundConnects = "p2p/dials"   // Name for the registered outbound connects meter
    38  	MetricsInboundConnects  = "p2p/serves"  // Name for the registered inbound connects meter
    39  
    40  	MeteredPeerLimit = 1024 // This amount of peers are individually metered
    41  )
    42  
    43  var (
    44  	ingressConnectMeter = metrics.NewRegisteredMeter(MetricsInboundConnects, nil)  // Meter counting the ingress connections
    45  	ingressTrafficMeter = metrics.NewRegisteredMeter(MetricsInboundTraffic, nil)   // Meter metering the cumulative ingress traffic
    46  	egressConnectMeter  = metrics.NewRegisteredMeter(MetricsOutboundConnects, nil) // Meter counting the egress connections
    47  	egressTrafficMeter  = metrics.NewRegisteredMeter(MetricsOutboundTraffic, nil)  // Meter metering the cumulative egress traffic
    48  	activePeerGauge     = metrics.NewRegisteredGauge("p2p/peers", nil)             // Gauge tracking the current peer count
    49  
    50  	PeerIngressRegistry = metrics.NewPrefixedChildRegistry(metrics.EphemeralRegistry, MetricsInboundTraffic+"/")  // Registry containing the peer ingress
    51  	PeerEgressRegistry  = metrics.NewPrefixedChildRegistry(metrics.EphemeralRegistry, MetricsOutboundTraffic+"/") // Registry containing the peer egress
    52  
    53  	meteredPeerFeed  event.Feed // Event feed for peer metrics
    54  	meteredPeerCount int32      // Actually stored peer connection count
    55  )
    56  
    57  // MeteredPeerEventType is the type of peer events emitted by a metered connection.
    58  type MeteredPeerEventType int
    59  
    60  const (
    61  	// PeerConnected is the type of event emitted when a peer successfully
    62  	// made the handshake.
    63  	PeerConnected MeteredPeerEventType = iota
    64  
    65  	// PeerDisconnected is the type of event emitted when a peer disconnects.
    66  	PeerDisconnected
    67  
    68  	// PeerHandshakeFailed is the type of event emitted when a peer fails to
    69  	// make the handshake or disconnects before the handshake.
    70  	PeerHandshakeFailed
    71  )
    72  
    73  // MeteredPeerEvent is an event emitted when peers connect or disconnect.
    74  type MeteredPeerEvent struct {
    75  	Type    MeteredPeerEventType // Type of peer event
    76  	IP      net.IP               // IP address of the peer
    77  	ID      enode.ID             // NodeID of the peer
    78  	Elapsed time.Duration        // Time elapsed between the connection and the handshake/disconnection
    79  	Ingress uint64               // Ingress count at the moment of the event
    80  	Egress  uint64               // Egress count at the moment of the event
    81  }
    82  
    83  // SubscribeMeteredPeerEvent registers a subscription for peer life-cycle events
    84  // if metrics collection is enabled.
    85  func SubscribeMeteredPeerEvent(ch chan<- MeteredPeerEvent) event.Subscription {
    86  	return meteredPeerFeed.Subscribe(ch)
    87  }
    88  
    89  // meteredConn is a wrapper around a net.Conn that meters both the
    90  // inbound and outbound network traffic.
    91  type meteredConn struct {
    92  	net.Conn // Network connection to wrap with metering
    93  
    94  	connected time.Time // Connection time of the peer
    95  	ip        net.IP    // IP address of the peer
    96  	id        enode.ID  // NodeID of the peer
    97  
    98  	// trafficMetered denotes if the peer is registered in the traffic registries.
    99  	// Its value is true if the metered peer count doesn't reach the limit in the
   100  	// moment of the peer's connection.
   101  	trafficMetered bool
   102  	ingressMeter   metrics.Meter // Meter for the read bytes of the peer
   103  	egressMeter    metrics.Meter // Meter for the written bytes of the peer
   104  
   105  	lock sync.RWMutex // Lock protecting the metered connection's internals
   106  }
   107  
   108  // newMeteredConn creates a new metered connection, bumps the ingress or egress
   109  // connection meter and also increases the metered peer count. If the metrics
   110  // system is disabled or the IP address is unspecified, this function returns
   111  // the original object.
   112  func newMeteredConn(conn net.Conn, ingress bool, ip net.IP) net.Conn {
   113  	// Short circuit if metrics are disabled
   114  	if !metrics.Enabled {
   115  		return conn
   116  	}
   117  	if ip.IsUnspecified() {
   118  		log.Warn("Peer IP is unspecified")
   119  		return conn
   120  	}
   121  	// Bump the connection counters and wrap the connection
   122  	if ingress {
   123  		ingressConnectMeter.Mark(1)
   124  	} else {
   125  		egressConnectMeter.Mark(1)
   126  	}
   127  	activePeerGauge.Inc(1)
   128  
   129  	return &meteredConn{
   130  		Conn:      conn,
   131  		ip:        ip,
   132  		connected: time.Now(),
   133  	}
   134  }
   135  
   136  // Read delegates a network read to the underlying connection, bumping the common
   137  // and the peer ingress traffic meters along the way.
   138  func (c *meteredConn) Read(b []byte) (n int, err error) {
   139  	n, err = c.Conn.Read(b)
   140  	ingressTrafficMeter.Mark(int64(n))
   141  	c.lock.RLock()
   142  	if c.trafficMetered {
   143  		c.ingressMeter.Mark(int64(n))
   144  	}
   145  	c.lock.RUnlock()
   146  	return n, err
   147  }
   148  
   149  // Write delegates a network write to the underlying connection, bumping the common
   150  // and the peer egress traffic meters along the way.
   151  func (c *meteredConn) Write(b []byte) (n int, err error) {
   152  	n, err = c.Conn.Write(b)
   153  	egressTrafficMeter.Mark(int64(n))
   154  	c.lock.RLock()
   155  	if c.trafficMetered {
   156  		c.egressMeter.Mark(int64(n))
   157  	}
   158  	c.lock.RUnlock()
   159  	return n, err
   160  }
   161  
   162  // handshakeDone is called when a peer handshake is done. Registers the peer to
   163  // the ingress and the egress traffic registries using the peer's IP and node ID,
   164  // also emits connect event.
   165  func (c *meteredConn) handshakeDone(id enode.ID) {
   166  	// TODO (kurkomisi): use the node URL instead of the pure node ID. (the String() method of *Node)
   167  	if atomic.AddInt32(&meteredPeerCount, 1) >= MeteredPeerLimit {
   168  		// Don't register the peer in the traffic registries.
   169  		atomic.AddInt32(&meteredPeerCount, -1)
   170  		c.lock.Lock()
   171  		c.id, c.trafficMetered = id, false
   172  		c.lock.Unlock()
   173  		log.Warn("Metered peer count reached the limit")
   174  	} else {
   175  		key := fmt.Sprintf("%s/%s", c.ip, id.String())
   176  		c.lock.Lock()
   177  		c.id, c.trafficMetered = id, true
   178  		c.ingressMeter = metrics.NewRegisteredMeter(key, PeerIngressRegistry)
   179  		c.egressMeter = metrics.NewRegisteredMeter(key, PeerEgressRegistry)
   180  		c.lock.Unlock()
   181  	}
   182  	meteredPeerFeed.Send(MeteredPeerEvent{
   183  		Type:    PeerConnected,
   184  		IP:      c.ip,
   185  		ID:      id,
   186  		Elapsed: time.Since(c.connected),
   187  	})
   188  }
   189  
   190  // Close delegates a close operation to the underlying connection, unregisters
   191  // the peer from the traffic registries and emits close event.
   192  func (c *meteredConn) Close() error {
   193  	err := c.Conn.Close()
   194  	c.lock.RLock()
   195  	if c.id == (enode.ID{}) {
   196  		// If the peer disconnects before the handshake.
   197  		c.lock.RUnlock()
   198  		meteredPeerFeed.Send(MeteredPeerEvent{
   199  			Type:    PeerHandshakeFailed,
   200  			IP:      c.ip,
   201  			Elapsed: time.Since(c.connected),
   202  		})
   203  		activePeerGauge.Dec(1)
   204  		return err
   205  	}
   206  	id := c.id
   207  	if !c.trafficMetered {
   208  		// If the peer isn't registered in the traffic registries.
   209  		c.lock.RUnlock()
   210  		meteredPeerFeed.Send(MeteredPeerEvent{
   211  			Type: PeerDisconnected,
   212  			IP:   c.ip,
   213  			ID:   id,
   214  		})
   215  		activePeerGauge.Dec(1)
   216  		return err
   217  	}
   218  	ingress, egress := uint64(c.ingressMeter.Count()), uint64(c.egressMeter.Count())
   219  	c.lock.RUnlock()
   220  
   221  	// Decrement the metered peer count
   222  	atomic.AddInt32(&meteredPeerCount, -1)
   223  
   224  	// Unregister the peer from the traffic registries
   225  	key := fmt.Sprintf("%s/%s", c.ip, id)
   226  	PeerIngressRegistry.Unregister(key)
   227  	PeerEgressRegistry.Unregister(key)
   228  
   229  	meteredPeerFeed.Send(MeteredPeerEvent{
   230  		Type:    PeerDisconnected,
   231  		IP:      c.ip,
   232  		ID:      id,
   233  		Ingress: ingress,
   234  		Egress:  egress,
   235  	})
   236  	activePeerGauge.Dec(1)
   237  	return err
   238  }