pipeline_toolkit 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.markdown +20 -0
- data/README.markdown +98 -0
- data/bin/msg_generator +13 -0
- data/bin/msg_probe +13 -0
- data/bin/msg_push +13 -0
- data/bin/msg_sink +14 -0
- data/bin/msg_subscribe +13 -0
- data/lib/pipeline_toolkit.rb +17 -0
- data/lib/pipeline_toolkit/amqp/abstract.rb +95 -0
- data/lib/pipeline_toolkit/amqp/reader.rb +64 -0
- data/lib/pipeline_toolkit/amqp/writer.rb +54 -0
- data/lib/pipeline_toolkit/commands/msg_generator/cli.rb +45 -0
- data/lib/pipeline_toolkit/commands/msg_probe/cli.rb +46 -0
- data/lib/pipeline_toolkit/commands/msg_push/cli.rb +58 -0
- data/lib/pipeline_toolkit/commands/msg_sink/cli.rb +41 -0
- data/lib/pipeline_toolkit/commands/msg_subscribe/cli.rb +58 -0
- data/lib/pipeline_toolkit/default_logger.rb +126 -11
- data/lib/pipeline_toolkit/handlers/message_handler.rb +38 -0
- data/lib/pipeline_toolkit/message_coder.rb +18 -8
- data/lib/pipeline_toolkit/message_command.rb +138 -61
- data/lib/pipeline_toolkit/message_generator.rb +21 -0
- data/lib/pipeline_toolkit/message_probe.rb +6 -6
- data/lib/pipeline_toolkit/message_pusher.rb +51 -54
- data/lib/pipeline_toolkit/message_sink.rb +1 -1
- data/lib/pipeline_toolkit/message_subscriber.rb +182 -201
- data/lib/pipeline_toolkit/monitoring/monitor_server.rb +124 -0
- data/spec/eventmachine_helper.rb +44 -0
- data/spec/message_subscriber_spec.rb +64 -0
- data/spec/spec_helper.rb +15 -0
- metadata +202 -47
- data/.gitignore +0 -5
- data/README.rdoc +0 -70
- data/Rakefile +0 -40
- data/VERSION +0 -1
- data/bin/msg_generator.rb +0 -0
- data/bin/msg_probe.rb +0 -15
- data/bin/msg_push.rb +0 -25
- data/bin/msg_sink.rb +0 -11
- data/bin/msg_subscribe.rb +0 -27
- data/monitor/munin.rb +0 -91
- data/pipeline_toolkit.gemspec +0 -72
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class MessageGenerator
|
4
|
+
|
5
|
+
def initialize(opts)
|
6
|
+
@delay = opts.delay
|
7
|
+
@msg = JSON.parse(opts.msg)
|
8
|
+
end
|
9
|
+
|
10
|
+
def start
|
11
|
+
loop do
|
12
|
+
self.send_msg(@msg)
|
13
|
+
sleep(@delay) unless @delay.nil?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_msg(msg)
|
18
|
+
# puts MessageCoder.encode(@msg)
|
19
|
+
$stdout.flush
|
20
|
+
end
|
21
|
+
end
|
@@ -13,16 +13,16 @@ class MessageProbe
|
|
13
13
|
self.init_dnssd if opts.dnssd
|
14
14
|
end
|
15
15
|
|
16
|
-
def init_loop
|
17
|
-
EM.start_server('0.0.0.0', @http_port, ProbeHttpRequest, self)
|
18
|
-
EM.add_periodic_timer(@interval) { self.tick }
|
19
|
-
end
|
20
|
-
|
21
16
|
def init_dnssd
|
22
17
|
require 'dnssd'
|
23
18
|
DNSSD.register!("#{@name} probe", "_http._tcp", nil, @http_port)
|
24
19
|
end
|
25
20
|
|
21
|
+
def initialize_machine
|
22
|
+
EM.start_server('0.0.0.0', @http_port, ProbeHttpRequest, self)
|
23
|
+
EM.add_periodic_timer(@interval) { self.tick }
|
24
|
+
end
|
25
|
+
|
26
26
|
def tick
|
27
27
|
@time_delta = Time.now - @prev_time
|
28
28
|
@mps = @count / @time_delta
|
@@ -40,7 +40,7 @@ class MessageProbe
|
|
40
40
|
|
41
41
|
# OPTIMIZE. can improve performance by overriding base process_line method
|
42
42
|
# saving us the unnecessary marshal step.
|
43
|
-
def
|
43
|
+
def process_standard(msg)
|
44
44
|
@count += 1
|
45
45
|
msg
|
46
46
|
end
|
@@ -1,61 +1,58 @@
|
|
1
1
|
require "mq"
|
2
2
|
|
3
|
+
##
|
4
|
+
# A Message Queue machine to handle messages that has to be published
|
5
|
+
# back into a message queue.
|
6
|
+
#
|
3
7
|
class MessagePusher
|
4
8
|
include MessageCommand
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@exchanges.each do |exchange|
|
53
|
-
key = route_key(msg)
|
54
|
-
exchange.publish(msg.to_yaml)
|
55
|
-
# OPTIMIZE. Using MessageCoder.encode(msg) instead of to_yaml is 2x faster. But won't be easy to
|
56
|
-
# debug. Worth it?
|
57
|
-
end
|
58
|
-
:ack
|
9
|
+
include Amqp::Writer
|
10
|
+
|
11
|
+
##
|
12
|
+
# Initializes a new intance
|
13
|
+
#
|
14
|
+
# @param options<Hash> Options hash for the message pusher.
|
15
|
+
#
|
16
|
+
def initialize(options = {})
|
17
|
+
super(options)
|
18
|
+
DefaultLogger.debug("MessagePusher#initialize(options = {})") if options[:env] == "development"
|
19
|
+
|
20
|
+
queues_string = options[:queues].map { |name, type| "#{name} (key:#{type})" }.join(",")
|
21
|
+
DefaultLogger.info "================================================================"
|
22
|
+
DefaultLogger.info "Booting #{self.class.name} (#{options[:env]})"
|
23
|
+
DefaultLogger.info "Exchange: #{options[:exchange]} (#{options[:type]} passive:#{options[:passive]} durable:#{options[:durable]})"
|
24
|
+
DefaultLogger.info "Queues: #{queues_string}"
|
25
|
+
DefaultLogger.info "amqp://#{options[:user]}:#{options[:pass]}@#{options[:host]}:#{options[:port]}#{options[:vhost]}"
|
26
|
+
DefaultLogger.info ""
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Initialize the AMQP server connection and exchange so we can write messages to the queue.
|
31
|
+
#
|
32
|
+
def initialize_machine
|
33
|
+
DefaultLogger.debug("MessagePusher#initialize_machine") if options[:env] == "development"
|
34
|
+
initialize_writer
|
35
|
+
initialize_queues
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Notify back down the pipeline information about this machine.
|
40
|
+
#
|
41
|
+
def report_back
|
42
|
+
DefaultLogger.debug("MessagePusher#open_acknowledgement_pipe") if options[:env] == "development"
|
43
|
+
message = { :msg_type => :pipe_desc, :queues => queue_names.join(",") }
|
44
|
+
write_to_pipe(message, @ack_pipe)
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Handle the messages by writing them into the AMQP server exchange.
|
49
|
+
#
|
50
|
+
# @param message<Hash> The message to write to the exchange.
|
51
|
+
#
|
52
|
+
def process_standard(message)
|
53
|
+
DefaultLogger.debug("MessagePusher#process_standard(message)") if options[:env] == "development"
|
54
|
+
publish(message)
|
55
|
+
acknowledge(message) if options[:ack]
|
59
56
|
end
|
60
57
|
|
61
58
|
end
|
@@ -3,246 +3,227 @@ require "mq"
|
|
3
3
|
require "time"
|
4
4
|
require "socket"
|
5
5
|
|
6
|
+
##
|
7
|
+
# The message subscriber is used to subscribe to a AMQP server queue
|
8
|
+
# and handle a single message at a time.
|
9
|
+
#
|
6
10
|
class MessageSubscriber
|
7
|
-
include DefaultLogger
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
include Amqp::Reader
|
13
|
+
|
14
|
+
attr_reader :start_time
|
15
|
+
attr_reader :mps
|
16
|
+
attr_reader :options
|
17
|
+
attr_accessor :queues_out
|
11
18
|
|
12
19
|
PIPE_PATH = "/tmp"
|
13
20
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
21
|
+
##
|
22
|
+
# Initializes a new instance
|
23
|
+
#
|
24
|
+
# @param options<Hash> An options hash, see command line interface.
|
25
|
+
#
|
26
|
+
def initialize(options = {})
|
27
|
+
|
28
|
+
DefaultLogger.init_logger(options)
|
29
|
+
|
30
|
+
@options = options
|
31
|
+
|
32
|
+
@options[:http_port] ||= Socket.select_random_port(10_000, 11_000)
|
33
|
+
@options[:name] ||= (Socket.gethostname + '_' + Process.pid.to_s)
|
34
|
+
|
35
|
+
DefaultLogger.info "================================================================"
|
36
|
+
DefaultLogger.info "Booting #{self.class.name} (#{options[:env]})"
|
37
|
+
DefaultLogger.info "Exchange: #{options[:exchange]} (#{options[:type]} passive:#{options[:passive]} durable:#{options[:durable]})"
|
38
|
+
DefaultLogger.info "Queue: #{options[:queue]} (ack:#{options[:ack]})"
|
39
|
+
DefaultLogger.info "amqp://#{options[:user]}:#{options[:pass]}@#{options[:host]}:#{options[:port]}#{options[:vhost]}"
|
40
|
+
DefaultLogger.info "Monitoring: http://localhost:#{options[:http_port]}/ (#{options[:content_type]} name:#{options[:name]} DNS-SD:#{options[:dnssd]})"
|
41
|
+
DefaultLogger.info ""
|
42
|
+
|
43
|
+
@queues_out = ''
|
44
|
+
|
45
|
+
initialize_dnssd
|
46
|
+
|
47
|
+
reset_message_statistics
|
35
48
|
end
|
36
49
|
|
50
|
+
##
|
51
|
+
# Starts the subscriber reactor loop
|
52
|
+
#
|
37
53
|
def start
|
38
|
-
# FIXME. Need to do more investigation on if/when messages are lost with shutdowns
|
39
|
-
Signal.trap('INT') { AMQP.stop{ EM.stop } }
|
40
|
-
Signal.trap('TERM') { AMQP.stop{ EM.stop } }
|
41
54
|
|
55
|
+
Signal.trap('INT') { EM.stop }
|
56
|
+
Signal.trap('TERM') { EM.stop }
|
57
|
+
|
58
|
+
DefaultLogger.debug("MessageSubscriber#start")
|
42
59
|
begin
|
43
|
-
|
44
|
-
|
60
|
+
create_ack_pipe
|
61
|
+
|
62
|
+
EM.run do
|
45
63
|
@start_time = Time.now
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
conn = EM.watch(@sys_pipe, HandleCtlMessages, @sys_pipe, self)
|
64
|
+
|
65
|
+
initialize_reader
|
66
|
+
queue_subscribe
|
67
|
+
|
68
|
+
conn = EM.watch(@ack_pipe, Handlers::MessageHandler, self, options)
|
69
|
+
# must call this to setup callback to notify_readable
|
54
70
|
conn.notify_readable = true
|
55
|
-
|
56
|
-
EM.start_server('0.0.0.0',
|
57
|
-
EM.add_periodic_timer(5) {
|
71
|
+
|
72
|
+
EM.start_server('0.0.0.0', options[:http_port], Monitoring::MonitorServer, self, options)
|
73
|
+
EM.add_periodic_timer(5) { calculate_message_statistics }
|
58
74
|
end
|
59
75
|
rescue StandardError => e
|
60
|
-
|
76
|
+
DefaultLogger.error "#{e.class.name}: #{e.message}\n" << e.backtrace.join("\n")
|
61
77
|
raise e
|
62
78
|
ensure
|
63
|
-
|
79
|
+
shutdown
|
64
80
|
end
|
65
81
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
self.reset
|
71
|
-
end
|
72
|
-
|
73
|
-
def reset
|
74
|
-
@count = 0
|
75
|
-
@prev_time = Time.now
|
76
|
-
end
|
77
|
-
|
82
|
+
|
83
|
+
##
|
84
|
+
# Stop the subscriber
|
85
|
+
#
|
78
86
|
def shutdown
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
DefaultLogger.info("Shutting down #{self.class.name}")
|
88
|
+
DefaultLogger.info "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
|
89
|
+
destroy_ack_pipe
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Callback for the Handlers::MessageHandler when it receives a message
|
94
|
+
#
|
95
|
+
# @param message<Hash> The decoded message
|
96
|
+
#
|
97
|
+
def process(message)
|
98
|
+
case message[:msg_type]
|
99
|
+
when :ack
|
100
|
+
perform_acknowledgement(message[:ack_id])
|
101
|
+
when :pipe_desc
|
102
|
+
DefaultLogger.debug("AcknowledgementHandler#queues_out")
|
103
|
+
@queues_out = message[:queues]
|
104
|
+
else
|
105
|
+
raise Exception.new("Unknown control message received: #{message}")
|
91
106
|
end
|
92
107
|
end
|
93
|
-
|
94
|
-
def create_exchange(name, type)
|
95
|
-
MQ::Exchange.new(MQ.default, type.to_sym, @exchange_name, :durable => true, :passive => false)
|
96
|
-
end
|
97
|
-
|
98
|
-
def create_sys_pipe
|
99
|
-
log.debug("creating sys-pipe")
|
100
|
-
name = File.join(PIPE_PATH, "sys_pipe_#{self.generate_guid}")
|
101
|
-
`mkfifo #{name}`
|
102
|
-
@sys_pipe = File.new(name, "r+")
|
103
|
-
$stdout.puts(MessageCoder.encode({:msg_type => :system,
|
104
|
-
:sys_pipe => name,
|
105
|
-
:use_ack => @use_ack,
|
106
|
-
:max_unackd => @max_unackd}))
|
107
|
-
$stdout.flush
|
108
|
-
end
|
109
|
-
|
110
|
-
def destroy_sys_pipe
|
111
|
-
`rm #{@sys_pipe.path}`
|
112
|
-
end
|
113
108
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
109
|
+
##
|
110
|
+
# Support templating of member data.
|
111
|
+
#
|
112
|
+
def get_binding
|
113
|
+
binding
|
118
114
|
end
|
119
115
|
|
120
|
-
#
|
121
|
-
def
|
122
|
-
|
123
|
-
if @ix and @ix >= 10_000
|
124
|
-
@ix = nil
|
125
|
-
@seed = nil
|
126
|
-
end
|
127
|
-
|
128
|
-
# NB. This will only work on *nix platforms
|
129
|
-
@seed ||= `uuidgen`.chomp.gsub(/-/,"")
|
130
|
-
@ix ||= 0
|
131
|
-
|
132
|
-
"#{@seed}#{@ix += 1}"
|
133
|
-
end
|
134
|
-
|
135
|
-
def process_message(header, body)
|
136
|
-
msg = YAML.load(body)
|
137
|
-
store_ack(msg, header) if @use_ack
|
138
|
-
write_msg(msg)
|
116
|
+
# :nodoc:
|
117
|
+
def name
|
118
|
+
options[:name] || ''
|
139
119
|
end
|
140
120
|
|
141
|
-
|
142
|
-
|
143
|
-
|
121
|
+
# :nodoc:
|
122
|
+
def queue_name
|
123
|
+
options[:name] || ''
|
144
124
|
end
|
145
125
|
|
146
|
-
|
147
|
-
header = @unackd_msgs.delete(ack_id)
|
148
|
-
header.ack
|
149
|
-
end
|
126
|
+
protected
|
150
127
|
|
151
|
-
|
152
|
-
|
153
|
-
|
128
|
+
##
|
129
|
+
# Process the message received from AMQP server
|
130
|
+
#
|
131
|
+
# @param header<MQ::Header> The header of the message
|
132
|
+
# @param body Description
|
133
|
+
#
|
134
|
+
def process_queue_message(header, body)
|
135
|
+
DefaultLogger.debug("MessageSubscriber#process_queue_message(header, body)")
|
136
|
+
unless body.is_a?(Hash)
|
137
|
+
message = { :msg_type => :standard, :raw => body }
|
138
|
+
else
|
139
|
+
message = { :msg_type => :standard }.merge(body)
|
140
|
+
end
|
141
|
+
store_acknowledgement(message, header) if options[:ack]
|
142
|
+
write(message)
|
154
143
|
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
##
|
148
|
+
# Reset the instance variables used to store message statistics
|
149
|
+
#
|
150
|
+
def reset_message_statistics
|
151
|
+
@count = 0
|
152
|
+
@prev_time = Time.now
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Calculate the statistics on message handling
|
157
|
+
#
|
158
|
+
def calculate_message_statistics
|
159
|
+
@time_delta = Time.now - @prev_time
|
160
|
+
@mps = @count / @time_delta
|
161
|
+
reset_message_statistics
|
162
|
+
end
|
155
163
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
@
|
164
|
+
##
|
165
|
+
# Create the acknowledgement named pipe
|
166
|
+
#
|
167
|
+
def create_ack_pipe
|
168
|
+
name = File.join(PIPE_PATH, "sys_pipe_#{generate_guid}")
|
169
|
+
`mkfifo #{name}`
|
170
|
+
@ack_pipe = File.new(name, "r+")
|
171
|
+
message = { :msg_type => :system,
|
172
|
+
:sys_pipe => name,
|
173
|
+
:ack => options[:ack] }
|
174
|
+
write_to_pipe(message)
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Destroy the acknowledgement named pipe
|
179
|
+
#
|
180
|
+
def destroy_ack_pipe
|
181
|
+
`rm #{@ack_pipe.path}`
|
163
182
|
end
|
164
183
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
184
|
+
##
|
185
|
+
# Generates a unique guid to use as part of the acknowledgement named pipe name.
|
186
|
+
# Stolen from EM.
|
187
|
+
#
|
188
|
+
def generate_guid
|
189
|
+
# Cache uuidgen seed for better performance
|
190
|
+
if @ix and @ix >= 10_000
|
191
|
+
@ix = nil
|
192
|
+
@seed = nil
|
174
193
|
end
|
175
|
-
end
|
176
|
-
end
|
177
194
|
|
178
|
-
|
179
|
-
|
195
|
+
# NB. This will only work on *nix platforms
|
196
|
+
@seed ||= `uuidgen`.chomp.gsub(/-/,"")
|
197
|
+
@ix ||= 0
|
180
198
|
|
181
|
-
|
182
|
-
@msg_sub = msg_sub
|
199
|
+
"#{@seed}#{@ix += 1}"
|
183
200
|
end
|
184
201
|
|
185
|
-
|
186
|
-
|
187
|
-
|
202
|
+
##
|
203
|
+
# Write the message to STDOUT
|
204
|
+
#
|
205
|
+
# @param message<Hash> The message to write
|
206
|
+
#
|
207
|
+
def write(message)
|
208
|
+
DefaultLogger.debug("MessageSubscriber#write(message)")
|
209
|
+
@count += 1
|
210
|
+
write_to_pipe(message)
|
188
211
|
end
|
189
212
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
background: black;
|
201
|
-
color: #80c0c0;
|
202
|
-
}
|
203
|
-
h1 {
|
204
|
-
font: 12pt Monospace;
|
205
|
-
text-align:center;
|
206
|
-
}
|
207
|
-
table {
|
208
|
-
font: 10pt Monospace;
|
209
|
-
margin-left:auto;
|
210
|
-
margin-right:auto;
|
211
|
-
text-align:right;
|
212
|
-
}
|
213
|
-
.page {
|
214
|
-
position:relative;
|
215
|
-
top: 20%;
|
216
|
-
# border-style:solid;
|
217
|
-
# border-width:5px;
|
218
|
-
width: 30%;
|
219
|
-
margin-left:auto;
|
220
|
-
margin-right:auto;
|
221
|
-
}
|
222
|
-
</style>
|
223
|
-
</head>
|
224
|
-
<body>
|
225
|
-
<div class=page>
|
226
|
-
<h1><span class="name">#{@msg_sub.name}</span></h1>
|
227
|
-
<table>
|
228
|
-
<tr>
|
229
|
-
<td>Structure:</td><td><span class="queue_in">#{@msg_sub.queue_name}</span> -> <span class="exchanges_out">#{@msg_sub.exchanges_out}</span></td>
|
230
|
-
</tr>
|
231
|
-
<tr>
|
232
|
-
<td>Throughput:</td><td><span class="mps">#{@msg_sub.mps.to_i}</span></td>
|
233
|
-
</tr>
|
234
|
-
<tr>
|
235
|
-
<td>Uptime:</td><td><span class="uptime">#{(Time.now - @msg_sub.start_time).to_i / 60}mins</span></td>
|
236
|
-
</tr>
|
237
|
-
</table>
|
238
|
-
</div>
|
239
|
-
</body>
|
240
|
-
</html>
|
241
|
-
EOL
|
242
|
-
|
243
|
-
response.send_response
|
213
|
+
##
|
214
|
+
# Initialize the DNS Service Discovery (aka Bonjour, MDNS).
|
215
|
+
#
|
216
|
+
def initialize_dnssd
|
217
|
+
return unless options[:dnssd]
|
218
|
+
# FIXME. DNS-SD breaks when under load. Not sure what problem is yet, have raised it with
|
219
|
+
# gem author:
|
220
|
+
# http://github.com/tenderlove/dnssd/issues#issue/3
|
221
|
+
require 'dnssd'
|
222
|
+
DNSSD.register!("#{options[:name]}_pipe", "_http._tcp", nil, options[:http_port])
|
244
223
|
end
|
245
|
-
end
|
246
|
-
|
247
|
-
end
|
248
224
|
|
225
|
+
def write_to_pipe(message, pipe=$stdout)
|
226
|
+
pipe.syswrite(MessageCoder.encode(message) << "\n")
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|