rjr 0.12.2 → 0.15.1
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/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
|