rjr 0.12.2 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +49 -36
- data/Rakefile +2 -0
- data/bin/rjr-client +11 -9
- data/bin/rjr-server +12 -10
- data/examples/amqp.rb +29 -0
- data/examples/client.rb +32 -0
- data/examples/complete.rb +36 -0
- data/examples/local.rb +29 -0
- data/examples/server.rb +26 -0
- data/examples/tcp.rb +29 -0
- data/examples/web.rb +22 -0
- data/examples/ws.rb +29 -0
- data/lib/rjr/common.rb +7 -12
- data/lib/rjr/dispatcher.rb +171 -239
- data/lib/rjr/em_adapter.rb +33 -66
- data/lib/rjr/message.rb +43 -12
- data/lib/rjr/node.rb +197 -103
- data/lib/rjr/nodes/amqp.rb +216 -0
- data/lib/rjr/nodes/easy.rb +159 -0
- data/lib/rjr/nodes/local.rb +118 -0
- data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
- data/lib/rjr/nodes/multi.rb +79 -0
- data/lib/rjr/nodes/tcp.rb +211 -0
- data/lib/rjr/nodes/web.rb +197 -0
- data/lib/rjr/nodes/ws.rb +187 -0
- data/lib/rjr/stats.rb +70 -0
- data/lib/rjr/thread_pool.rb +178 -123
- data/site/index.html +45 -0
- data/site/jquery-latest.js +9404 -0
- data/site/jrw.js +297 -0
- data/site/json.js +199 -0
- data/specs/dispatcher_spec.rb +244 -198
- data/specs/em_adapter_spec.rb +52 -80
- data/specs/message_spec.rb +223 -197
- data/specs/node_spec.rb +67 -163
- data/specs/nodes/amqp_spec.rb +82 -0
- data/specs/nodes/easy_spec.rb +13 -0
- data/specs/nodes/local_spec.rb +72 -0
- data/specs/nodes/multi_spec.rb +65 -0
- data/specs/nodes/tcp_spec.rb +75 -0
- data/specs/nodes/web_spec.rb +77 -0
- data/specs/nodes/ws_spec.rb +78 -0
- data/specs/stats_spec.rb +59 -0
- data/specs/thread_pool_spec.rb +44 -35
- metadata +40 -30
- data/lib/rjr/amqp_node.rb +0 -330
- data/lib/rjr/inspect.rb +0 -65
- data/lib/rjr/local_node.rb +0 -150
- data/lib/rjr/multi_node.rb +0 -65
- data/lib/rjr/tcp_node.rb +0 -323
- data/lib/rjr/thread_pool2.rb +0 -272
- data/lib/rjr/util.rb +0 -104
- data/lib/rjr/web_node.rb +0 -266
- data/lib/rjr/ws_node.rb +0 -289
- data/lib/rjr.rb +0 -16
- data/specs/amqp_node_spec.rb +0 -31
- data/specs/inspect_spec.rb +0 -60
- data/specs/local_node_spec.rb +0 -43
- data/specs/multi_node_spec.rb +0 -45
- data/specs/tcp_node_spec.rb +0 -33
- data/specs/util_spec.rb +0 -46
- data/specs/web_node_spec.rb +0 -32
- data/specs/ws_node_spec.rb +0 -32
- /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
- /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rjr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 2.0.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 2.0.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: eventmachine
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,40 +68,50 @@ executables:
|
|
68
68
|
extensions: []
|
69
69
|
extra_rdoc_files: []
|
70
70
|
files:
|
71
|
+
- examples/web.rb
|
72
|
+
- examples/tcp.rb
|
73
|
+
- examples/server.rb
|
74
|
+
- examples/ws.rb
|
75
|
+
- examples/local.rb
|
76
|
+
- examples/complete.rb
|
77
|
+
- examples/amqp.rb
|
78
|
+
- examples/client.rb
|
71
79
|
- lib/rjr/semaphore.rb
|
72
|
-
- lib/rjr/udp_node.rb
|
73
80
|
- lib/rjr/em_adapter.rb
|
74
81
|
- lib/rjr/errors.rb
|
75
|
-
- lib/rjr/missing_node.rb
|
76
|
-
- lib/rjr/inspect.rb
|
77
|
-
- lib/rjr/ws_node.rb
|
78
|
-
- lib/rjr/tcp_node.rb
|
79
|
-
- lib/rjr/web_node.rb
|
80
82
|
- lib/rjr/node.rb
|
81
|
-
- lib/rjr/thread_pool2.rb
|
82
83
|
- lib/rjr/common.rb
|
83
84
|
- lib/rjr/thread_pool.rb
|
84
85
|
- lib/rjr/message.rb
|
85
|
-
- lib/rjr/
|
86
|
-
- lib/rjr/
|
87
|
-
- lib/rjr/
|
86
|
+
- lib/rjr/stats.rb
|
87
|
+
- lib/rjr/nodes/web.rb
|
88
|
+
- lib/rjr/nodes/udp.rb
|
89
|
+
- lib/rjr/nodes/tcp.rb
|
90
|
+
- lib/rjr/nodes/ws.rb
|
91
|
+
- lib/rjr/nodes/tcp2.rb
|
92
|
+
- lib/rjr/nodes/local.rb
|
93
|
+
- lib/rjr/nodes/amqp.rb
|
94
|
+
- lib/rjr/nodes/easy.rb
|
95
|
+
- lib/rjr/nodes/missing.rb
|
96
|
+
- lib/rjr/nodes/multi.rb
|
88
97
|
- lib/rjr/dispatcher.rb
|
89
|
-
- lib/rjr/util.rb
|
90
|
-
- lib/rjr/amqp_node.rb
|
91
|
-
- lib/rjr.rb
|
92
98
|
- specs/dispatcher_spec.rb
|
93
99
|
- specs/node_spec.rb
|
94
|
-
- specs/
|
95
|
-
- specs/
|
96
|
-
- specs/
|
97
|
-
- specs/
|
98
|
-
- specs/
|
99
|
-
- specs/
|
100
|
-
- specs/
|
101
|
-
- specs/
|
100
|
+
- specs/stats_spec.rb
|
101
|
+
- specs/nodes/multi_spec.rb
|
102
|
+
- specs/nodes/tcp_spec.rb
|
103
|
+
- specs/nodes/web_spec.rb
|
104
|
+
- specs/nodes/local_spec.rb
|
105
|
+
- specs/nodes/amqp_spec.rb
|
106
|
+
- specs/nodes/ws_spec.rb
|
107
|
+
- specs/nodes/easy_spec.rb
|
102
108
|
- specs/thread_pool_spec.rb
|
103
109
|
- specs/message_spec.rb
|
104
110
|
- specs/em_adapter_spec.rb
|
111
|
+
- site/index.html
|
112
|
+
- site/jquery-latest.js
|
113
|
+
- site/json.js
|
114
|
+
- site/jrw.js
|
105
115
|
- LICENSE
|
106
116
|
- Rakefile
|
107
117
|
- README.md
|
@@ -127,9 +137,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
137
|
- !ruby/object:Gem::Version
|
128
138
|
version: 1.3.3
|
129
139
|
requirements:
|
130
|
-
- amqp gem is needed to use the amqp node
|
131
|
-
- eventmachine_httpserver and em-http-request gems are needed to use the web node
|
132
|
-
- em-websocket and em-websocket-client gems are needed to use the web socket node
|
140
|
+
- The amqp gem and a running rabbitmq server is needed to use the amqp node
|
141
|
+
- The eventmachine_httpserver and em-http-request gems are needed to use the web node
|
142
|
+
- The em-websocket and em-websocket-client gems are needed to use the web socket node
|
133
143
|
rubyforge_project:
|
134
144
|
rubygems_version: 1.8.24
|
135
145
|
signing_key:
|
data/lib/rjr/amqp_node.rb
DELETED
@@ -1,330 +0,0 @@
|
|
1
|
-
# RJR AMQP Endpoint
|
2
|
-
#
|
3
|
-
# Implements the RJR::Node interface to satisty JSON-RPC requests over the AMQP protocol
|
4
|
-
#
|
5
|
-
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
6
|
-
# Licensed under the Apache License, Version 2.0
|
7
|
-
|
8
|
-
skip_module = false
|
9
|
-
begin
|
10
|
-
require 'amqp'
|
11
|
-
rescue LoadError
|
12
|
-
skip_module = true
|
13
|
-
end
|
14
|
-
|
15
|
-
if skip_module
|
16
|
-
# TODO output: "amqp gem could not be loaded, skipping amqp node definition"
|
17
|
-
require 'rjr/missing_node'
|
18
|
-
RJR::AMQPNode = RJR::MissingNode
|
19
|
-
|
20
|
-
else
|
21
|
-
require 'rjr/node'
|
22
|
-
require 'rjr/message'
|
23
|
-
require 'rjr/dispatcher'
|
24
|
-
require 'rjr/errors'
|
25
|
-
require 'rjr/thread_pool2'
|
26
|
-
|
27
|
-
module RJR
|
28
|
-
|
29
|
-
# AMQP node callback interface, used to invoke json-rpc methods on a
|
30
|
-
# remote node which previously invoked a method on the local one.
|
31
|
-
#
|
32
|
-
# After a node sends a json-rpc request to another, the either node may send
|
33
|
-
# additional requests to each other via amqp through this callback interface
|
34
|
-
# until the queues are closed
|
35
|
-
class AMQPNodeCallback
|
36
|
-
|
37
|
-
# AMQPNodeCallback initializer
|
38
|
-
# @param [Hash] args the options to create the amqp node callback with
|
39
|
-
# @option args [AMQPNode] :node amqp node used to send/receive messages
|
40
|
-
# @option args [String] :destination name of the queue to invoke callbacks on
|
41
|
-
def initialize(args = {})
|
42
|
-
@node = args[:node]
|
43
|
-
@destination = args[:destination]
|
44
|
-
end
|
45
|
-
|
46
|
-
# Implementation of {RJR::NodeCallback#invoke}
|
47
|
-
def invoke(callback_method, *data)
|
48
|
-
msg = NotificationMessage.new :method => callback_method, :args => data, :headers => @message_headers
|
49
|
-
@node.publish(msg.to_s, :routing_key => @destination, :mandatory => true) { }
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# AMQP node definition, implements the {RJR::Node} interface to
|
54
|
-
# listen for and invoke json-rpc requests over
|
55
|
-
# {http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol AMQP}.
|
56
|
-
#
|
57
|
-
# Clients should specify the amqp broker to connect to when initializing
|
58
|
-
# a node and specify the remote queue when invoking requests.
|
59
|
-
#
|
60
|
-
# @example Listening for json-rpc requests over amqp
|
61
|
-
# # register rjr dispatchers (see RJR::Dispatcher)
|
62
|
-
# RJR::Dispatcher.add_handler('hello') { |name|
|
63
|
-
# "Hello #{name}!"
|
64
|
-
# }
|
65
|
-
#
|
66
|
-
# # initialize node, listen, and block
|
67
|
-
# server = RJR::AMQPNode.new :node_id => 'server', :broker => 'localhost'
|
68
|
-
# server.listen
|
69
|
-
# server.join
|
70
|
-
#
|
71
|
-
# @example Invoking json-rpc requests over amqp
|
72
|
-
# client = RJR::AMQPNode.new :node_id => 'client', :broker => 'localhost'
|
73
|
-
# puts client.invoke_request('server-queue', 'hello', 'mo') # the queue name is set to "#{node_id}-queue"
|
74
|
-
class AMQPNode < RJR::Node
|
75
|
-
RJR_NODE_TYPE = :amqp
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
# Internal helper, handle message pulled off queue
|
80
|
-
def handle_message(metadata, msg)
|
81
|
-
if RequestMessage.is_request_message?(msg)
|
82
|
-
reply_to = metadata.reply_to
|
83
|
-
ThreadPool2Manager << ThreadPool2Job.new { handle_request(reply_to, msg, false) }
|
84
|
-
|
85
|
-
elsif NotificationMessage.is_notification_message?(msg)
|
86
|
-
reply_to = metadata.reply_to
|
87
|
-
ThreadPool2Manager << ThreadPool2Job.new { handle_request(reply_to, msg, true) }
|
88
|
-
|
89
|
-
elsif ResponseMessage.is_response_message?(msg)
|
90
|
-
handle_response(msg)
|
91
|
-
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Internal helper, handle request message pulled off queue
|
96
|
-
def handle_request(reply_to, message, notification=false)
|
97
|
-
msg = notification ? NotificationMessage.new(:message => message, :headers => @message_headers) :
|
98
|
-
RequestMessage.new(:message => message, :headers => @message_headers)
|
99
|
-
headers = @message_headers.merge(msg.headers) # append request message headers
|
100
|
-
result = Dispatcher.dispatch_request(msg.jr_method,
|
101
|
-
:method_args => msg.jr_args,
|
102
|
-
:headers => headers,
|
103
|
-
:client_ip => nil, # since client doesn't directly connect to server, we can't leverage
|
104
|
-
:client_port => nil, # client ip / port for requests received via the amqp node type
|
105
|
-
:rjr_node => self,
|
106
|
-
:rjr_node_id => @node_id,
|
107
|
-
:rjr_node_type => RJR_NODE_TYPE,
|
108
|
-
:rjr_callback =>
|
109
|
-
AMQPNodeCallback.new(:node => self,
|
110
|
-
:exchange => @exchange,
|
111
|
-
:destination => reply_to,
|
112
|
-
:headers => headers))
|
113
|
-
unless notification
|
114
|
-
response = ResponseMessage.new(:id => msg.msg_id, :result => result, :headers => headers)
|
115
|
-
publish(response.to_s, :routing_key => reply_to) { }
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Internal helper, handle response message pulled off queue
|
120
|
-
def handle_response(message)
|
121
|
-
msg = ResponseMessage.new(:message => message, :headers => @message_headers)
|
122
|
-
res = err = nil
|
123
|
-
begin
|
124
|
-
res = Dispatcher.handle_response(msg.result)
|
125
|
-
rescue Exception => e
|
126
|
-
err = e.to_s
|
127
|
-
end
|
128
|
-
|
129
|
-
@response_lock.synchronize{
|
130
|
-
result = [msg.msg_id, res]
|
131
|
-
result << err if !err.nil?
|
132
|
-
@responses << result
|
133
|
-
@response_cv.signal
|
134
|
-
}
|
135
|
-
end
|
136
|
-
|
137
|
-
# Initialize the amqp subsystem
|
138
|
-
def init_node(&on_init)
|
139
|
-
if !@conn.nil? && @conn.connected?
|
140
|
-
on_init.call
|
141
|
-
return
|
142
|
-
end
|
143
|
-
|
144
|
-
super
|
145
|
-
@conn = AMQP.connect(:host => @broker) do |*args|
|
146
|
-
on_init.call
|
147
|
-
end
|
148
|
-
@conn.on_tcp_connection_failure { puts "OTCF #{@node_id}" }
|
149
|
-
|
150
|
-
# TODO move the rest into connect callback ?
|
151
|
-
|
152
|
-
### connect to qpid broker
|
153
|
-
@channel = AMQP::Channel.new(@conn)
|
154
|
-
|
155
|
-
# qpid constructs that will be created for node
|
156
|
-
@queue_name = "#{@node_id.to_s}-queue"
|
157
|
-
@queue = @channel.queue(@queue_name, :auto_delete => true)
|
158
|
-
@exchange = @channel.default_exchange
|
159
|
-
|
160
|
-
@listening = false
|
161
|
-
#@disconnected = false
|
162
|
-
|
163
|
-
@exchange.on_return do |basic_return, metadata, payload|
|
164
|
-
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
165
|
-
#@disconnected = true # FIXME member will be set on wrong class
|
166
|
-
connection_event(:error)
|
167
|
-
connection_event(:closed)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
# Internal helper, block until response matching message id is received
|
172
|
-
def wait_for_result(message)
|
173
|
-
res = nil
|
174
|
-
while res.nil?
|
175
|
-
@response_lock.synchronize{
|
176
|
-
# FIXME throw err if more than 1 match found
|
177
|
-
res = @responses.select { |response| message.msg_id == response.first }.first
|
178
|
-
if !res.nil?
|
179
|
-
@responses.delete(res)
|
180
|
-
|
181
|
-
else
|
182
|
-
@response_cv.signal
|
183
|
-
@response_cv.wait @response_lock
|
184
|
-
end
|
185
|
-
}
|
186
|
-
end
|
187
|
-
return res
|
188
|
-
end
|
189
|
-
|
190
|
-
# Internal helper, run connection event handlers for specified event
|
191
|
-
# TODO these are only run when we fail to send message to queue, need to detect when that queue is shutdown & other events
|
192
|
-
def connection_event(event)
|
193
|
-
if @connection_event_handlers.keys.include?(event)
|
194
|
-
@connection_event_handlers[event].each { |h|
|
195
|
-
h.call self
|
196
|
-
}
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# Internal helper, subscribe to messages using the amqp queue
|
201
|
-
def subscribe(*args, &bl)
|
202
|
-
return if @listening
|
203
|
-
@amqp_lock.synchronize {
|
204
|
-
@listening = true
|
205
|
-
@queue.subscribe do |metadata, msg|
|
206
|
-
bl.call metadata, msg
|
207
|
-
end
|
208
|
-
}
|
209
|
-
nil
|
210
|
-
end
|
211
|
-
|
212
|
-
|
213
|
-
public
|
214
|
-
|
215
|
-
# AMQPNode initializer
|
216
|
-
# @param [Hash] args the options to create the amqp node with
|
217
|
-
# @option args [String] :broker the amqp message broker which to connect to
|
218
|
-
def initialize(args = {})
|
219
|
-
super(args)
|
220
|
-
@broker = args[:broker]
|
221
|
-
@connection_event_handlers = {:closed => [], :error => []}
|
222
|
-
@response_lock = Mutex.new
|
223
|
-
@response_cv = ConditionVariable.new
|
224
|
-
@responses = []
|
225
|
-
@amqp_lock = Mutex.new
|
226
|
-
end
|
227
|
-
|
228
|
-
# Publish a message using the amqp exchange (*do* *not* *use*).
|
229
|
-
#
|
230
|
-
# XXX hack should be private, declared publically so as to be able to be used by {RJR::AMQPNodeCallback}
|
231
|
-
def publish(*args, &on_publish)
|
232
|
-
@amqp_lock.synchronize {
|
233
|
-
#raise RJR::Errors::ConnectionError.new("client unreachable") if @disconnected
|
234
|
-
@exchange.publish *args do |*cargs|
|
235
|
-
on_publish.call
|
236
|
-
end
|
237
|
-
}
|
238
|
-
nil
|
239
|
-
end
|
240
|
-
|
241
|
-
# Register connection event handler
|
242
|
-
# @param [:error, :close] event the event to register the handler for
|
243
|
-
# @param [Callable] handler block param to be added to array of handlers that are called when event occurs
|
244
|
-
# @yield [AMQPNode] self is passed to each registered handler when event occurs
|
245
|
-
def on(event, &handler)
|
246
|
-
if @connection_event_handlers.keys.include?(event)
|
247
|
-
@connection_event_handlers[event] << handler
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
# Instruct Node to start listening for and dispatching rpc requests.
|
252
|
-
#
|
253
|
-
# Implementation of {RJR::Node#listen}
|
254
|
-
def listen
|
255
|
-
em_run do
|
256
|
-
init_node {
|
257
|
-
# start receiving messages
|
258
|
-
subscribe { |metadata, msg|
|
259
|
-
handle_message(metadata, msg)
|
260
|
-
}
|
261
|
-
}
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
# Instructs node to send rpc request, and wait for and return response.
|
266
|
-
#
|
267
|
-
# Do not invoke directly from em event loop or callback as will block the message
|
268
|
-
# subscription used to receive responses
|
269
|
-
#
|
270
|
-
# @param [String] routing_key destination queue to send request to
|
271
|
-
# @param [String] rpc_method json-rpc method to invoke on destination
|
272
|
-
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
273
|
-
# @return [Object] the json result retrieved from destination converted to a ruby object
|
274
|
-
# @raise [Exception] if the destination raises an exception, it will be converted to json and re-raised here
|
275
|
-
def invoke_request(routing_key, rpc_method, *args)
|
276
|
-
message = RequestMessage.new :method => rpc_method,
|
277
|
-
:args => args,
|
278
|
-
:headers => @message_headers
|
279
|
-
em_run do
|
280
|
-
init_node {
|
281
|
-
# begin listening for result
|
282
|
-
subscribe { |metadata, msg|
|
283
|
-
handle_message(metadata, msg)
|
284
|
-
}
|
285
|
-
|
286
|
-
publish(message.to_s, :routing_key => routing_key, :reply_to => @queue_name) { }
|
287
|
-
}
|
288
|
-
end
|
289
|
-
|
290
|
-
# TODO optional timeout for response
|
291
|
-
result = wait_for_result(message)
|
292
|
-
|
293
|
-
if result.size > 2
|
294
|
-
raise Exception, result[2]
|
295
|
-
end
|
296
|
-
return result[1]
|
297
|
-
end
|
298
|
-
|
299
|
-
# FIXME add method to instruct node to send rpc request, and immediately
|
300
|
-
# return / ignoring response & also add method to collect response
|
301
|
-
# at a later time
|
302
|
-
|
303
|
-
# Instructs node to send rpc notification (immadiately returns / no response is generated)
|
304
|
-
#
|
305
|
-
# @param [String] routing_key destination queue to send request to
|
306
|
-
# @param [String] rpc_method json-rpc method to invoke on destination
|
307
|
-
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
308
|
-
def send_notification(routing_key, rpc_method, *args)
|
309
|
-
# will block until message is published
|
310
|
-
published_l = Mutex.new
|
311
|
-
published_c = ConditionVariable.new
|
312
|
-
|
313
|
-
invoked = false
|
314
|
-
message = NotificationMessage.new :method => rpc_method,
|
315
|
-
:args => args,
|
316
|
-
:headers => @message_headers
|
317
|
-
em_run do
|
318
|
-
init_node {
|
319
|
-
publish(message.to_s, :routing_key => routing_key, :reply_to => @queue_name){
|
320
|
-
published_l.synchronize { invoked = true ; published_c.signal }
|
321
|
-
}
|
322
|
-
}
|
323
|
-
end
|
324
|
-
published_l.synchronize { published_c.wait published_l unless invoked }
|
325
|
-
nil
|
326
|
-
end
|
327
|
-
|
328
|
-
end
|
329
|
-
end
|
330
|
-
end
|
data/lib/rjr/inspect.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
# JSON-RPC method definitions providing access to inspect the internal
|
2
|
-
# node state.
|
3
|
-
#
|
4
|
-
# Note this isn't included in the top level rjr module by default,
|
5
|
-
# manually include this module to incorporate these additional rjr method
|
6
|
-
# definitions into your node
|
7
|
-
#
|
8
|
-
# Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
|
9
|
-
# Licensed under the Apache License, Version 2.0
|
10
|
-
|
11
|
-
require 'rjr/util'
|
12
|
-
include RJR::Definitions
|
13
|
-
|
14
|
-
# Helper method to process user params / select stats
|
15
|
-
def select_stats(*filter)
|
16
|
-
lf = []
|
17
|
-
while q = filter.shift
|
18
|
-
lf <<
|
19
|
-
case q
|
20
|
-
when 'on_node' then
|
21
|
-
n = filter.shift
|
22
|
-
lambda { |ds| ds.request.rjr_node_type.to_s == n}
|
23
|
-
|
24
|
-
when "for_method" then
|
25
|
-
m = filter.shift
|
26
|
-
lambda { |ds| ds.request.rjr_method == m}
|
27
|
-
|
28
|
-
when 'successful' then
|
29
|
-
lambda { |ds| ds.result.success }
|
30
|
-
|
31
|
-
when 'failed' then
|
32
|
-
lambda { |ds| ds.result.failed }
|
33
|
-
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
RJR::DispatcherStat.stats.select { |ds| lf.all? { |lf| lf.call(ds) } }
|
38
|
-
end
|
39
|
-
|
40
|
-
rjr_method \
|
41
|
-
"rjr::dispatches" =>
|
42
|
-
# Retrieve all the dispatches this node served matching the specified criteri
|
43
|
-
lambda { |*filter| select_stats(*filter) },
|
44
|
-
|
45
|
-
"rjr::num_dispatches" =>
|
46
|
-
# Retrieve the number of dispatches this node served matching the specified criteria
|
47
|
-
lambda { |*filter| select_stats(*filter).size },
|
48
|
-
|
49
|
-
"rjr::status" =>
|
50
|
-
# Retrieve the overall status of this node
|
51
|
-
lambda {
|
52
|
-
{
|
53
|
-
# event machine
|
54
|
-
:event_machine => { :running => EMAdapter.running?,
|
55
|
-
:thread_status => EMAdapter.reactor_thread.status,
|
56
|
-
:connections => EventMachine.connection_count },
|
57
|
-
|
58
|
-
# thread pool
|
59
|
-
:thread_pool => { :running => ThreadPool2Manager.thread_pool.running?,
|
60
|
-
:inspect => ThreadPool2Manager.thread_pool.inspect },
|
61
|
-
}
|
62
|
-
}
|
63
|
-
|
64
|
-
#:log =>
|
65
|
-
# lambda {},
|
data/lib/rjr/local_node.rb
DELETED
@@ -1,150 +0,0 @@
|
|
1
|
-
# RJR Local Endpoint
|
2
|
-
#
|
3
|
-
# Implements the RJR::Node interface to satisty JSON-RPC requests via local method calls
|
4
|
-
#
|
5
|
-
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
6
|
-
# Licensed under the Apache License, Version 2.0
|
7
|
-
|
8
|
-
require 'rjr/node'
|
9
|
-
require 'rjr/message'
|
10
|
-
require 'rjr/dispatcher'
|
11
|
-
require 'rjr/errors'
|
12
|
-
|
13
|
-
module RJR
|
14
|
-
|
15
|
-
# Local node callback interface, used to invoke local json-rpc method handlers
|
16
|
-
class LocalNodeCallback
|
17
|
-
# LocalNodeCallback initializer
|
18
|
-
# @param [Hash] args the options to create the local node callback with
|
19
|
-
# @option args [LocalNode] :node local node used to send/receive messages
|
20
|
-
def initialize(args = {})
|
21
|
-
@node = args[:node]
|
22
|
-
end
|
23
|
-
|
24
|
-
# Implementation of {RJR::NodeCallback#invoke}
|
25
|
-
def invoke(callback_method, *data)
|
26
|
-
# TODO any exceptions from handler will propagate here, surround w/ begin/rescue block
|
27
|
-
# TODO support local_node 'disconnections' via RJR::ConnectionError & triggering mechanism
|
28
|
-
ThreadPool2Manager <<
|
29
|
-
ThreadPool2Job.new(callback_method, data) { |m|
|
30
|
-
@node.local_dispatch(callback_method, *data)
|
31
|
-
}
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Local node definition, implements the {RJR::Node} interface to
|
36
|
-
# listen for and invoke json-rpc requests via local handlers
|
37
|
-
#
|
38
|
-
# This is useful for situations in which you would like to invoke registered
|
39
|
-
# json-rpc handlers locally, enforcing the same constraints as
|
40
|
-
# you would on a json-rpc request coming in remotely.
|
41
|
-
#
|
42
|
-
# @example Listening for and dispatching json-rpc requests locally
|
43
|
-
# RJR::Dispatcher.add_handler('hello') { |name|
|
44
|
-
# @rjr_node_type == :local ? "Hello superuser #{name}" : "Hello #{name}!"
|
45
|
-
# }
|
46
|
-
#
|
47
|
-
# # initialize node and invoke request
|
48
|
-
# node = RJR::LocalNode.new :node_id => 'node'
|
49
|
-
# node.invoke_request('hello', 'mo')
|
50
|
-
#
|
51
|
-
class LocalNode < RJR::Node
|
52
|
-
RJR_NODE_TYPE = :local
|
53
|
-
|
54
|
-
# allows clients to override the node type for the local node
|
55
|
-
attr_accessor :node_type
|
56
|
-
|
57
|
-
# Helper method to locally dispatch method/args
|
58
|
-
#
|
59
|
-
# TODO would like to make private but needed in LocalNodeCallback
|
60
|
-
def local_dispatch(rpc_method, *args)
|
61
|
-
# create request from args
|
62
|
-
0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
|
63
|
-
message = RequestMessage.new :method => rpc_method,
|
64
|
-
:args => args,
|
65
|
-
:headers => @message_headers
|
66
|
-
|
67
|
-
# here we serialize / unserialze messages to/from json to
|
68
|
-
# ensure local node complies to same json-rpc restrictions as other nodes
|
69
|
-
message = RequestMessage.new :message => message.to_s,
|
70
|
-
:headers => @message_headers
|
71
|
-
|
72
|
-
result = Dispatcher.dispatch_request(message.jr_method,
|
73
|
-
:method_args => message.jr_args,
|
74
|
-
:headers => @message_headers,
|
75
|
-
:rjr_node => self,
|
76
|
-
:rjr_node_id => @node_id,
|
77
|
-
:rjr_node_type => @node_type,
|
78
|
-
:rjr_callback =>
|
79
|
-
LocalNodeCallback.new(:node => self,
|
80
|
-
:headers => @message_headers))
|
81
|
-
|
82
|
-
# create response message from result
|
83
|
-
response = ResponseMessage.new(:id => message.msg_id,
|
84
|
-
:result => result,
|
85
|
-
:headers => @message_headers)
|
86
|
-
|
87
|
-
# same comment on serialization/unserialization as above
|
88
|
-
response = ResponseMessage.new(:message => response.to_s,
|
89
|
-
:headers => @message_headers)
|
90
|
-
|
91
|
-
response
|
92
|
-
end
|
93
|
-
|
94
|
-
# LocalNode initializer
|
95
|
-
# @param [Hash] args the options to create the local node with
|
96
|
-
def initialize(args = {})
|
97
|
-
super(args)
|
98
|
-
@node_type = RJR_NODE_TYPE
|
99
|
-
end
|
100
|
-
|
101
|
-
# register connection event handler,
|
102
|
-
# *note* Until we support manual disconnections of the local node, we don't have to do anything here
|
103
|
-
#
|
104
|
-
# @param [:error, :close] event the event to register the handler for
|
105
|
-
# @param [Callable] handler block param to be added to array of handlers that are called when event occurs
|
106
|
-
# @yield [LocalNode] self is passed to each registered handler when event occurs
|
107
|
-
def on(event, &handler)
|
108
|
-
# TODO raise error (for the time being)?
|
109
|
-
end
|
110
|
-
|
111
|
-
# Instruct Node to start listening for and dispatching rpc requests
|
112
|
-
#
|
113
|
-
# Currently does nothing as method handlers can be invoked directly upon invoke_request
|
114
|
-
def listen
|
115
|
-
end
|
116
|
-
|
117
|
-
# Instructs node to send rpc request, and wait for and return response
|
118
|
-
#
|
119
|
-
# If strictly confirming to other nodes, use event machine to launch
|
120
|
-
# a thread pool job to dispatch request and block on result.
|
121
|
-
# Optimized for performance reasons but recognize the semantics of using
|
122
|
-
# the local node will be somewhat different.
|
123
|
-
#
|
124
|
-
# @param [String] rpc_method json-rpc method to invoke on destination
|
125
|
-
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
126
|
-
# @return [Object] the json result retrieved from destination converted to a ruby object
|
127
|
-
# @raise [Exception] if the destination raises an exception, it will be converted to json and re-raised here
|
128
|
-
def invoke_request(rpc_method, *args)
|
129
|
-
res = local_dispatch(rpc_method, *args)
|
130
|
-
return Dispatcher.handle_response(res.result)
|
131
|
-
end
|
132
|
-
|
133
|
-
# Instructs node to send rpc notification (immediately returns / no response is generated)
|
134
|
-
#
|
135
|
-
# Same performance comment as invoke_request above
|
136
|
-
#
|
137
|
-
# @param [String] rpc_method json-rpc method to invoke on destination
|
138
|
-
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
139
|
-
def send_notification(rpc_method, *args)
|
140
|
-
# TODO run in thread & immediately return?
|
141
|
-
begin
|
142
|
-
local_dispatch(rpc_method, *args)
|
143
|
-
rescue
|
144
|
-
end
|
145
|
-
nil
|
146
|
-
end
|
147
|
-
|
148
|
-
|
149
|
-
end
|
150
|
-
end
|