wamp-worker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +204 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/wamp-worker +46 -0
- data/lib/wamp/worker.rb +132 -0
- data/lib/wamp/worker/config.rb +184 -0
- data/lib/wamp/worker/handler.rb +196 -0
- data/lib/wamp/worker/proxy/backgrounder.rb +38 -0
- data/lib/wamp/worker/proxy/base.rb +101 -0
- data/lib/wamp/worker/proxy/dispatcher.rb +115 -0
- data/lib/wamp/worker/proxy/requestor.rb +91 -0
- data/lib/wamp/worker/queue.rb +135 -0
- data/lib/wamp/worker/rails.rb +28 -0
- data/lib/wamp/worker/runner.rb +240 -0
- data/lib/wamp/worker/ticker.rb +30 -0
- data/lib/wamp/worker/version.rb +5 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/client_stub.rb +47 -0
- data/spec/support/handler_stub.rb +105 -0
- data/spec/support/redis_stub.rb +89 -0
- data/spec/support/session_stub.rb +101 -0
- data/spec/wamp/worker/config_spec.rb +90 -0
- data/spec/wamp/worker/handler_spec.rb +162 -0
- data/spec/wamp/worker/proxy_spec.rb +153 -0
- data/spec/wamp/worker/queue_spec.rb +49 -0
- data/spec/wamp/worker/runner_spec.rb +108 -0
- data/spec/wamp/worker_spec.rb +8 -0
- data/test/app_test.rb +124 -0
- data/test/hello.py +50 -0
- data/test/sidekiq.yml +5 -0
- data/test/wamp_test/.generators +8 -0
- data/test/wamp_test/.ruby-version +1 -0
- data/test/wamp_test/Gemfile +65 -0
- data/test/wamp_test/Gemfile.lock +246 -0
- data/test/wamp_test/README.md +24 -0
- data/test/wamp_test/Rakefile +6 -0
- data/test/wamp_test/app/assets/config/manifest.js +3 -0
- data/test/wamp_test/app/assets/images/.keep +0 -0
- data/test/wamp_test/app/assets/javascripts/application.js +16 -0
- data/test/wamp_test/app/assets/javascripts/cable.js +13 -0
- data/test/wamp_test/app/assets/javascripts/channels/.keep +0 -0
- data/test/wamp_test/app/assets/stylesheets/application.css +15 -0
- data/test/wamp_test/app/channels/application_cable/channel.rb +4 -0
- data/test/wamp_test/app/channels/application_cable/connection.rb +4 -0
- data/test/wamp_test/app/controllers/add_controller.rb +11 -0
- data/test/wamp_test/app/controllers/application_controller.rb +3 -0
- data/test/wamp_test/app/controllers/concerns/.keep +0 -0
- data/test/wamp_test/app/controllers/ping_controller.rb +7 -0
- data/test/wamp_test/app/handlers/add_handler.rb +9 -0
- data/test/wamp_test/app/handlers/back_add_handler.rb +26 -0
- data/test/wamp_test/app/handlers/back_ping_handler.rb +10 -0
- data/test/wamp_test/app/handlers/ping_handler.rb +10 -0
- data/test/wamp_test/app/helpers/application_helper.rb +2 -0
- data/test/wamp_test/app/jobs/application_job.rb +2 -0
- data/test/wamp_test/app/mailers/application_mailer.rb +4 -0
- data/test/wamp_test/app/models/application_record.rb +3 -0
- data/test/wamp_test/app/models/concerns/.keep +0 -0
- data/test/wamp_test/app/views/layouts/application.html.erb +15 -0
- data/test/wamp_test/app/views/layouts/mailer.html.erb +13 -0
- data/test/wamp_test/app/views/layouts/mailer.text.erb +1 -0
- data/test/wamp_test/bin/bundle +3 -0
- data/test/wamp_test/bin/rails +9 -0
- data/test/wamp_test/bin/rake +9 -0
- data/test/wamp_test/bin/setup +36 -0
- data/test/wamp_test/bin/spring +17 -0
- data/test/wamp_test/bin/update +31 -0
- data/test/wamp_test/bin/yarn +11 -0
- data/test/wamp_test/config.ru +5 -0
- data/test/wamp_test/config/application.rb +19 -0
- data/test/wamp_test/config/boot.rb +4 -0
- data/test/wamp_test/config/cable.yml +10 -0
- data/test/wamp_test/config/credentials.yml.enc +1 -0
- data/test/wamp_test/config/database.yml +25 -0
- data/test/wamp_test/config/environment.rb +5 -0
- data/test/wamp_test/config/environments/development.rb +61 -0
- data/test/wamp_test/config/environments/production.rb +94 -0
- data/test/wamp_test/config/environments/test.rb +46 -0
- data/test/wamp_test/config/initializers/application_controller_renderer.rb +8 -0
- data/test/wamp_test/config/initializers/assets.rb +14 -0
- data/test/wamp_test/config/initializers/backtrace_silencers.rb +7 -0
- data/test/wamp_test/config/initializers/content_security_policy.rb +25 -0
- data/test/wamp_test/config/initializers/cookies_serializer.rb +5 -0
- data/test/wamp_test/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/wamp_test/config/initializers/inflections.rb +16 -0
- data/test/wamp_test/config/initializers/mime_types.rb +4 -0
- data/test/wamp_test/config/initializers/wamp-worker.rb +8 -0
- data/test/wamp_test/config/initializers/wrap_parameters.rb +14 -0
- data/test/wamp_test/config/locales/en.yml +33 -0
- data/test/wamp_test/config/master.key +1 -0
- data/test/wamp_test/config/puma.rb +34 -0
- data/test/wamp_test/config/routes.rb +4 -0
- data/test/wamp_test/config/sidekiq.yml +6 -0
- data/test/wamp_test/config/spring.rb +6 -0
- data/test/wamp_test/config/storage.yml +34 -0
- data/test/wamp_test/db/development.sqlite3 +0 -0
- data/test/wamp_test/db/seeds.rb +7 -0
- data/test/wamp_test/lib/assets/.keep +0 -0
- data/test/wamp_test/lib/tasks/.keep +0 -0
- data/test/wamp_test/package.json +5 -0
- data/test/wamp_test/public/404.html +67 -0
- data/test/wamp_test/public/422.html +67 -0
- data/test/wamp_test/public/500.html +66 -0
- data/test/wamp_test/public/apple-touch-icon-precomposed.png +0 -0
- data/test/wamp_test/public/apple-touch-icon.png +0 -0
- data/test/wamp_test/public/favicon.ico +0 -0
- data/test/wamp_test/public/robots.txt +1 -0
- data/test/wamp_test/storage/.keep +0 -0
- data/test/wamp_test/test/application_system_test_case.rb +5 -0
- data/test/wamp_test/test/controllers/.keep +0 -0
- data/test/wamp_test/test/fixtures/.keep +0 -0
- data/test/wamp_test/test/fixtures/files/.keep +0 -0
- data/test/wamp_test/test/helpers/.keep +0 -0
- data/test/wamp_test/test/integration/.keep +0 -0
- data/test/wamp_test/test/mailers/.keep +0 -0
- data/test/wamp_test/test/models/.keep +0 -0
- data/test/wamp_test/test/system/.keep +0 -0
- data/test/wamp_test/test/test_helper.rb +10 -0
- data/test/wamp_test/vendor/.keep +0 -0
- data/test/web/index.html +101 -0
- data/wamp-worker.gemspec +32 -0
- metadata +395 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
module Wamp
|
4
|
+
module Worker
|
5
|
+
module Proxy
|
6
|
+
|
7
|
+
class Dispatcher < Base
|
8
|
+
attr_accessor :session
|
9
|
+
|
10
|
+
# We want to timeout every few seconds so higher level code can
|
11
|
+
# look for a shutdown
|
12
|
+
TIMEOUT = 2
|
13
|
+
|
14
|
+
# Constructor
|
15
|
+
#
|
16
|
+
def initialize(name, session=nil, uuid: nil)
|
17
|
+
super name, uuid: uuid
|
18
|
+
self.session = session
|
19
|
+
end
|
20
|
+
|
21
|
+
# Increments the ticker
|
22
|
+
#
|
23
|
+
def increment_ticker
|
24
|
+
self.ticker.increment
|
25
|
+
end
|
26
|
+
|
27
|
+
# Check the queues
|
28
|
+
#
|
29
|
+
def check_queues
|
30
|
+
check_queue [self.command_req_queue, self.background_res_queue]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Executes the request
|
34
|
+
#
|
35
|
+
# @param request [Descriptor] - The request
|
36
|
+
def process(descriptor)
|
37
|
+
return unless descriptor != nil
|
38
|
+
|
39
|
+
raise(RuntimeError, "must have a session to process a descriptor") unless self.session != nil
|
40
|
+
|
41
|
+
# Create the callback
|
42
|
+
callback = -> result, error, details {
|
43
|
+
# Need to remove the session from the details response
|
44
|
+
details&.delete(:session)
|
45
|
+
|
46
|
+
# Create the params
|
47
|
+
params = { result: result, error: error, details: details }
|
48
|
+
|
49
|
+
# Push the response back
|
50
|
+
self.queue.push descriptor.handle, descriptor.command, params
|
51
|
+
}
|
52
|
+
|
53
|
+
# Call the session
|
54
|
+
if descriptor.command == :call
|
55
|
+
|
56
|
+
# invoke the call method
|
57
|
+
procedure = descriptor.params[:procedure]
|
58
|
+
args = descriptor.params[:args]
|
59
|
+
kwargs = descriptor.params[:kwargs]
|
60
|
+
options = descriptor.params[:options]
|
61
|
+
|
62
|
+
self.session.call(procedure, args, kwargs, options, &callback)
|
63
|
+
|
64
|
+
elsif descriptor.command == :publish
|
65
|
+
|
66
|
+
# invoke the publish method
|
67
|
+
topic = descriptor.params[:topic]
|
68
|
+
args = descriptor.params[:args]
|
69
|
+
kwargs = descriptor.params[:kwargs]
|
70
|
+
options = descriptor.params[:options]
|
71
|
+
|
72
|
+
self.session.publish(topic, args, kwargs, options, &callback)
|
73
|
+
|
74
|
+
elsif descriptor.command == :yield
|
75
|
+
|
76
|
+
# invoke the yield method
|
77
|
+
request = descriptor.params[:request]
|
78
|
+
options = descriptor.params[:options]
|
79
|
+
check_defer = descriptor.params[:check_defer]
|
80
|
+
result_hash = descriptor.params[:result] || {}
|
81
|
+
|
82
|
+
# Get the response from the descriptor params
|
83
|
+
result = Wamp::Client::Response.from_hash(result_hash)
|
84
|
+
|
85
|
+
self.session.yield(request, result, options, check_defer)
|
86
|
+
|
87
|
+
else
|
88
|
+
|
89
|
+
# Return error if the command is not supported
|
90
|
+
error = Wamp::Client::Response::CallError.new(
|
91
|
+
Wamp::Client::Response::DEFAULT_ERROR,
|
92
|
+
["unsupported proxy command '#{descriptor.command}'"])
|
93
|
+
callback.call(nil, error.to_hash, {})
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# This methods blocks waiting for a value to appear in the queue
|
102
|
+
#
|
103
|
+
# @param queue_name [String] - the name of the queue
|
104
|
+
def check_queue(queue_name)
|
105
|
+
|
106
|
+
# Wait for a value to appear in the queue. We have a timeout so
|
107
|
+
# the thread can check if the worker has been killed
|
108
|
+
self.queue.pop(queue_name, wait: true, timeout: TIMEOUT)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
module Wamp
|
4
|
+
module Worker
|
5
|
+
module Proxy
|
6
|
+
|
7
|
+
class Requestor < Base
|
8
|
+
|
9
|
+
# Performs the session "call" method
|
10
|
+
#
|
11
|
+
# @param procedure [String] - The procedure to call
|
12
|
+
# @param args [Array] - Array of arguments
|
13
|
+
# @param kwargs [Hash] - Hash of key/word arguments
|
14
|
+
# @param options [Hash] - Options for the call
|
15
|
+
def call(procedure, args=nil, kwargs=nil, options={}, &callback)
|
16
|
+
|
17
|
+
# Create the params
|
18
|
+
params = { procedure: procedure, args: args, kwargs: kwargs, options: options }
|
19
|
+
|
20
|
+
# Execute the command
|
21
|
+
request_response :call, params, true, &callback
|
22
|
+
end
|
23
|
+
|
24
|
+
# Performs the session "publish" method
|
25
|
+
#
|
26
|
+
# @param topic [String] - The topic to publish
|
27
|
+
# @param args [Array] - Array of arguments
|
28
|
+
# @param kwargs [Hash] - Hash of key/word arguments
|
29
|
+
# @param options [Hash] - Options for the subscribe
|
30
|
+
def publish(topic, args=nil, kwargs=nil, options={}, &callback)
|
31
|
+
|
32
|
+
# Create the params
|
33
|
+
params = { topic: topic , args: args, kwargs: kwargs, options: options }
|
34
|
+
|
35
|
+
# Execute the command
|
36
|
+
request_response :publish, params, options[:acknowledge], &callback
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Method to push the request and wait for the response
|
42
|
+
#
|
43
|
+
# @param command [Symbol] - The command
|
44
|
+
# @param params [Hash] - The parameters
|
45
|
+
# @param wait [Bool] - if true, will wait for the response
|
46
|
+
def request_response(command, params, wait=true)
|
47
|
+
|
48
|
+
# Create a response handle
|
49
|
+
handle = self.unique_command_resp_queue
|
50
|
+
|
51
|
+
# Push the request
|
52
|
+
self.queue.push self.command_req_queue, command, params, handle
|
53
|
+
|
54
|
+
# If wait, check the queue and respond
|
55
|
+
if wait
|
56
|
+
|
57
|
+
# Store the start ticker
|
58
|
+
start_tick = self.ticker.get
|
59
|
+
|
60
|
+
# Wait for the response
|
61
|
+
descriptor = self.queue.pop(handle, wait: true, delete: true)
|
62
|
+
|
63
|
+
# check for nil descriptor
|
64
|
+
if descriptor == nil
|
65
|
+
|
66
|
+
# If the ticker never incremented, throw a "worker not responding" error
|
67
|
+
current_tick = self.ticker.get
|
68
|
+
if start_tick == current_tick
|
69
|
+
raise(RuntimeError, "worker '#{self.name}' is not responding")
|
70
|
+
else
|
71
|
+
raise(RuntimeError, "request to #{handle} timed out")
|
72
|
+
end
|
73
|
+
|
74
|
+
else
|
75
|
+
|
76
|
+
# If a block was given, respond
|
77
|
+
if block_given?
|
78
|
+
response = [descriptor.params[:result], descriptor.params[:error], descriptor.params[:details]]
|
79
|
+
yield(*response)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Wamp
|
4
|
+
module Worker
|
5
|
+
|
6
|
+
class Queue
|
7
|
+
attr_reader :redis, :default_timeout
|
8
|
+
|
9
|
+
# This class represents the payload that will be stored in Redis
|
10
|
+
class Descriptor
|
11
|
+
attr_reader :command, :handle, :params
|
12
|
+
|
13
|
+
# Constructor
|
14
|
+
#
|
15
|
+
# @param command [Symbol] - The command for the descriptor
|
16
|
+
# @param handle [String] - The handle representing the descriptor
|
17
|
+
# @param params [Hash] - The params for the command
|
18
|
+
def initialize(command, handle, params)
|
19
|
+
@command = command.to_sym
|
20
|
+
@handle = handle
|
21
|
+
@params = params || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create a Descriptor object from the json payload
|
25
|
+
#
|
26
|
+
# @param json_string [String] - The string from the Redis store
|
27
|
+
# @return [Descriptor] - The instantiated descriptor
|
28
|
+
def self.from_json(json_string)
|
29
|
+
return unless json_string
|
30
|
+
parsed = JSON.parse(json_string, :symbolize_names => true)
|
31
|
+
self.new(parsed[:command], parsed[:handle], parsed[:params])
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates the json payload from the object
|
35
|
+
#
|
36
|
+
# @return [String] - The string that will go into the Redis store
|
37
|
+
def to_json
|
38
|
+
{ command: self.command, handle: self.handle, params: self.params }.to_json
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Constructor
|
43
|
+
#
|
44
|
+
def initialize(name)
|
45
|
+
@redis = Wamp::Worker.config.redis(name)
|
46
|
+
@default_timeout = Wamp::Worker.config.timeout(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Pushes a command onto the queue
|
50
|
+
#
|
51
|
+
# @param queue_name [String] - The name of the queue
|
52
|
+
# @param command [Symbol] - The command
|
53
|
+
# @param params [Hash] - The params for the request
|
54
|
+
# @param handle [String] - The response handle
|
55
|
+
def push(queue_name, command, params, handle=nil)
|
56
|
+
|
57
|
+
# Create the descriptor
|
58
|
+
descriptor = Descriptor.new(command, handle, params)
|
59
|
+
|
60
|
+
# Log the info
|
61
|
+
log(:push, queue_name, descriptor)
|
62
|
+
|
63
|
+
# Queue the command
|
64
|
+
self.redis.lpush(queue_name, descriptor.to_json)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Pops a command off of the queue
|
68
|
+
#
|
69
|
+
# @param queue_name [String, Array] - The name of the queue (or multiple queues if brpop)
|
70
|
+
# @param wait [Bool] - True if we want to block waiting for the response
|
71
|
+
# @param delete [Bool] - True if we want the queue deleted (only applicable if wait)
|
72
|
+
# @param timeout [Int] - Number of seconds to wait before timing out
|
73
|
+
def pop(queue_name, wait: false, delete: false, timeout: nil)
|
74
|
+
|
75
|
+
# Retrieve the response from the queue
|
76
|
+
if wait
|
77
|
+
# Use the default timeout if non is specified
|
78
|
+
timeout ||= self.default_timeout
|
79
|
+
|
80
|
+
# Make the pop call
|
81
|
+
response = self.redis.brpop(queue_name, timeout: timeout)
|
82
|
+
|
83
|
+
# Returns [queue, value]
|
84
|
+
if response != nil
|
85
|
+
queue_name = response[0]
|
86
|
+
response = response[1]
|
87
|
+
end
|
88
|
+
else
|
89
|
+
# Else just call the method
|
90
|
+
response = self.redis.rpop(queue_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
# If delete was set, delete the queue
|
94
|
+
if delete
|
95
|
+
self.redis.del(queue_name)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Parse the response
|
99
|
+
descriptor = response != nil ? Descriptor.from_json(response) : nil
|
100
|
+
|
101
|
+
# Log the info
|
102
|
+
log(:pop, queue_name, descriptor)
|
103
|
+
|
104
|
+
# Return the queue_name and the descriptor
|
105
|
+
descriptor
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# Returns the logger
|
111
|
+
#
|
112
|
+
def logger
|
113
|
+
Wamp::Worker.logger
|
114
|
+
end
|
115
|
+
|
116
|
+
# Logs the info
|
117
|
+
#
|
118
|
+
def log(type, queue_name, descriptor)
|
119
|
+
return unless logger.level == Logger::DEBUG
|
120
|
+
|
121
|
+
if descriptor
|
122
|
+
logger.debug("#{self.class.name} #{type.upcase} : #{queue_name}")
|
123
|
+
logger.debug(" command: #{descriptor.command}")
|
124
|
+
logger.debug(" params: #{descriptor.params}")
|
125
|
+
logger.debug(" handle: #{descriptor.handle}")
|
126
|
+
else
|
127
|
+
logger.debug("#{self.class.name} #{type.upcase} : #{queue_name} : EMPTY")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Wamp
|
2
|
+
module Worker
|
3
|
+
module Rails
|
4
|
+
|
5
|
+
# This method will load Rails
|
6
|
+
#
|
7
|
+
# @param environment [String] - The Rails environment
|
8
|
+
# @param require [String] - The path to the Rails working directory or a file with requires
|
9
|
+
def self.load_app(environment, require)
|
10
|
+
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = environment
|
11
|
+
|
12
|
+
raise ArgumentError, "'#{require}' does not exist" unless File.exist?(require)
|
13
|
+
|
14
|
+
if File.directory?(require)
|
15
|
+
require 'rails'
|
16
|
+
if ::Rails::VERSION::MAJOR < 5
|
17
|
+
raise "only Rails version 5 and higher supported"
|
18
|
+
else
|
19
|
+
require File.expand_path("#{require}/config/environment.rb")
|
20
|
+
end
|
21
|
+
else
|
22
|
+
require(require) || raise(ArgumentError, "no require file found at '#{require}'")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require "thread"
|
2
|
+
require "wamp/client/connection"
|
3
|
+
|
4
|
+
module Wamp
|
5
|
+
module Worker
|
6
|
+
module Runner
|
7
|
+
|
8
|
+
# This is a base class for all of the runners
|
9
|
+
class Base
|
10
|
+
attr_reader :name, :dispatcher
|
11
|
+
|
12
|
+
# Constructor
|
13
|
+
#
|
14
|
+
# @param name [Symbol] - the name of the worker
|
15
|
+
def initialize(name, uuid: nil)
|
16
|
+
# Initialize the dispatcher
|
17
|
+
@name = name || :default
|
18
|
+
@dispatcher = Proxy::Dispatcher.new(self.name, uuid: uuid)
|
19
|
+
@active = false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the logger
|
23
|
+
#
|
24
|
+
def logger
|
25
|
+
Wamp::Worker.logger
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns if the runner is active
|
29
|
+
#
|
30
|
+
def active?
|
31
|
+
@active
|
32
|
+
end
|
33
|
+
|
34
|
+
# Starts the runner
|
35
|
+
#
|
36
|
+
def start
|
37
|
+
return if self.active?
|
38
|
+
@active = true
|
39
|
+
self._start
|
40
|
+
end
|
41
|
+
|
42
|
+
# Stops the runner
|
43
|
+
#
|
44
|
+
def stop
|
45
|
+
return unless self.active?
|
46
|
+
self._stop
|
47
|
+
@active = false
|
48
|
+
end
|
49
|
+
|
50
|
+
#region Override Methods
|
51
|
+
def _start
|
52
|
+
end
|
53
|
+
|
54
|
+
def _stop
|
55
|
+
end
|
56
|
+
#endregion
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
# This class monitors the queue and returns the descriptor
|
61
|
+
class Background < Base
|
62
|
+
attr_reader :callback, :thread
|
63
|
+
|
64
|
+
# Constructor
|
65
|
+
#
|
66
|
+
# @param name [Symbol] - the name of the worker
|
67
|
+
def initialize(name, uuid: nil, &callback)
|
68
|
+
super name, uuid: uuid
|
69
|
+
|
70
|
+
@callback = callback
|
71
|
+
|
72
|
+
# Log the event
|
73
|
+
logger.debug("#{self.class.name} '#{self.name}' created")
|
74
|
+
end
|
75
|
+
|
76
|
+
# Starts the background runner
|
77
|
+
#
|
78
|
+
def _start
|
79
|
+
# Start the background thread
|
80
|
+
Thread.new do
|
81
|
+
|
82
|
+
# The background thread will infinitely call the callback while the
|
83
|
+
# runner is active
|
84
|
+
while self.active?
|
85
|
+
begin
|
86
|
+
self.callback.call(self)
|
87
|
+
rescue => e
|
88
|
+
logger.error("#{self.class.name} #{e.class.name} - #{e.message}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# This class is the main runner
|
97
|
+
class Main < Base
|
98
|
+
attr_reader :challenge, :client, :descriptor_queue, :queue_monitor
|
99
|
+
|
100
|
+
# Constructor
|
101
|
+
#
|
102
|
+
def initialize(name=nil, **options)
|
103
|
+
super name
|
104
|
+
|
105
|
+
# Combine the options
|
106
|
+
options = Wamp::Worker.config.connection(self.name).merge options
|
107
|
+
|
108
|
+
# Setup different options
|
109
|
+
@challenge = options[:challenge]
|
110
|
+
@client = options[:client] || Wamp::Client::Connection.new(options)
|
111
|
+
@active = false
|
112
|
+
|
113
|
+
# Log the event
|
114
|
+
logger.info("#{self.class.name} '#{self.name}' created with options")
|
115
|
+
logger.info(" uri: #{options[:uri]}")
|
116
|
+
logger.info(" realm: #{options[:realm]}")
|
117
|
+
|
118
|
+
# Create a queue for passing messages to the main runner
|
119
|
+
@descriptor_queue = ::Queue.new
|
120
|
+
|
121
|
+
# Note: since the queue monitor is attached to the same worker,
|
122
|
+
# we need to lock the UUIDs together. This will make sure they
|
123
|
+
# delegate background tasks correctly
|
124
|
+
uuid = self.dispatcher.uuid
|
125
|
+
|
126
|
+
# Create a command queue monitor
|
127
|
+
@queue_monitor = Background.new(self.name, uuid: uuid) do |runner|
|
128
|
+
descriptor = runner.dispatcher.check_queues
|
129
|
+
self.descriptor_queue.push(descriptor) if descriptor
|
130
|
+
end
|
131
|
+
|
132
|
+
# Add the tick loop handler
|
133
|
+
self.client.transport_class.add_tick_loop { self.tick_handler }
|
134
|
+
|
135
|
+
# Initialize the last tick
|
136
|
+
@last_tick = Time.now.to_i
|
137
|
+
|
138
|
+
# Catch SIGINT
|
139
|
+
Signal.trap('INT') { self.stop }
|
140
|
+
Signal.trap('TERM') { self.stop }
|
141
|
+
end
|
142
|
+
|
143
|
+
# Starts the run loop
|
144
|
+
#
|
145
|
+
def _start
|
146
|
+
|
147
|
+
# On join, we need to subscribe and register the different handlers
|
148
|
+
self.client.on :join do |session, details|
|
149
|
+
self.join_handler session, details
|
150
|
+
end
|
151
|
+
|
152
|
+
# On leave, we will print a message
|
153
|
+
self.client.on :leave do |reason, details|
|
154
|
+
self.leave_handler(reason, details)
|
155
|
+
end
|
156
|
+
|
157
|
+
# On challenge, we will run the users challenge code
|
158
|
+
self.client.on :challenge do |authmethod, details|
|
159
|
+
self.challenge_handler(authmethod, details)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Start the monitors
|
163
|
+
self.queue_monitor.start
|
164
|
+
|
165
|
+
# Log info
|
166
|
+
logger.info("#{self.class.name} '#{self.name}' started")
|
167
|
+
|
168
|
+
# Start the connection
|
169
|
+
self.client.open
|
170
|
+
end
|
171
|
+
|
172
|
+
# Stops the run loop
|
173
|
+
#
|
174
|
+
def _stop
|
175
|
+
|
176
|
+
# Stop the other threads
|
177
|
+
self.queue_monitor.stop
|
178
|
+
|
179
|
+
# Stop the event machine
|
180
|
+
self.client.close
|
181
|
+
end
|
182
|
+
|
183
|
+
def join_handler(session, details)
|
184
|
+
logger.info("#{self.class.name} runner '#{self.name}' joined session with realm '#{details[:realm]}'")
|
185
|
+
|
186
|
+
# Set the session
|
187
|
+
self.dispatcher.session = session
|
188
|
+
|
189
|
+
# Register for the procedures
|
190
|
+
Wamp::Worker.register_procedures(self.name, self.dispatcher, session)
|
191
|
+
|
192
|
+
# Subscribe to the topics
|
193
|
+
Wamp::Worker.subscribe_topics(self.name, self.dispatcher, session)
|
194
|
+
end
|
195
|
+
|
196
|
+
def leave_handler(reason, details)
|
197
|
+
logger.info("#{self.class.name} runner '#{self.name}' left session: #{reason}")
|
198
|
+
|
199
|
+
# Clear the session
|
200
|
+
self.dispatcher.session = nil
|
201
|
+
end
|
202
|
+
|
203
|
+
def challenge_handler(authmethod, extra)
|
204
|
+
logger.info("#{self.class.name} runner '#{self.name}' challenge")
|
205
|
+
|
206
|
+
if self.challenge
|
207
|
+
self.challenge.call(authmethod, extra)
|
208
|
+
else
|
209
|
+
self.stop
|
210
|
+
raise(ArgumentError, "client asked for '#{authmethod}' challenge, but no ':challenge' option was provided")
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# This method periodically checks if any work has come in from the queues
|
215
|
+
#
|
216
|
+
def tick_handler
|
217
|
+
|
218
|
+
# This code will implement the ticker every second. This tells the
|
219
|
+
# requestors that the worker is alive
|
220
|
+
current_time = Time.now.to_i
|
221
|
+
if current_time > @last_tick
|
222
|
+
self.dispatcher.increment_ticker
|
223
|
+
@last_tick = current_time
|
224
|
+
end
|
225
|
+
|
226
|
+
# Loop until the queue is empty
|
227
|
+
until self.descriptor_queue.empty? do
|
228
|
+
|
229
|
+
# Pop the value and process it
|
230
|
+
descriptor = self.descriptor_queue.pop
|
231
|
+
self.dispatcher.process(descriptor)
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|