github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/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/p2p/enode" 29 30 "github.com/ethereum/go-ethereum/event" 31 "github.com/ethereum/go-ethereum/log" 32 "github.com/ethereum/go-ethereum/metrics" 33 ) 34 35 const ( 36 MetricsInboundConnects = "p2p/InboundConnects" // Name for the registered inbound connects meter 37 MetricsInboundTraffic = "p2p/InboundTraffic" // Name for the registered inbound traffic meter 38 MetricsOutboundConnects = "p2p/OutboundConnects" // Name for the registered outbound connects meter 39 MetricsOutboundTraffic = "p2p/OutboundTraffic" // Name for the registered outbound traffic meter 40 41 MeteredPeerLimit = 1024 // This amount of peers are individually metered 42 ) 43 44 var ( 45 ingressConnectMeter = metrics.NewRegisteredMeter(MetricsInboundConnects, nil) // Meter counting the ingress connections 46 ingressTrafficMeter = metrics.NewRegisteredMeter(MetricsInboundTraffic, nil) // Meter metering the cumulative ingress traffic 47 egressConnectMeter = metrics.NewRegisteredMeter(MetricsOutboundConnects, nil) // Meter counting the egress connections 48 egressTrafficMeter = metrics.NewRegisteredMeter(MetricsOutboundTraffic, nil) // Meter metering the cumulative egress traffic 49 50 PeerIngressRegistry = metrics.NewPrefixedChildRegistry(metrics.DefaultRegistry, MetricsInboundTraffic+"/") // Registry containing the peer ingress 51 PeerEgressRegistry = metrics.NewPrefixedChildRegistry(metrics.DefaultRegistry, 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 string // 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 string // 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 return &meteredConn{ 128 Conn: conn, 129 ip: ip, 130 connected: time.Now(), 131 } 132 } 133 134 // Read delegates a network read to the underlying connection, bumping the common 135 // and the peer ingress traffic meters along the way. 136 func (c *meteredConn) Read(b []byte) (n int, err error) { 137 n, err = c.Conn.Read(b) 138 ingressTrafficMeter.Mark(int64(n)) 139 c.lock.RLock() 140 if c.trafficMetered { 141 c.ingressMeter.Mark(int64(n)) 142 } 143 c.lock.RUnlock() 144 return n, err 145 } 146 147 // Write delegates a network write to the underlying connection, bumping the common 148 // and the peer egress traffic meters along the way. 149 func (c *meteredConn) Write(b []byte) (n int, err error) { 150 n, err = c.Conn.Write(b) 151 egressTrafficMeter.Mark(int64(n)) 152 c.lock.RLock() 153 if c.trafficMetered { 154 c.egressMeter.Mark(int64(n)) 155 } 156 c.lock.RUnlock() 157 return n, err 158 } 159 160 // handshakeDone is called when a peer handshake is done. Registers the peer to 161 // the ingress and the egress traffic registries using the peer's IP and node ID, 162 // also emits connect event. 163 func (c *meteredConn) handshakeDone(nodeID enode.ID) { 164 id := nodeID.String() 165 if atomic.AddInt32(&meteredPeerCount, 1) >= MeteredPeerLimit { 166 // Don't register the peer in the traffic registries. 167 atomic.AddInt32(&meteredPeerCount, -1) 168 c.lock.Lock() 169 c.id, c.trafficMetered = id, false 170 c.lock.Unlock() 171 log.Warn("Metered peer count reached the limit") 172 } else { 173 key := fmt.Sprintf("%s/%s", c.ip, id) 174 c.lock.Lock() 175 c.id, c.trafficMetered = id, true 176 c.ingressMeter = metrics.NewRegisteredMeter(key, PeerIngressRegistry) 177 c.egressMeter = metrics.NewRegisteredMeter(key, PeerEgressRegistry) 178 c.lock.Unlock() 179 } 180 meteredPeerFeed.Send(MeteredPeerEvent{ 181 Type: PeerConnected, 182 IP: c.ip, 183 ID: id, 184 Elapsed: time.Since(c.connected), 185 }) 186 } 187 188 // Close delegates a close operation to the underlying connection, unregisters 189 // the peer from the traffic registries and emits close event. 190 func (c *meteredConn) Close() error { 191 err := c.Conn.Close() 192 c.lock.RLock() 193 if c.id == "" { 194 // If the peer disconnects before the handshake. 195 c.lock.RUnlock() 196 meteredPeerFeed.Send(MeteredPeerEvent{ 197 Type: PeerHandshakeFailed, 198 IP: c.ip, 199 Elapsed: time.Since(c.connected), 200 }) 201 return err 202 } 203 id := c.id 204 if !c.trafficMetered { 205 // If the peer isn't registered in the traffic registries. 206 c.lock.RUnlock() 207 meteredPeerFeed.Send(MeteredPeerEvent{ 208 Type: PeerDisconnected, 209 IP: c.ip, 210 ID: id, 211 }) 212 return err 213 } 214 ingress, egress := uint64(c.ingressMeter.Count()), uint64(c.egressMeter.Count()) 215 c.lock.RUnlock() 216 217 // Decrement the metered peer count 218 atomic.AddInt32(&meteredPeerCount, -1) 219 220 // Unregister the peer from the traffic registries 221 key := fmt.Sprintf("%s/%s", c.ip, id) 222 PeerIngressRegistry.Unregister(key) 223 PeerEgressRegistry.Unregister(key) 224 225 meteredPeerFeed.Send(MeteredPeerEvent{ 226 Type: PeerDisconnected, 227 IP: c.ip, 228 ID: id, 229 Ingress: ingress, 230 Egress: egress, 231 }) 232 return err 233 }