github.com/goldbock/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 }