From 6994a3daaa0acfc8431e33da535c85e23ab319e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Sun, 21 Jun 2015 20:23:37 +0300 Subject: p2p: instrument P2P networking layer --- p2p/dial.go | 4 +++- p2p/metrics.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ p2p/server.go | 6 ++++-- 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 p2p/metrics.go (limited to 'p2p') diff --git a/p2p/dial.go b/p2p/dial.go index b82d6d1f5..45cd8116b 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -196,7 +196,9 @@ func (t *dialTask) Do(srv *Server) { glog.V(logger.Detail).Infof("dial error: %v", err) return } - srv.setupConn(fd, t.flags, t.dest) + mfd := newMeteredConn(fd, false) + + srv.setupConn(mfd, t.flags, t.dest) } func (t *dialTask) String() string { return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP) diff --git a/p2p/metrics.go b/p2p/metrics.go new file mode 100644 index 000000000..fbe5b1e90 --- /dev/null +++ b/p2p/metrics.go @@ -0,0 +1,49 @@ +// Contains the meters and timers used by the networking layer. + +package p2p + +import ( + "net" + + "github.com/rcrowley/go-metrics" +) + +var ( + ingressConnectMeter = metrics.GetOrRegisterMeter("p2p/InboundConnects", metrics.DefaultRegistry) + ingressTrafficMeter = metrics.GetOrRegisterMeter("p2p/InboundTraffic", metrics.DefaultRegistry) + egressConnectMeter = metrics.GetOrRegisterMeter("p2p/OutboundConnects", metrics.DefaultRegistry) + egressTrafficMeter = metrics.GetOrRegisterMeter("p2p/OutboundTraffic", metrics.DefaultRegistry) +) + +// meteredConn is a wrapper around a network TCP connection that meters both the +// inbound and outbound network traffic. +type meteredConn struct { + *net.TCPConn // Network connection to wrap with metering +} + +// newMeteredConn creates a new metered connection, also bumping the ingress or +// egress connection meter. +func newMeteredConn(conn net.Conn, ingress bool) net.Conn { + if ingress { + ingressConnectMeter.Mark(1) + } else { + egressConnectMeter.Mark(1) + } + return &meteredConn{conn.(*net.TCPConn)} +} + +// Read delegates a network read to the underlying connection, bumping the ingress +// traffic meter along the way. +func (c *meteredConn) Read(b []byte) (n int, err error) { + n, err = c.TCPConn.Read(b) + ingressTrafficMeter.Mark(int64(n)) + return +} + +// Write delegates a network write to the underlying connection, bumping the +// egress traffic meter along the way. +func (c *meteredConn) Write(b []byte) (n int, err error) { + n, err = c.TCPConn.Write(b) + egressTrafficMeter.Mark(int64(n)) + return +} diff --git a/p2p/server.go b/p2p/server.go index 5eff70345..9078841a8 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -548,9 +548,11 @@ func (srv *Server) listenLoop() { if err != nil { return } - glog.V(logger.Debug).Infof("Accepted conn %v\n", fd.RemoteAddr()) + mfd := newMeteredConn(fd, true) + + glog.V(logger.Debug).Infof("Accepted conn %v\n", mfd.RemoteAddr()) go func() { - srv.setupConn(fd, inboundConn, nil) + srv.setupConn(mfd, inboundConn, nil) slots <- struct{}{} }() } -- cgit v1.2.3 From d84638bd31878f772c6d1de3b491160319ddfc4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 26 Jun 2015 15:48:50 +0300 Subject: p2p: support protocol version negotiation --- p2p/peer.go | 11 +++++-- p2p/peer_test.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ p2p/protocol.go | 10 +++--- 3 files changed, 110 insertions(+), 6 deletions(-) (limited to 'p2p') diff --git a/p2p/peer.go b/p2p/peer.go index 40466cf84..e1bda1d03 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -249,15 +249,22 @@ func countMatchingProtocols(protocols []Protocol, caps []Cap) int { // matchProtocols creates structures for matching named subprotocols. func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { - sort.Sort(capsByName(caps)) + sort.Sort(capsByNameAndVersion(caps)) offset := baseProtocolLength result := make(map[string]*protoRW) + outer: for _, cap := range caps { for _, proto := range protocols { - if proto.Name == cap.Name && proto.Version == cap.Version && result[cap.Name] == nil { + if proto.Name == cap.Name && proto.Version == cap.Version { + // If an old protocol version matched, revert it + if old := result[cap.Name]; old != nil { + offset -= old.Length + } + // Assign the new match result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} offset += proto.Length + continue outer } } diff --git a/p2p/peer_test.go b/p2p/peer_test.go index 575d0ff79..95167f4a1 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -196,3 +196,98 @@ func TestNewPeer(t *testing.T) { p.Disconnect(DiscAlreadyConnected) // Should not hang } + +func TestMatchProtocols(t *testing.T) { + tests := []struct { + Local []Cap + Remote []Protocol + Match map[string]protoRW + }{ + { + // No remote protocols + Local: []Cap{{Name: "a"}}, + }, + { + // No local capabilities + Remote: []Protocol{{Name: "a"}}, + }, + { + // No mutual protocols + Local: []Cap{{Name: "a"}}, + Remote: []Protocol{{Name: "b"}}, + }, + { + // Some matches, some differences + Local: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, + Remote: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, + Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, + }, + { + // Various alphabetical ordering + Local: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, + Remote: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, + Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, + }, + { + // No mutual versions + Local: []Cap{{Version: 1}}, + Remote: []Protocol{{Version: 2}}, + }, + { + // Multiple versions, single common + Local: []Cap{{Version: 1}, {Version: 2}}, + Remote: []Protocol{{Version: 2}, {Version: 3}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, + }, + { + // Multiple versions, multiple common + Local: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, + Remote: []Protocol{{Version: 2}, {Version: 3}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, + }, + { + // Various version orderings + Local: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, + Remote: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, + }, + { + // Versions overriding sub-protocol lengths + Local: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, + Remote: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, + Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, + }, + } + + for i, tt := range tests { + result := matchProtocols(tt.Remote, tt.Local, nil) + if len(result) != len(tt.Match) { + t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) + continue + } + // Make sure all negotiated protocols are needed and correct + for name, proto := range result { + match, ok := tt.Match[name] + if !ok { + t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name) + continue + } + if proto.Name != match.Name { + t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name) + } + if proto.Version != match.Version { + t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version) + } + if proto.offset-baseProtocolLength != match.offset { + t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset) + } + } + // Make sure no protocols missed negotiation + for name, _ := range tt.Match { + if _, ok := result[name]; !ok { + t.Errorf("test %d, proto '%s': not negotiated, should have", i, name) + continue + } + } + } +} diff --git a/p2p/protocol.go b/p2p/protocol.go index 5fa395eda..a229ba911 100644 --- a/p2p/protocol.go +++ b/p2p/protocol.go @@ -43,8 +43,10 @@ func (cap Cap) String() string { return fmt.Sprintf("%s/%d", cap.Name, cap.Version) } -type capsByName []Cap +type capsByNameAndVersion []Cap -func (cs capsByName) Len() int { return len(cs) } -func (cs capsByName) Less(i, j int) bool { return cs[i].Name < cs[j].Name } -func (cs capsByName) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] } +func (cs capsByNameAndVersion) Len() int { return len(cs) } +func (cs capsByNameAndVersion) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] } +func (cs capsByNameAndVersion) Less(i, j int) bool { + return cs[i].Name < cs[j].Name || (cs[i].Name == cs[j].Name && cs[i].Version < cs[j].Version) +} -- cgit v1.2.3 From 216fc267fafa6e27b14f642d0440cbd817a3a2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Fri, 26 Jun 2015 20:45:13 +0300 Subject: p2p: fix local/remote cap/protocol mixup --- p2p/peer_test.go | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'p2p') diff --git a/p2p/peer_test.go b/p2p/peer_test.go index 95167f4a1..d849c925f 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -199,68 +199,68 @@ func TestNewPeer(t *testing.T) { func TestMatchProtocols(t *testing.T) { tests := []struct { - Local []Cap - Remote []Protocol + Remote []Cap + Local []Protocol Match map[string]protoRW }{ { - // No remote protocols - Local: []Cap{{Name: "a"}}, + // No remote capabilities + Local: []Protocol{{Name: "a"}}, }, { - // No local capabilities - Remote: []Protocol{{Name: "a"}}, + // No local protocols + Remote: []Cap{{Name: "a"}}, }, { // No mutual protocols - Local: []Cap{{Name: "a"}}, - Remote: []Protocol{{Name: "b"}}, + Remote: []Cap{{Name: "a"}}, + Local: []Protocol{{Name: "b"}}, }, { // Some matches, some differences - Local: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, - Remote: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, + Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, + Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, }, { // Various alphabetical ordering - Local: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, - Remote: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, + Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, + Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, }, { // No mutual versions - Local: []Cap{{Version: 1}}, - Remote: []Protocol{{Version: 2}}, + Remote: []Cap{{Version: 1}}, + Local: []Protocol{{Version: 2}}, }, { // Multiple versions, single common - Local: []Cap{{Version: 1}, {Version: 2}}, - Remote: []Protocol{{Version: 2}, {Version: 3}}, + Remote: []Cap{{Version: 1}, {Version: 2}}, + Local: []Protocol{{Version: 2}, {Version: 3}}, Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, }, { // Multiple versions, multiple common - Local: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, - Remote: []Protocol{{Version: 2}, {Version: 3}}, + Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, + Local: []Protocol{{Version: 2}, {Version: 3}}, Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, }, { // Various version orderings - Local: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, - Remote: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, + Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, + Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, }, { // Versions overriding sub-protocol lengths - Local: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, - Remote: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, + Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, + Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, }, } for i, tt := range tests { - result := matchProtocols(tt.Remote, tt.Local, nil) + result := matchProtocols(tt.Local, tt.Remote, nil) if len(result) != len(tt.Match) { t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) continue -- cgit v1.2.3 From 01fe97211354d13ecaba8a52c82b808b7a7e8393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 29 Jun 2015 16:11:01 +0300 Subject: cmd, core, eth, metrics, p2p: require enabling metrics --- p2p/metrics.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'p2p') diff --git a/p2p/metrics.go b/p2p/metrics.go index fbe5b1e90..4b519e438 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -5,14 +5,14 @@ package p2p import ( "net" - "github.com/rcrowley/go-metrics" + "github.com/ethereum/go-ethereum/metrics" ) var ( - ingressConnectMeter = metrics.GetOrRegisterMeter("p2p/InboundConnects", metrics.DefaultRegistry) - ingressTrafficMeter = metrics.GetOrRegisterMeter("p2p/InboundTraffic", metrics.DefaultRegistry) - egressConnectMeter = metrics.GetOrRegisterMeter("p2p/OutboundConnects", metrics.DefaultRegistry) - egressTrafficMeter = metrics.GetOrRegisterMeter("p2p/OutboundTraffic", metrics.DefaultRegistry) + ingressConnectMeter = metrics.NewMeter("p2p/InboundConnects") + ingressTrafficMeter = metrics.NewMeter("p2p/InboundTraffic") + egressConnectMeter = metrics.NewMeter("p2p/OutboundConnects") + egressTrafficMeter = metrics.NewMeter("p2p/OutboundTraffic") ) // meteredConn is a wrapper around a network TCP connection that meters both the -- cgit v1.2.3