Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit dba7e7a

Browse files
committed
statsd: Allow buffering for the clients
The new `Buffer` wrapper allow wrapping any UDP clients in a "buffering mode" that batches incoming metrics into a buffer and eventually flushes them to the statsd-compatible server. The receiving server *must* support the multi-metric StatsD protocol (namely, parsing several metrics in the same packet, separated by newlines). Both the original StatsD and now Brubeck support this. By sending several metrics in a single packet, we reduce the amount of network operations (both in the client and the server) and the amount of context switches to the kernel UDP stack, significantly increasing the throughput of the server implementation. The default maximum packet size has been set at 512 bytes, which is a "safe size" to reliably transmit an atomic packet through the internet: The MTU on the internet is 576, and the size of the IPv4 header is 20 bytes, and the UDP header 8 bytes. This leaves 548 bytes available for user data. We cap it at 512 to add some headspace (most other UDP-based protocols, such as DNS, do the same thing).
1 parent 80ac4de commit dba7e7a

File tree

1 file changed

+62
-18
lines changed

1 file changed

+62
-18
lines changed

lib/statsd.rb

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@
1414
# statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
1515
# statsd.increment 'activate'
1616
class Statsd
17+
class UDPClient
18+
attr_reader :sock
19+
20+
def initialize(address, port = nil)
21+
address, port = address.split(':') if address.include?(':')
22+
addrinfo = Addrinfo.ip(address)
23+
24+
@sock = UDPSocket.new(addrinfo.pfamily)
25+
@sock.connect(addrinfo.ip_address, port)
26+
end
27+
28+
def send(msg)
29+
sock.write(msg)
30+
rescue SystemCallError
31+
nil
32+
end
33+
end
34+
1735
class SecureUDPClient < UDPClient
1836
def initialize(address, port, key)
1937
super(address, port)
@@ -51,24 +69,6 @@ def nonce
5169
end
5270
end
5371

54-
class UDPClient
55-
attr_reader :sock
56-
57-
def initialize(address, port = nil)
58-
address, port = address.split(':') if address.include?(':')
59-
addrinfo = Addrinfo.ip(address)
60-
61-
@sock = UDPSocket.new(addrinfo.pfamily)
62-
@sock.connect(addrinfo.ip_address, port)
63-
end
64-
65-
def send(msg)
66-
sock.write(msg)
67-
rescue => boom
68-
nil
69-
end
70-
end
71-
7272
# A namespace to prepend to all statsd calls.
7373
attr_reader :namespace
7474

@@ -103,6 +103,25 @@ def add_shard(*args)
103103
self
104104
end
105105

106+
def enable_buffering(buffer_size = nil)
107+
return if @buffering
108+
@shards.map! { |client| Buffer.new(client, buffer_size) }
109+
@buffering = true
110+
end
111+
112+
def disable_buffering
113+
return unless @buffering
114+
flush_all
115+
@shards.map! { |client| client.base_client }
116+
@buffering = false
117+
end
118+
119+
def flush_all
120+
return unless @buffering
121+
@shards.each { |client| client.flush }
122+
end
123+
124+
106125
# Sends an increment (count = 1) for the given stat to the statsd server.
107126
#
108127
# @param stat (see #count)
@@ -200,4 +219,29 @@ def select_shard(stat)
200219
@shards[Zlib.crc32(stat) % @shards.size]
201220
end
202221
end
222+
223+
class Buffer
224+
DEFAULT_BUFFER_CAP = 512
225+
226+
attr_reader :base_client
227+
228+
def initialize(client, buffer_cap = nil)
229+
@base_client = client
230+
@buffer = String.new
231+
@buffer_cap = buffer_cap || DEFAULT_BUFFER_CAP
232+
end
233+
234+
def flush
235+
return unless @buffer.bytesize > 0
236+
@base_client.send(@buffer)
237+
@buffer.clear
238+
end
239+
240+
def send(msg)
241+
flush if @buffer.bytesize + msg.bytesize >= @buffer_cap
242+
@buffer << msg
243+
@buffer << "\n".freeze
244+
nil
245+
end
246+
end
203247
end

0 commit comments

Comments
 (0)