github.com/gojoychain/go-geth@v1.8.22/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.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 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(id enode.ID) { 164 if atomic.AddInt32(&meteredPeerCount, 1) >= MeteredPeerLimit { 165 // Don't register the peer in the traffic registries. 166 atomic.AddInt32(&meteredPeerCount, -1) 167 c.lock.Lock() 168 c.id, c.trafficMetered = id, false 169 c.lock.Unlock() 170 log.Warn("Metered peer count reached the limit") 171 } else { 172 key := fmt.Sprintf("%s/%s", c.ip, id.String()) 173 c.lock.Lock() 174 c.id, c.trafficMetered = id, true 175 c.ingressMeter = metrics.NewRegisteredMeter(key, PeerIngressRegistry) 176 c.egressMeter = metrics.NewRegisteredMeter(key, PeerEgressRegistry) 177 c.lock.Unlock() 178 } 179 meteredPeerFeed.Send(MeteredPeerEvent{ 180 Type: PeerConnected, 181 IP: c.ip, 182 ID: id, 183 Elapsed: time.Since(c.connected), 184 }) 185 } 186 187 // Close delegates a close operation to the underlying connection, unregisters 188 // the peer from the traffic registries and emits close event. 189 func (c *meteredConn) Close() error { 190 err := c.Conn.Close() 191 c.lock.RLock() 192 if c.id == (enode.ID{}) { 193 // If the peer disconnects before the handshake. 194 c.lock.RUnlock() 195 meteredPeerFeed.Send(MeteredPeerEvent{ 196 Type: PeerHandshakeFailed, 197 IP: c.ip, 198 Elapsed: time.Since(c.connected), 199 }) 200 return err 201 } 202 id := c.id 203 if !c.trafficMetered { 204 // If the peer isn't registered in the traffic registries. 205 c.lock.RUnlock() 206 meteredPeerFeed.Send(MeteredPeerEvent{ 207 Type: PeerDisconnected, 208 IP: c.ip, 209 ID: id, 210 }) 211 return err 212 } 213 ingress, egress := uint64(c.ingressMeter.Count()), uint64(c.egressMeter.Count()) 214 c.lock.RUnlock() 215 216 // Decrement the metered peer count 217 atomic.AddInt32(&meteredPeerCount, -1) 218 219 // Unregister the peer from the traffic registries 220 key := fmt.Sprintf("%s/%s", c.ip, id) 221 PeerIngressRegistry.Unregister(key) 222 PeerEgressRegistry.Unregister(key) 223 224 meteredPeerFeed.Send(MeteredPeerEvent{ 225 Type: PeerDisconnected, 226 IP: c.ip, 227 ID: id, 228 Ingress: ingress, 229 Egress: egress, 230 }) 231 return err 232 }