gilmour 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/gilmour.gemspec +18 -18
- data/lib/gilmour/responder.rb +126 -28
- data/lib/gilmour/stdhijack.rb +49 -0
- data/lib/gilmour/waiter.rb +35 -0
- data/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 762241aef84bf02c572d89ac9f22c16b710911dc
|
4
|
+
data.tar.gz: 50f90851b8bbe757576118d44480b4d102aa6d0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d91f45f9377ff883ff8c371c21212e2702323649c4d45d819bf405bbb570aa3ccd0dff4c401a85d5d59e6a745b2a2398f456947bdc83183c51dc99a0ed31e573
|
7
|
+
data.tar.gz: 8202e63ba3940829ccac7a89aeba02336f0b2f6b18001c73d3549da496a4576ddb54d01b6ded31472bc2af8891bab5d24b8512bed8dda696065b35c63a1a2b7c
|
data/gilmour.gemspec
CHANGED
@@ -3,24 +3,24 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
require "./version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
s.name = "gilmour"
|
7
|
+
s.version = Gilmour::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Aditya Godbole", "Piyush Verma"]
|
10
|
+
s.email = ["code.aa@gdbl.me", "piyush@piyushverma.net"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{A Sinatra like DSL for implementing AMQP services}
|
13
|
+
s.description = %q{This gem provides a Sinatra like DSL and a simple protocol to enable writing services that communicate over AMQP}
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
s.add_development_dependency "rspec"
|
16
|
+
s.add_development_dependency "rspec-given"
|
17
|
+
s.add_dependency "mash"
|
18
|
+
s.add_dependency "redis"
|
19
|
+
s.add_dependency "gilmour-em-hiredis"
|
20
|
+
s.add_dependency "amqp"
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
26
|
end
|
data/lib/gilmour/responder.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'json'
|
4
4
|
require 'logger'
|
5
|
+
require_relative './stdhijack'
|
6
|
+
require_relative './waiter'
|
5
7
|
|
6
8
|
# Top level module
|
7
9
|
module Gilmour
|
@@ -20,9 +22,23 @@ module Gilmour
|
|
20
22
|
end
|
21
23
|
|
22
24
|
class Responder
|
25
|
+
CAPTURE_STDOUT = false
|
26
|
+
LOG_SEPERATOR = '%%'
|
27
|
+
LOG_PREFIX = "#{LOG_SEPERATOR}gilmour#{LOG_SEPERATOR}"
|
28
|
+
|
23
29
|
attr_reader :logger
|
24
30
|
attr_reader :request
|
25
31
|
|
32
|
+
def fork_logger
|
33
|
+
logger = Logger.new(STDERR)
|
34
|
+
loglevel = ENV["LOG_LEVEL"] ? ENV["LOG_LEVEL"].to_sym : :warn
|
35
|
+
logger.level = Gilmour::LoggerLevels[loglevel] || Logger::WARN
|
36
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
37
|
+
"#{LOG_PREFIX}#{severity}#{LOG_SEPERATOR}#{msg}"
|
38
|
+
end
|
39
|
+
logger
|
40
|
+
end
|
41
|
+
|
26
42
|
def make_logger
|
27
43
|
logger = Logger.new(STDERR)
|
28
44
|
original_formatter = Logger::Formatter.new
|
@@ -82,53 +98,129 @@ module Gilmour
|
|
82
98
|
end
|
83
99
|
end
|
84
100
|
|
101
|
+
def pub_relay(waiter)
|
102
|
+
Thread.new {
|
103
|
+
waiter.add 1
|
104
|
+
loop {
|
105
|
+
begin
|
106
|
+
data = @read_publish_pipe.readline
|
107
|
+
destination, message = JSON.parse(data)
|
108
|
+
@backend.publish(message, destination)
|
109
|
+
rescue EOFError
|
110
|
+
waiter.done
|
111
|
+
rescue Exception => e
|
112
|
+
GLogger.debug e.message
|
113
|
+
GLogger.debug e.backtrace
|
114
|
+
end
|
115
|
+
}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def io_readers(parent_io, waiter)
|
120
|
+
io_threads = []
|
121
|
+
|
122
|
+
parent_io.each do |reader|
|
123
|
+
io_threads << Thread.new {
|
124
|
+
waiter.add 1
|
125
|
+
loop {
|
126
|
+
begin
|
127
|
+
data = reader.readline.chomp
|
128
|
+
if data.empty?
|
129
|
+
next
|
130
|
+
end
|
131
|
+
|
132
|
+
if data.start_with?(LOG_PREFIX)
|
133
|
+
data.split(LOG_PREFIX).each do |msg|
|
134
|
+
msg_grp = msg.split(LOG_SEPERATOR, 2)
|
135
|
+
|
136
|
+
if msg_grp.length > 1
|
137
|
+
data = msg_grp[1]
|
138
|
+
case msg_grp[0]
|
139
|
+
when 'INFO'
|
140
|
+
logger.info data
|
141
|
+
when 'UNKNOWN'
|
142
|
+
logger.unknown data
|
143
|
+
when 'WARN'
|
144
|
+
logger.warn data
|
145
|
+
when 'ERROR'
|
146
|
+
logger.error data
|
147
|
+
when 'FATAL'
|
148
|
+
logger.fatal data
|
149
|
+
else
|
150
|
+
logger.debug data
|
151
|
+
end
|
152
|
+
else
|
153
|
+
logger.debug msg
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
next
|
158
|
+
end
|
159
|
+
|
160
|
+
logger.debug data
|
161
|
+
rescue EOFError
|
162
|
+
waiter.done
|
163
|
+
rescue Exception => e
|
164
|
+
GLogger.error e.message
|
165
|
+
GLogger.error e.backtrace
|
166
|
+
end
|
167
|
+
}
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
io_threads
|
172
|
+
end
|
173
|
+
|
85
174
|
# Called by parent
|
86
175
|
# :nodoc:
|
87
176
|
def execute(handler)
|
88
177
|
if @multi_process
|
89
178
|
GLogger.debug "Executing #{@sender} in forked moode"
|
90
|
-
@read_pipe = @pipe[0]
|
91
|
-
@write_pipe = @pipe[1]
|
92
179
|
|
93
|
-
@
|
94
|
-
@write_publish_pipe = @publish_pipe
|
180
|
+
@read_pipe, @write_pipe = @pipe
|
181
|
+
@read_publish_pipe, @write_publish_pipe = @publish_pipe
|
182
|
+
|
183
|
+
out_r, out_w = IO.pipe
|
184
|
+
parent_io = [out_r]
|
185
|
+
child_io = [out_w]
|
186
|
+
|
187
|
+
if CAPTURE_STDOUT == true
|
188
|
+
err_r, err_w = IO.pipe
|
189
|
+
child_io << err_w
|
190
|
+
parent_io << err_r
|
191
|
+
end
|
95
192
|
|
96
193
|
pid = Process.fork do
|
97
194
|
@backend.stop
|
98
195
|
EventMachine.stop_event_loop
|
196
|
+
|
197
|
+
#Close the parent channels in forked process
|
99
198
|
@read_pipe.close
|
100
199
|
@read_publish_pipe.close
|
200
|
+
parent_io.each{|io| io.close}
|
201
|
+
|
101
202
|
@response_sent = false
|
102
|
-
|
203
|
+
@logger = fork_logger
|
204
|
+
|
205
|
+
capture_output(child_io, CAPTURE_STDOUT) {
|
206
|
+
_execute(handler)
|
207
|
+
}
|
103
208
|
end
|
104
209
|
|
210
|
+
# Cleanup the writers in Parent process.
|
211
|
+
child_io.each {|io| io.close }
|
212
|
+
|
105
213
|
@write_pipe.close
|
106
214
|
@write_publish_pipe.close
|
107
215
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
loop {
|
112
|
-
begin
|
113
|
-
data = @read_publish_pipe.readline
|
114
|
-
pub_mutex.synchronize do
|
115
|
-
destination, message = JSON.parse(data)
|
116
|
-
@backend.publish(message, destination)
|
117
|
-
end
|
118
|
-
rescue EOFError
|
119
|
-
# awkward blank rescue block
|
120
|
-
rescue Exception => e
|
121
|
-
GLogger.debug e.message
|
122
|
-
GLogger.debug e.backtrace
|
123
|
-
end
|
124
|
-
}
|
125
|
-
}
|
216
|
+
wg = Gilmour::Waiter.new
|
217
|
+
io_threads = io_readers(parent_io, wg)
|
218
|
+
io_threads << pub_relay(wg)
|
126
219
|
|
127
220
|
begin
|
128
221
|
receive_data(@read_pipe.readline)
|
129
222
|
rescue EOFError => e
|
130
223
|
logger.debug e.message
|
131
|
-
logger.debug "EOFError caught in responder.rb, because of nil response"
|
132
224
|
end
|
133
225
|
|
134
226
|
pid, status = Process.waitpid2(pid)
|
@@ -146,12 +238,18 @@ module Gilmour
|
|
146
238
|
write_response(@sender, msg, 500)
|
147
239
|
end
|
148
240
|
|
149
|
-
|
150
|
-
|
241
|
+
@read_pipe.close
|
242
|
+
|
243
|
+
wg.wait do
|
244
|
+
io_threads.each { |th|
|
245
|
+
th.kill
|
246
|
+
}
|
151
247
|
end
|
152
248
|
|
153
|
-
|
249
|
+
# Cleanup.
|
154
250
|
@read_publish_pipe.close
|
251
|
+
parent_io.each{|io| io.close unless io.closed?}
|
252
|
+
|
155
253
|
else
|
156
254
|
_execute(handler)
|
157
255
|
end
|
@@ -162,7 +260,7 @@ module Gilmour
|
|
162
260
|
# supplied at setup.
|
163
261
|
def emit_error(message, code = 500, extra = {})
|
164
262
|
opts = {
|
165
|
-
topic: @request.topic,
|
263
|
+
topic: @request.topic,
|
166
264
|
request_data: @request.body,
|
167
265
|
userdata: JSON.generate(extra || {}),
|
168
266
|
sender: @sender,
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# StringIO also do as IO, but IO#reopen fails.
|
2
|
+
# The problem is that a StringIO cannot exist in the O/S's file descriptor
|
3
|
+
# table. STDERR.reopen(...) at the low level does a dup() or dup2() to
|
4
|
+
# copy one file descriptor to another.
|
5
|
+
#
|
6
|
+
# I have two options:
|
7
|
+
#
|
8
|
+
# (1) $stderr = StringIO.new
|
9
|
+
# Then any program which writes to $stderr will be fine. But anything
|
10
|
+
# which writes to STDERR will still go to file descriptor 2.
|
11
|
+
#
|
12
|
+
# (2) reopen STDERR with something which exists in the O/S file descriptor
|
13
|
+
# table: e.g. a file or a pipe.
|
14
|
+
#
|
15
|
+
# I canot use a file, hence a Pipe.
|
16
|
+
|
17
|
+
def capture_output(pipes, capture_stdout=false)
|
18
|
+
streams = []
|
19
|
+
|
20
|
+
if capture_stdout == true
|
21
|
+
streams << $stdout
|
22
|
+
end
|
23
|
+
|
24
|
+
streams << $stderr
|
25
|
+
|
26
|
+
# Save the streams to be reassigned later.
|
27
|
+
# Actually it doesn't matter because the child process would be killed
|
28
|
+
# anyway after the work is done.
|
29
|
+
saved = streams.each do |stream|
|
30
|
+
stream.dup
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
streams.each_with_index do |stream, ix|
|
35
|
+
# Probably I should not use IX, otherwise stdout and stderr can arrive
|
36
|
+
# out of order, which they should?
|
37
|
+
# If I reopen both of them on the same PIPE, they are guaranteed to
|
38
|
+
# arrive in order.
|
39
|
+
stream.reopen(pipes[ix])
|
40
|
+
end
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
# This is sort of meaningless, just makes sense aesthetically.
|
44
|
+
# To return what was borrowed.
|
45
|
+
streams.each_with_index do |stream, i|
|
46
|
+
stream.reopen(saved[i])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/gilmour/waiter.rb
CHANGED
@@ -1,11 +1,25 @@
|
|
1
1
|
module Gilmour
|
2
2
|
class Waiter
|
3
3
|
def initialize
|
4
|
+
@count = 0
|
4
5
|
@done = false
|
5
6
|
@waiter_m = Mutex.new
|
6
7
|
@waiter_c = ConditionVariable.new
|
7
8
|
end
|
8
9
|
|
10
|
+
def add n = 1
|
11
|
+
synchronize { @count += n }
|
12
|
+
end
|
13
|
+
|
14
|
+
def done
|
15
|
+
synchronize do
|
16
|
+
@count -= n
|
17
|
+
if @count == 0
|
18
|
+
signal
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
9
23
|
def synchronize(&blk)
|
10
24
|
@waiter_m.synchronize(&blk)
|
11
25
|
end
|
@@ -13,12 +27,33 @@ module Gilmour
|
|
13
27
|
def signal
|
14
28
|
synchronize do
|
15
29
|
@done = true
|
30
|
+
@count = 0
|
16
31
|
@waiter_c.signal
|
17
32
|
end
|
18
33
|
end
|
19
34
|
|
20
35
|
def wait(timeout=nil)
|
21
36
|
synchronize { @waiter_c.wait(@waiter_m, timeout) unless @done }
|
37
|
+
yield if block_given?
|
22
38
|
end
|
23
39
|
end
|
24
40
|
end
|
41
|
+
|
42
|
+
def test
|
43
|
+
wg = Gilmour::Waiter.new
|
44
|
+
wg.add 3
|
45
|
+
|
46
|
+
3.times do
|
47
|
+
Thread.new {
|
48
|
+
t = rand(10000) / 10000.0
|
49
|
+
sleep(t)
|
50
|
+
puts "done\n"
|
51
|
+
wg.done
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
wg.wait do
|
56
|
+
puts "All jobs done"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gilmour
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aditya Godbole
|
8
|
+
- Piyush Verma
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
12
|
+
date: 2015-07-28 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: rspec
|
@@ -98,6 +99,7 @@ description: This gem provides a Sinatra like DSL and a simple protocol to enabl
|
|
98
99
|
writing services that communicate over AMQP
|
99
100
|
email:
|
100
101
|
- code.aa@gdbl.me
|
102
|
+
- piyush@piyushverma.net
|
101
103
|
executables: []
|
102
104
|
extensions: []
|
103
105
|
extra_rdoc_files: []
|
@@ -117,6 +119,7 @@ files:
|
|
117
119
|
- lib/gilmour/base.rb
|
118
120
|
- lib/gilmour/protocol.rb
|
119
121
|
- lib/gilmour/responder.rb
|
122
|
+
- lib/gilmour/stdhijack.rb
|
120
123
|
- lib/gilmour/waiter.rb
|
121
124
|
- test/spec/helpers/common.rb
|
122
125
|
- test/spec/helpers/connection.rb
|