euston-daemons 1.0.0-java
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/Gemfile +4 -0
- data/Gemfile.lock +101 -0
- data/LICENSE +19 -0
- data/Rakefile +161 -0
- data/euston-daemons.gemspec +69 -0
- data/lib/euston-daemons.rb +15 -0
- data/lib/euston-daemons/command_processor_daemon/config/environment.rb +14 -0
- data/lib/euston-daemons/command_processor_daemon/lib/components/command_handler_component.rb +55 -0
- data/lib/euston-daemons/command_processor_daemon/lib/daemon.rb +43 -0
- data/lib/euston-daemons/command_processor_daemon/lib/settings.rb +19 -0
- data/lib/euston-daemons/command_processor_daemon/rake_task.rb +31 -0
- data/lib/euston-daemons/event_processor_daemon/config/environment.rb +20 -0
- data/lib/euston-daemons/event_processor_daemon/lib/components/event_handler_component.rb +72 -0
- data/lib/euston-daemons/event_processor_daemon/lib/daemon.rb +72 -0
- data/lib/euston-daemons/event_processor_daemon/lib/settings.rb +22 -0
- data/lib/euston-daemons/event_processor_daemon/rake_task.rb +37 -0
- data/lib/euston-daemons/framework/basic_component.rb +33 -0
- data/lib/euston-daemons/framework/component_shutdown.rb +22 -0
- data/lib/euston-daemons/framework/daemon.rb +27 -0
- data/lib/euston-daemons/framework/queue.rb +71 -0
- data/lib/euston-daemons/message_buffer_daemon/config/environment.rb +26 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/components/buffer_component.rb +74 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/components/event_store_component.rb +51 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/daemon.rb +48 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/message_logger.rb +54 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/publisher.rb +56 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/read_model/message_buffer.rb +54 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/read_model/message_log.rb +36 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/settings.rb +14 -0
- data/lib/euston-daemons/message_buffer_daemon/lib/subscriber.rb +60 -0
- data/lib/euston-daemons/message_buffer_daemon/rake_task.rb +30 -0
- data/lib/euston-daemons/rake_task.rb +116 -0
- data/lib/euston-daemons/version.rb +5 -0
- metadata +235 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module Euston
|
2
|
+
module CommandProcessorDaemon
|
3
|
+
class Daemon
|
4
|
+
include Euston::Daemon
|
5
|
+
|
6
|
+
attr_reader :clients,:queue #,:ctx
|
7
|
+
|
8
|
+
def initialize #(ctx)
|
9
|
+
@queue = Queue.new
|
10
|
+
# @ctx = ctx
|
11
|
+
@clients = {}
|
12
|
+
|
13
|
+
Settings.client_instances.times do |i|
|
14
|
+
@clients["command_component_#{i.succ}"] = CommandHandlerComponent.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
@clients.each do |name, component|
|
20
|
+
sleep 0.25
|
21
|
+
EUSTON_LOG.debug "Starting component: #{name}"
|
22
|
+
component.start
|
23
|
+
end
|
24
|
+
|
25
|
+
EUSTON_LOG.debug "Components started"
|
26
|
+
|
27
|
+
@clients.each do |name, component|
|
28
|
+
EUSTON_LOG.debug("Thread state of #{name}: #{component.thread_state.inspect}")
|
29
|
+
end
|
30
|
+
|
31
|
+
@queue.pop #<-------- stops here until interrupted
|
32
|
+
|
33
|
+
@clients.each do |name, component|
|
34
|
+
EUSTON_LOG.debug "Stopping component: #{name}"
|
35
|
+
component.stop
|
36
|
+
end
|
37
|
+
|
38
|
+
report_shutdown_reasons
|
39
|
+
EUSTON_LOG.debug "Components stopped"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Euston
|
2
|
+
module CommandProcessorDaemon
|
3
|
+
module Settings
|
4
|
+
def self.configure(cfg=nil)
|
5
|
+
@config ||= {}
|
6
|
+
@config.merge!(cfg) if cfg && cfg.is_a?(Hash)
|
7
|
+
end
|
8
|
+
def self.client_instances
|
9
|
+
@config[:client_instances] || 1
|
10
|
+
end
|
11
|
+
def self.mongo_db_name
|
12
|
+
@config[:mongo_db_name]
|
13
|
+
end
|
14
|
+
def self.debug
|
15
|
+
@config[:debug]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Euston
|
2
|
+
class CommandProcessorRakeTask < Euston::Daemons::RakeTask
|
3
|
+
attr_accessor :amqp_config_path, :command_handler_namespaces, :daemon_config_path
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@command_handler_namespaces = []
|
7
|
+
super(:command_processor_daemon)
|
8
|
+
end
|
9
|
+
|
10
|
+
def before_creating_task
|
11
|
+
@daemon_path = File.expand_path(File.dirname __FILE__) + File::SEPARATOR
|
12
|
+
@daemon_class = 'Euston::CommandProcessorDaemon::Daemon'
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize_paths
|
16
|
+
EUSTON_LOG.debug "AMQP config path: #{@amqp_config_path}"
|
17
|
+
Object.const_set :AMQP_CONFIG_PATH, @amqp_config_path
|
18
|
+
|
19
|
+
EUSTON_LOG.debug "Daemon config path: #{@daemon_config_path}"
|
20
|
+
Object.const_set :DAEMON_CONFIG_PATH, @daemon_config_path
|
21
|
+
|
22
|
+
EUSTON_LOG.debug "Command handler namespaces: #{@command_handler_namespaces}"
|
23
|
+
Object.const_set :COMMAND_HANDLER_NAMESPACES, @command_handler_namespaces
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_environment
|
27
|
+
EUSTON_LOG.debug "Loading environment"
|
28
|
+
require_rel 'config/environment.rb'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_rel '../components'
|
2
|
+
require_rel '../lib'
|
3
|
+
|
4
|
+
Safely::Strategy::Log.logger = EUSTON_LOG
|
5
|
+
Cqrs.uuid = Uuid
|
6
|
+
AMQP.settings.merge! ErbYaml.read(AMQP_CONFIG_PATH, EUSTON_ENV)
|
7
|
+
Euston::EventProcessorDaemon::Settings.configure ErbYaml.read(DAEMON_CONFIG_PATH, EUSTON_ENV)
|
8
|
+
|
9
|
+
hash = ErbYaml.read(MONGOID_CONFIG_PATH, EUSTON_ENV)
|
10
|
+
|
11
|
+
read_connection = Mongo::Connection.new hash[:host], hash[:port], :safe => hash[:safe] #, :logger => EUSTON_LOG
|
12
|
+
|
13
|
+
Mongoid.configure do |config|
|
14
|
+
config.master = Mongo::DB.new(hash[:read_model_database], read_connection)
|
15
|
+
config.logger = EUSTON_LOG
|
16
|
+
end
|
17
|
+
|
18
|
+
Cqrs::RabbitMq.read_model_mongodb = Mongo::DB.new(hash[:read_model_database], read_connection)
|
19
|
+
|
20
|
+
I18n.load_path += Dir[I18N_LOCALES_PATH]
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Euston
|
2
|
+
module EventProcessorDaemon
|
3
|
+
class EventHandlerComponent
|
4
|
+
include Euston::Daemons::ComponentShutdown
|
5
|
+
|
6
|
+
attr_reader :channel, :cli_threads
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
@channel = AMQP::Channel.new
|
10
|
+
@channel.prefetch(1)
|
11
|
+
@event_handler_bindings = Cqrs::RabbitMq::EventHandlerBindings.new(@channel)
|
12
|
+
|
13
|
+
if Object.const_defined? 'EVENT_HANDLER_NAMESPACES'
|
14
|
+
EVENT_HANDLER_NAMESPACES.each do |handler_namespace|
|
15
|
+
ns = Object
|
16
|
+
found = true
|
17
|
+
|
18
|
+
handler_namespace.split('::').each do |c|
|
19
|
+
if found && ns.const_defined?(c)
|
20
|
+
ns = ns.const_get c.to_sym
|
21
|
+
elsif found
|
22
|
+
EUSTON_LOG.warn "Couldn't find requested event handler namespace: #{handler_namespace}"
|
23
|
+
found = false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if found
|
28
|
+
EUSTON_LOG.debug "Successfully found event handler namespace: #{handler_namespace}"
|
29
|
+
@event_handler_bindings.add_namespace ns
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@cli_threads = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def start
|
38
|
+
@event_handler_bindings.namespaced_handler_types.each do |namespace, handler_type|
|
39
|
+
|
40
|
+
@cli_threads["#{namespace}.#{handler_type}"] = Thread.new(namespace, handler_type) do |ns, ht|
|
41
|
+
begin
|
42
|
+
chan = AMQP::Channel.new
|
43
|
+
chan.prefetch(1)
|
44
|
+
EUSTON_LOG.debug "EventHandlerBinding for: #{ns}.#{ht}"
|
45
|
+
binding = Cqrs::RabbitMq::EventHandlerBinding.new(chan, ns, ht)
|
46
|
+
binding.bind
|
47
|
+
binding.listen
|
48
|
+
rescue => e
|
49
|
+
Thread.current[:exception] = e
|
50
|
+
ensure
|
51
|
+
chan.disconnect
|
52
|
+
end
|
53
|
+
check_exception_and_shutdown
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def thread_state
|
59
|
+
out = {}
|
60
|
+
@cli_threads.each do |k, th|
|
61
|
+
out[k] = th.status
|
62
|
+
end
|
63
|
+
out.inspect
|
64
|
+
end
|
65
|
+
|
66
|
+
def stop
|
67
|
+
@cli_threads.values.each { |th| th[:stop] = true }
|
68
|
+
@channel.disconnect
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Euston
|
2
|
+
module EventProcessorDaemon
|
3
|
+
class Daemon
|
4
|
+
include Euston::Daemon
|
5
|
+
|
6
|
+
#attr_reader :ctx,:servers,:bus,:queue,:event_handler
|
7
|
+
attr_reader :queue, :event_handler
|
8
|
+
|
9
|
+
def initialize() #ctx)
|
10
|
+
# @servers = []
|
11
|
+
# @ctx = ctx
|
12
|
+
# @queue = Queue.new
|
13
|
+
|
14
|
+
# bus_client_port = ReadDaemonSettings.bus_port_base
|
15
|
+
# bus_server_port = bus_client_port + 10
|
16
|
+
|
17
|
+
# BusComponentSettings.client_side_bind_address("tcp://*:#{bus_client_port}")
|
18
|
+
# BusComponentSettings.server_side_bind_address("tcp://*:#{bus_server_port}")
|
19
|
+
# ReadModelComponentSettings.bus_server_side_address("tcp://localhost:#{bus_server_port}")
|
20
|
+
|
21
|
+
# @bus = BusComponent.new(@ctx)
|
22
|
+
|
23
|
+
# ReadDaemonSettings.server_instances.times do
|
24
|
+
# @servers << ReadModelComponent.new(@ctx)
|
25
|
+
# end
|
26
|
+
|
27
|
+
@queue = Queue.new
|
28
|
+
@event_handler = EventHandlerComponent.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
# @bus.daemon = self
|
33
|
+
# @bus.start
|
34
|
+
|
35
|
+
# @servers.each do |ele|
|
36
|
+
# sleep(0.5)
|
37
|
+
# ele.daemon = self
|
38
|
+
# ele.start
|
39
|
+
# end
|
40
|
+
@event_handler.daemon = self
|
41
|
+
|
42
|
+
name = 'event_handler'
|
43
|
+
EUSTON_LOG.debug "Starting component: #{name}"
|
44
|
+
@event_handler.start
|
45
|
+
|
46
|
+
EUSTON_LOG.debug "Components started"
|
47
|
+
EUSTON_LOG.debug "Thread state of #{name}: #{@event_handler.thread_state.inspect}"
|
48
|
+
|
49
|
+
# EUSTON_LOG.debug @bus.thread_state.inspect
|
50
|
+
|
51
|
+
# @servers.each do |svr|
|
52
|
+
# EUSTON_LOG.debug "Server thread status: #{svr.thread_state}"
|
53
|
+
# end
|
54
|
+
|
55
|
+
@queue.pop #<-------- stops here until interrupted
|
56
|
+
|
57
|
+
#exiting
|
58
|
+
# @servers.each do |ele|
|
59
|
+
# ele.stop
|
60
|
+
# end
|
61
|
+
# sleep(0.6)
|
62
|
+
# @bus.stop
|
63
|
+
|
64
|
+
EUSTON_LOG.debug "Stopping component: #{name}"
|
65
|
+
@event_handler.stop
|
66
|
+
|
67
|
+
report_shutdown_reasons
|
68
|
+
EUSTON_LOG.debug "Components stopped"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Euston
|
2
|
+
module EventProcessorDaemon
|
3
|
+
module Settings
|
4
|
+
def self.configure(cfg=nil)
|
5
|
+
@config ||= {}
|
6
|
+
@config.merge!(cfg) if cfg && cfg.is_a?(Hash)
|
7
|
+
end
|
8
|
+
def self.server_instances
|
9
|
+
@config[:server_instances] || 2
|
10
|
+
end
|
11
|
+
def self.bus_port_base
|
12
|
+
(@config[:zmq_base_port] || 8200).to_i
|
13
|
+
end
|
14
|
+
def self.mongo_db_name
|
15
|
+
@config[:mongo_db_name]
|
16
|
+
end
|
17
|
+
def self.debug
|
18
|
+
@config[:debug]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Euston
|
2
|
+
class EventProcessorRakeTask < Euston::Daemons::RakeTask
|
3
|
+
attr_accessor :amqp_config_path, :daemon_config_path, :event_handler_namespaces, :i18n_locales_path, :mongoid_config_path
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@event_handler_namespaces = []
|
7
|
+
super(:event_processor_daemon)
|
8
|
+
end
|
9
|
+
|
10
|
+
def before_creating_task
|
11
|
+
@daemon_path = File.expand_path(File.dirname __FILE__) + File::SEPARATOR
|
12
|
+
@daemon_class = 'Euston::EventProcessorDaemon::Daemon'
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize_paths
|
16
|
+
EUSTON_LOG.debug "AMQP config path: #{@amqp_config_path}"
|
17
|
+
Object.const_set :AMQP_CONFIG_PATH, @amqp_config_path
|
18
|
+
|
19
|
+
EUSTON_LOG.debug "Daemon config path: #{@daemon_config_path}"
|
20
|
+
Object.const_set :DAEMON_CONFIG_PATH, @daemon_config_path
|
21
|
+
|
22
|
+
EUSTON_LOG.debug "Event handler namespaces: #{@event_handler_namespaces}"
|
23
|
+
Object.const_set :EVENT_HANDLER_NAMESPACES, @event_handler_namespaces
|
24
|
+
|
25
|
+
EUSTON_LOG.debug "i18n locales path: #{@i18n_locales_path}"
|
26
|
+
Object.const_set :I18N_LOCALES_PATH, @i18n_locales_path
|
27
|
+
|
28
|
+
EUSTON_LOG.debug "Mongoid config path: #{@mongoid_config_path}"
|
29
|
+
Object.const_set :MONGOID_CONFIG_PATH, @mongoid_config_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_environment
|
33
|
+
EUSTON_LOG.debug "Loading environment"
|
34
|
+
require_rel 'config/environment.rb'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
class BasicComponent
|
4
|
+
include Euston::Daemons::ComponentShutdown
|
5
|
+
|
6
|
+
attr_reader :client, :cli_thread
|
7
|
+
|
8
|
+
def initialize(hosted_object)
|
9
|
+
@client = hosted_object
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
@cli_thread = Thread.new do
|
14
|
+
begin
|
15
|
+
@client.start
|
16
|
+
rescue => e
|
17
|
+
Thread.current[:exception] = e
|
18
|
+
end
|
19
|
+
check_exception_and_shutdown
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def thread_state
|
24
|
+
@cli_thread.status
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
@cli_thread[:stop] = true
|
29
|
+
@client.disconnect if @client.respond_to?(:disconnect)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemons
|
3
|
+
module ComponentShutdown
|
4
|
+
attr_writer :daemon
|
5
|
+
|
6
|
+
def check_exception_and_shutdown(errors=[])
|
7
|
+
if errors.compact.empty?
|
8
|
+
errors << Thread.current[:exception]
|
9
|
+
return if errors.compact.empty?
|
10
|
+
end
|
11
|
+
errors.compact.each do |e|
|
12
|
+
if e.respond_to?(:cause) #its a serious (Java?) exception
|
13
|
+
Thread.current[:stop] = true
|
14
|
+
@daemon.irrecoverable_signal_from_component! e
|
15
|
+
else
|
16
|
+
Safely.report! e
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Euston
|
2
|
+
module Daemon
|
3
|
+
def errors
|
4
|
+
@errors ||= Queue.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def irrecoverable_signal_from_component!(e)
|
8
|
+
self.errors.push e
|
9
|
+
@queue.push "SHUTDOWN"
|
10
|
+
end
|
11
|
+
|
12
|
+
def report_shutdown_reasons
|
13
|
+
sleep(0.25)
|
14
|
+
reports = []
|
15
|
+
until self.errors.empty? do
|
16
|
+
e = self.errors.pop(true) rescue nil
|
17
|
+
next if e.nil?
|
18
|
+
case e
|
19
|
+
when NativeException
|
20
|
+
Safely.report!(e.cause)
|
21
|
+
else
|
22
|
+
Safely.report!(e)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class RabbitMQClient
|
2
|
+
class Queue
|
3
|
+
include Hollywood
|
4
|
+
|
5
|
+
attr_writer :timeout
|
6
|
+
|
7
|
+
def delivery_timeout
|
8
|
+
@timeout ||= 500
|
9
|
+
end
|
10
|
+
|
11
|
+
def safe_subscribe
|
12
|
+
_consumer = self.consumer
|
13
|
+
until Thread.current[:stop] do
|
14
|
+
safe_subscribe_with_timeout(_consumer, self.delivery_timeout)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def safe_handle_message(msg)
|
19
|
+
begin
|
20
|
+
message = JSON.parse_sym(msg.body)
|
21
|
+
begin
|
22
|
+
callback :message_received, message
|
23
|
+
msg.ack!
|
24
|
+
rescue ::EventStore::ConcurrencyError
|
25
|
+
msg.reject! true #requeue
|
26
|
+
rescue => e
|
27
|
+
callback :message_failed, message, e, msg
|
28
|
+
Safely.report! e
|
29
|
+
end
|
30
|
+
rescue => e
|
31
|
+
callback :message_decode_failed, msg.body, e
|
32
|
+
msg.ack!
|
33
|
+
Safely.report! e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def consumer(auto_ack=false)
|
38
|
+
consumer = QueueingConsumer.new(@channel)
|
39
|
+
@channel.basic_consume(@name, auto_ack, consumer)
|
40
|
+
consumer
|
41
|
+
end
|
42
|
+
|
43
|
+
def safe_subscribe_with_timeout(consumer, timeout=500)
|
44
|
+
loop do
|
45
|
+
delivery = nil
|
46
|
+
begin
|
47
|
+
delivery = consumer.next_delivery(timeout)
|
48
|
+
rescue NativeException => e
|
49
|
+
Thread.current[:exception] = e
|
50
|
+
break
|
51
|
+
end
|
52
|
+
break if delivery.nil?
|
53
|
+
remsg = ReactiveMessage.new(@channel, delivery, String.from_java_bytes(delivery.get_body))
|
54
|
+
safe_handle_message remsg
|
55
|
+
@channel.basic_ack(delivery.envelope.delivery_tag, false) if remsg.should_acknowledge?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ReactiveMessage
|
61
|
+
def reject(opts={})
|
62
|
+
self.reject!(opts.fetch(:requeue, true))
|
63
|
+
end
|
64
|
+
def ack(multiple=false)
|
65
|
+
self.ack!
|
66
|
+
end
|
67
|
+
def method()
|
68
|
+
self.envelope
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|