startback 0.11.3 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/startback/event/agent.rb +71 -0
- data/lib/startback/event/bus/bunny/async.rb +165 -0
- data/lib/startback/{bus → event/bus}/bunny.rb +0 -0
- data/lib/startback/event/bus/memory/async.rb +45 -0
- data/lib/startback/event/bus/memory/sync.rb +35 -0
- data/lib/startback/{bus → event/bus}/memory.rb +0 -0
- data/lib/startback/event/bus.rb +100 -0
- data/lib/startback/event/engine.rb +171 -0
- data/lib/startback/event.rb +3 -0
- data/lib/startback/support/env.rb +41 -0
- data/lib/startback/support/log_formatter.rb +2 -0
- data/lib/startback/support/robustness.rb +3 -3
- data/lib/startback/support.rb +1 -0
- data/lib/startback/version.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/event/bus/memory/test_async.rb +43 -0
- data/spec/unit/event/bus/memory/test_sync.rb +43 -0
- data/spec/unit/support/test_env.rb +75 -0
- data/spec/unit/support/test_robusteness.rb +11 -1
- metadata +14 -10
- data/lib/startback/bus/bunny/async.rb +0 -123
- data/lib/startback/bus/memory/async.rb +0 -40
- data/lib/startback/bus/memory/sync.rb +0 -30
- data/lib/startback/bus.rb +0 -94
- data/spec/unit/bus/memory/test_async.rb +0 -41
- data/spec/unit/bus/memory/test_sync.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67ffd7abceaf1b4e048cc8f02f668b682ea81f62fc7de5228a9f0d12abab1df8
|
4
|
+
data.tar.gz: 1eb075bdec30a569726bc3f2a74c1ce427aef8ff0c3cbcee401a61c034e71ca6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8cb4d6f1302a34cab267c836c1e0d81c54267607de7f24d350183ff05a79af7cc216a6f34bc96c0e1204f71cca1de744fde448590e99bd96a1ab45c15c8ceddf
|
7
|
+
data.tar.gz: fecf573e39eb04317d756414349d94092f24a07210629a55347191ef74ec94fcc8079f8a4f38b6c58bf94be5c59fbd79a90a168a4925819ce771269ccae6e3da
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Startback
|
2
|
+
class Event
|
3
|
+
#
|
4
|
+
# An agent listen to specific events and react with its
|
5
|
+
# `call` method.
|
6
|
+
#
|
7
|
+
# This class is intended to be subclasses and the following
|
8
|
+
# methods overriden:
|
9
|
+
#
|
10
|
+
# - install_listeners that installs sync and async listeners
|
11
|
+
# - call to create a context and implement reaction behavior
|
12
|
+
#
|
13
|
+
class Agent
|
14
|
+
include Support::OperationRunner
|
15
|
+
include Support::Robustness
|
16
|
+
|
17
|
+
def initialize(engine)
|
18
|
+
@engine = engine
|
19
|
+
install_listeners
|
20
|
+
end
|
21
|
+
attr_reader :engine
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
# Installs the various event handlers by calling `sync`
|
26
|
+
# and `async` methods.
|
27
|
+
#
|
28
|
+
# This method is intended to be overriden.
|
29
|
+
def install_listeners
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the underlying bus
|
33
|
+
def bus
|
34
|
+
engine.bus
|
35
|
+
end
|
36
|
+
|
37
|
+
# Asynchronously listen to a specific event.
|
38
|
+
#
|
39
|
+
# See Bus#listen
|
40
|
+
def async(exchange, queue)
|
41
|
+
bus.async.listen(exchange, queue) do |event|
|
42
|
+
dup.call(event)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Synchronously listen to a specific event.
|
47
|
+
#
|
48
|
+
# See Bus#listen
|
49
|
+
def sync(exchange, queue)
|
50
|
+
bus.listen(exchange, queue) do |event|
|
51
|
+
dup.call(event)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Reacts to a specific event.
|
56
|
+
#
|
57
|
+
# This method must be implemented by subclasses and raises
|
58
|
+
# an error by default.
|
59
|
+
def call(event = nil)
|
60
|
+
log(:fatal, {
|
61
|
+
op: self.class,
|
62
|
+
op_data: event,
|
63
|
+
error: %Q{Unexpected call to Startback::Event::Agent#call},
|
64
|
+
backtrace: caller
|
65
|
+
})
|
66
|
+
raise NotImplementedError
|
67
|
+
end
|
68
|
+
|
69
|
+
end # class Agent
|
70
|
+
end # class Event
|
71
|
+
end # module Starback
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
module Startback
|
3
|
+
class Event
|
4
|
+
class Bus
|
5
|
+
module Bunny
|
6
|
+
#
|
7
|
+
# Asynchronous implementation of the bus abstraction, on top of RabbitMQ
|
8
|
+
# and using the 'bunny' gem (you need to include it in your Gemfile
|
9
|
+
# yourself: it is NOT a startback official dependency).
|
10
|
+
#
|
11
|
+
# This bus implementation emits events by dumping them to RabbitMQ using
|
12
|
+
# the event type as exchange name. Listeners may use the `processor`
|
13
|
+
# parameter to specify the queue name ; otherwise a default "main" queue
|
14
|
+
# is used.
|
15
|
+
#
|
16
|
+
# Examples:
|
17
|
+
#
|
18
|
+
# # Connects to RabbitMQ using all default options
|
19
|
+
# #
|
20
|
+
# # Uses the STARTBACK_BUS_BUNNY_ASYNC_URL environment variable for
|
21
|
+
# # connection URL if present.
|
22
|
+
# Startback::Bus::Bunny::Async.new
|
23
|
+
#
|
24
|
+
# # Connects to RabbitMQ using a specific URL
|
25
|
+
# Startback::Bus::Bunny::Async.new("amqp://rabbituser:rabbitpass@192.168.17.17")
|
26
|
+
# Startback::Bus::Bunny::Async.new(url: "amqp://rabbituser:rabbitpass@192.168.17.17")
|
27
|
+
#
|
28
|
+
# # Connects to RabbitMQ using specific connection options. See Bunny's own
|
29
|
+
# # documentation
|
30
|
+
# Startback::Bus::Bunny::Async.new({
|
31
|
+
# connection_options: {
|
32
|
+
# host: "192.168.17.17"
|
33
|
+
# }
|
34
|
+
# })
|
35
|
+
#
|
36
|
+
class Async
|
37
|
+
include Support::Robustness
|
38
|
+
|
39
|
+
CHANNEL_KEY = 'Startback::Bus::Bunny::Async::ChannelKey'
|
40
|
+
|
41
|
+
DEFAULT_OPTIONS = {
|
42
|
+
# (optional) The URL to use for connecting to RabbitMQ.
|
43
|
+
url: ENV['STARTBACK_BUS_BUNNY_ASYNC_URL'],
|
44
|
+
|
45
|
+
# (optional) The options has to pass to ::Bunny constructor
|
46
|
+
connection_options: nil,
|
47
|
+
|
48
|
+
# (optional) The options to use for the emitter/listener fanout
|
49
|
+
fanout_options: {},
|
50
|
+
|
51
|
+
# (optional) The options to use for the listener queue
|
52
|
+
queue_options: {},
|
53
|
+
|
54
|
+
# (optional) Default event factory to use, if any
|
55
|
+
event_factory: nil,
|
56
|
+
|
57
|
+
# (optional) A default context to use for general logging
|
58
|
+
context: nil,
|
59
|
+
|
60
|
+
# (optional) Size of consumer pool
|
61
|
+
consumer_pool_size: 1,
|
62
|
+
|
63
|
+
# (optional) Whether the program must be aborted on consumption
|
64
|
+
# error
|
65
|
+
abort_on_exception: true,
|
66
|
+
|
67
|
+
# (optional) Whether connection occurs immediately,
|
68
|
+
# or on demand later
|
69
|
+
autoconnect: false
|
70
|
+
}
|
71
|
+
|
72
|
+
# Creates a bus instance, using the various options provided to
|
73
|
+
# fine-tune behavior.
|
74
|
+
def initialize(options = {})
|
75
|
+
options = { url: options } if options.is_a?(String)
|
76
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
77
|
+
connect if @options[:autoconnect]
|
78
|
+
end
|
79
|
+
attr_reader :options
|
80
|
+
|
81
|
+
def connect
|
82
|
+
disconnect
|
83
|
+
conn = options[:connection_options] || options[:url]
|
84
|
+
try_max_times(10) do
|
85
|
+
@bunny = ::Bunny.new(conn)
|
86
|
+
@bunny.start
|
87
|
+
channel # make sure we already create the channel
|
88
|
+
log(:info, {op: "#{self.class.name}#connect", op_data: conn}, options[:context])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def disconnect
|
93
|
+
if channel = Thread.current[CHANNEL_KEY]
|
94
|
+
channel.close
|
95
|
+
Thread.current[CHANNEL_KEY] = nil
|
96
|
+
end
|
97
|
+
@bunny.close if @bunny
|
98
|
+
end
|
99
|
+
|
100
|
+
def channel
|
101
|
+
unless @bunny
|
102
|
+
raise Startback::Errors::Error, "Please connect your bus first, or use autoconnect: true"
|
103
|
+
end
|
104
|
+
|
105
|
+
Thread.current[CHANNEL_KEY] ||= @bunny.create_channel(
|
106
|
+
nil,
|
107
|
+
consumer_pool_size, # consumer_pool_size
|
108
|
+
abort_on_exception? # consumer_pool_abort_on_exception
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def emit(event)
|
113
|
+
stop_errors(self, "emit", event.context) do
|
114
|
+
fanout = channel.fanout(event.type.to_s, fanout_options)
|
115
|
+
fanout.publish(event.to_json)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def listen(type, processor = nil, listener = nil, &bl)
|
120
|
+
raise ArgumentError, "A listener must be provided" unless listener || bl
|
121
|
+
|
122
|
+
fanout = channel.fanout(type.to_s, fanout_options)
|
123
|
+
queue = channel.queue((processor || "main").to_s, queue_options)
|
124
|
+
queue.bind(fanout)
|
125
|
+
queue.subscribe do |delivery_info, properties, body|
|
126
|
+
event = stop_errors(self, "listen") do
|
127
|
+
factor_event(body)
|
128
|
+
end
|
129
|
+
stop_errors(self, "listen", event.context) do
|
130
|
+
(listener || bl).call(event)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
protected
|
136
|
+
|
137
|
+
def consumer_pool_size
|
138
|
+
options[:consumer_pool_size]
|
139
|
+
end
|
140
|
+
|
141
|
+
def abort_on_exception?
|
142
|
+
options[:abort_on_exception]
|
143
|
+
end
|
144
|
+
|
145
|
+
def fanout_options
|
146
|
+
options[:fanout_options]
|
147
|
+
end
|
148
|
+
|
149
|
+
def queue_options
|
150
|
+
options[:queue_options]
|
151
|
+
end
|
152
|
+
|
153
|
+
def factor_event(body)
|
154
|
+
if options[:event_factory]
|
155
|
+
options[:event_factory].call(body)
|
156
|
+
else
|
157
|
+
Event.json(body, options)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end # class Async
|
162
|
+
end # module Bunny
|
163
|
+
end # class Bus
|
164
|
+
end # class Event
|
165
|
+
end # module Startback
|
File without changes
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Startback
|
2
|
+
class Event
|
3
|
+
class Bus
|
4
|
+
module Memory
|
5
|
+
#
|
6
|
+
# Asynchronous implementation of the Bus abstraction, for use between
|
7
|
+
# components sharing the same process.
|
8
|
+
#
|
9
|
+
# This implementation actually calls listeners synchronously (it mays)
|
10
|
+
# but hides error raised by them. See Bus::Bunny::Async for another
|
11
|
+
# implementation that is truly asynchronous and relies on RabbitMQ.
|
12
|
+
#
|
13
|
+
class Async
|
14
|
+
include Support::Robustness
|
15
|
+
|
16
|
+
DEFAULT_OPTIONS = {
|
17
|
+
}
|
18
|
+
|
19
|
+
def initialize(options = {})
|
20
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
21
|
+
@listeners = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect
|
25
|
+
end
|
26
|
+
|
27
|
+
def emit(event)
|
28
|
+
(@listeners[event.type.to_s] || []).each do |l|
|
29
|
+
stop_errors(self, "emit", event) {
|
30
|
+
l.call(event)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def listen(type, processor = nil, listener = nil, &bl)
|
36
|
+
raise ArgumentError, "A listener must be provided" unless listener || bl
|
37
|
+
@listeners[type.to_s] ||= []
|
38
|
+
@listeners[type.to_s] << (listener || bl)
|
39
|
+
end
|
40
|
+
|
41
|
+
end # class Sync
|
42
|
+
end # module Memory
|
43
|
+
end # class Bus
|
44
|
+
end # class Event
|
45
|
+
end # module Startback
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Startback
|
2
|
+
class Event
|
3
|
+
class Bus
|
4
|
+
module Memory
|
5
|
+
#
|
6
|
+
# Synchronous implementation of the Bus abstraction, for use between
|
7
|
+
# components sharing the same process.
|
8
|
+
#
|
9
|
+
class Sync
|
10
|
+
include Support::Robustness
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@listeners = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def connect
|
17
|
+
end
|
18
|
+
|
19
|
+
def emit(event)
|
20
|
+
(@listeners[event.type.to_s] || []).each do |l|
|
21
|
+
l.call(event)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def listen(type, processor = nil, listener = nil, &bl)
|
26
|
+
raise ArgumentError, "A listener must be provided" unless listener || bl
|
27
|
+
@listeners[type.to_s] ||= []
|
28
|
+
@listeners[type.to_s] << (listener || bl)
|
29
|
+
end
|
30
|
+
|
31
|
+
end # class Sync
|
32
|
+
end # module Memory
|
33
|
+
end # class Bus
|
34
|
+
end # class Event
|
35
|
+
end # module Startback
|
File without changes
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Startback
|
2
|
+
class Event
|
3
|
+
#
|
4
|
+
# Sync and async bus abstraction allowing to register listeners and
|
5
|
+
# emitting events towards them.
|
6
|
+
#
|
7
|
+
# This bus actually decorates two busses, one in synchronous and the
|
8
|
+
# other one is asynchronous (optional).
|
9
|
+
#
|
10
|
+
# * A synchronous bus MUST call the listeners as part of emitting
|
11
|
+
# process, and MUST re-raise any error occuring during that process.
|
12
|
+
# See, e.g. Startback::Bus::Memory::Sync
|
13
|
+
#
|
14
|
+
# * An asynchronous bus MAY call the listeners later, but MUST hide
|
15
|
+
# errors to the emitter.
|
16
|
+
# See, e.g. Startback::Bus::Memory::Async
|
17
|
+
#
|
18
|
+
# This bus facade emits events to both sync and async busses (if any),
|
19
|
+
# and listen on the sync one by default.
|
20
|
+
#
|
21
|
+
# For emitters:
|
22
|
+
#
|
23
|
+
# # This will synchronously call every listeners who `listen`
|
24
|
+
# # on the synchronous bus (& reraise exceptions) then call
|
25
|
+
# # (possibly later) all listeners who `listen` on the
|
26
|
+
# # asynchronous bus if any (& hide exceptions).
|
27
|
+
# bus.emit(event)
|
28
|
+
#
|
29
|
+
# # This only reaches sync listeners
|
30
|
+
# bus.sync.emit(event)
|
31
|
+
#
|
32
|
+
# # This only reaches async listeners (an async bus must be set)
|
33
|
+
# bus.async.emit(event)
|
34
|
+
#
|
35
|
+
# Please note that there is currently no way to reach sync listeners
|
36
|
+
# without having to implement error handling on the emitter side.
|
37
|
+
#
|
38
|
+
# For listeners:
|
39
|
+
#
|
40
|
+
# # This will listen synchronously and make the emitter fail if
|
41
|
+
# # anything goes wrong with the callback:
|
42
|
+
# bus.listen(event_type) do |event|
|
43
|
+
# ...
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# # It is a shortcut for:
|
47
|
+
# bus.sync.listen(event_type) do |event| ... end
|
48
|
+
#
|
49
|
+
# This will listen asynchronously and could not make the emitter
|
50
|
+
# fail if something goes wrong with the callback.
|
51
|
+
# bus.async.listen(event_type) do |event|
|
52
|
+
# ...
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# Feel free to access the sync and async busses directly for specific
|
56
|
+
# cases though.
|
57
|
+
#
|
58
|
+
class Bus
|
59
|
+
include Support::Robustness
|
60
|
+
|
61
|
+
def initialize(sync = Memory::Sync.new, async = nil)
|
62
|
+
@sync = sync
|
63
|
+
@async = async
|
64
|
+
end
|
65
|
+
attr_reader :sync, :async
|
66
|
+
|
67
|
+
def connect
|
68
|
+
sync.connect if sync
|
69
|
+
async.connect if async
|
70
|
+
end
|
71
|
+
|
72
|
+
# Emits a particular event to the listeners.
|
73
|
+
#
|
74
|
+
# @arg event an event, should be an Event instance (through duck
|
75
|
+
# typing is allowed)
|
76
|
+
def emit(event)
|
77
|
+
monitor({
|
78
|
+
op: "Startback::Bus#emit",
|
79
|
+
op_data: {
|
80
|
+
event: { type: event.type }
|
81
|
+
}
|
82
|
+
}, event.context) do
|
83
|
+
sync.emit(event)
|
84
|
+
async.emit(event) if async
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Registers `listener` as being interested in receiving events of
|
89
|
+
# a specific type.
|
90
|
+
#
|
91
|
+
# @arg type: Symbol, the type of event the listener is interested in.
|
92
|
+
# @arg listener: Proc, the listener itself.
|
93
|
+
def listen(type, processor = nil, listener = nil, &bl)
|
94
|
+
sync.listen(type, processor, listener, &bl)
|
95
|
+
end
|
96
|
+
|
97
|
+
end # class Bus
|
98
|
+
end # class Event
|
99
|
+
end # module Startback
|
100
|
+
require_relative 'bus/memory'
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'webrick'
|
3
|
+
require 'startback'
|
4
|
+
module Startback
|
5
|
+
class Event
|
6
|
+
#
|
7
|
+
# This class is the starting point of event handling in
|
8
|
+
# Startback. It holds a Bus instance to which emitters
|
9
|
+
# and listeners can connect, and the possibility for the
|
10
|
+
# the listening part to start an infinite loop (ServerEngine).
|
11
|
+
#
|
12
|
+
# The Engine automatically runs a Webrick small webapp
|
13
|
+
# with a /healthcheck webservice. The class can be extended
|
14
|
+
# and method `on_health_check` overriden to run specific
|
15
|
+
# checks.
|
16
|
+
#
|
17
|
+
# This class goes hand in hand with the `startback:engine`
|
18
|
+
# docker image. It can be extended by subclasses to override
|
19
|
+
# the following methods:
|
20
|
+
#
|
21
|
+
# - bus to use something else than a simple memory bus
|
22
|
+
# - on_health_check to check specific health conditions
|
23
|
+
# - create_agents to instantiate all listening agents
|
24
|
+
# (unless auto_create_agents is used)
|
25
|
+
#
|
26
|
+
class Engine
|
27
|
+
include Support::Robustness
|
28
|
+
|
29
|
+
DEFAULT_OPTIONS = {
|
30
|
+
|
31
|
+
# To be passed to ServerEngine
|
32
|
+
server_engine: {}
|
33
|
+
|
34
|
+
}
|
35
|
+
|
36
|
+
def initialize(options = {}, context = Context.new)
|
37
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
38
|
+
@context = context
|
39
|
+
end
|
40
|
+
attr_reader :options, :context
|
41
|
+
|
42
|
+
class << self
|
43
|
+
def auto_create_agents?
|
44
|
+
!!@auto_create_agents
|
45
|
+
end
|
46
|
+
|
47
|
+
# Register a base class which will be used to discover
|
48
|
+
# the agents to start when the engine is ran.
|
49
|
+
def auto_create_agents(base_class = nil)
|
50
|
+
@auto_create_agents ||= base_class
|
51
|
+
@auto_create_agents
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# This method is executed on health check and can be
|
56
|
+
# overriden by subclasses to perform specific checks.
|
57
|
+
def on_health_check
|
58
|
+
"Ok"
|
59
|
+
end
|
60
|
+
|
61
|
+
def bus
|
62
|
+
::Startback::Event::Bus.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def connect
|
66
|
+
log(:info, self, "Connecting to the bus now!")
|
67
|
+
bus.connect
|
68
|
+
end
|
69
|
+
|
70
|
+
def run(options = {})
|
71
|
+
connect
|
72
|
+
|
73
|
+
log(:info, self, "Running agents and server engine!")
|
74
|
+
create_agents
|
75
|
+
Runner.new(self, options[:server_engine] || {}).run
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_agents
|
79
|
+
return unless parent = self.class.auto_create_agents
|
80
|
+
|
81
|
+
ObjectSpace
|
82
|
+
.each_object(Class)
|
83
|
+
.select { |klass| klass < parent }
|
84
|
+
.each { |klass| klass.new(self) }
|
85
|
+
end
|
86
|
+
|
87
|
+
class Runner
|
88
|
+
|
89
|
+
DEFAULT_SERVER_ENGINE_OPTIONS = {
|
90
|
+
daemonize: false,
|
91
|
+
worker_type: 'process',
|
92
|
+
workers: 1
|
93
|
+
}
|
94
|
+
|
95
|
+
def initialize(engine, options = {})
|
96
|
+
raise ArgumentError if engine.nil?
|
97
|
+
|
98
|
+
@engine = engine
|
99
|
+
@options = DEFAULT_SERVER_ENGINE_OPTIONS.merge(options)
|
100
|
+
require 'serverengine'
|
101
|
+
end
|
102
|
+
attr_reader :engine, :options
|
103
|
+
|
104
|
+
def run(options = {})
|
105
|
+
health = self.class.build_health_check(engine)
|
106
|
+
worker = self.class.build_worker(engine, health)
|
107
|
+
se = ServerEngine.create(nil, worker, options)
|
108
|
+
se.run
|
109
|
+
se
|
110
|
+
end
|
111
|
+
|
112
|
+
class << self
|
113
|
+
def run(*args, &bl)
|
114
|
+
new.run(*args, &bl)
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_health_check(engine)
|
118
|
+
Rack::Builder.new do
|
119
|
+
map '/health-check' do
|
120
|
+
health = Startback::Web::HealthCheck.new {
|
121
|
+
engine.on_health_check
|
122
|
+
}
|
123
|
+
run(health)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_worker(engine, health)
|
129
|
+
Module.new do
|
130
|
+
include Support::Env
|
131
|
+
|
132
|
+
def initialize
|
133
|
+
@stop_flag = ServerEngine::BlockingFlag.new
|
134
|
+
end
|
135
|
+
|
136
|
+
define_method(:health) do
|
137
|
+
health
|
138
|
+
end
|
139
|
+
|
140
|
+
define_method(:engine) do
|
141
|
+
engine
|
142
|
+
end
|
143
|
+
|
144
|
+
def run
|
145
|
+
ran = false
|
146
|
+
until @stop_flag.set?
|
147
|
+
if ran
|
148
|
+
engine.send(:log, :warn, engine, "Restarting internal loop")
|
149
|
+
else
|
150
|
+
engine.send(:log, :info, engine, "Starting internal loop")
|
151
|
+
end
|
152
|
+
Rack::Handler::WEBrick.run(health, {
|
153
|
+
:Port => env('STARTBACK_ENGINE_PORT', '3000').to_i,
|
154
|
+
:Host => env('STARTBACK_ENGINE_LISTEN', '0.0.0.0')
|
155
|
+
})
|
156
|
+
ran = true
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def stop
|
161
|
+
engine.send(:log, :info, engine, "Stopping internal loop")
|
162
|
+
@stop_flag.set!
|
163
|
+
Rack::Handler::WEBrick.shutdown
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end # class << self
|
168
|
+
end # class Runner
|
169
|
+
end # class Engine
|
170
|
+
end # class Event
|
171
|
+
end # module Startback
|
data/lib/startback/event.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Startback
|
2
|
+
module Support
|
3
|
+
# This method provides the `env` and `env!` methods that
|
4
|
+
# help querying environment variables easily.
|
5
|
+
module Env
|
6
|
+
|
7
|
+
# Returns an environment variable or raise an error if
|
8
|
+
# not set.
|
9
|
+
#
|
10
|
+
# The result is always a String with no leading/trailing
|
11
|
+
# spaces.
|
12
|
+
#
|
13
|
+
# If a block is given, the environment variable is yield
|
14
|
+
# and the result of the block returned.
|
15
|
+
def env!(key, default = nil, &bl)
|
16
|
+
v = ENV[key].to_s.strip
|
17
|
+
raise Startback::Error, "Missing ENV var `#{key}`" if v.empty?
|
18
|
+
|
19
|
+
env(key, default, &bl)
|
20
|
+
end
|
21
|
+
module_function :env!
|
22
|
+
|
23
|
+
# Returns an environment variable or the default value
|
24
|
+
# passed as second argument.
|
25
|
+
#
|
26
|
+
# The result is always a String with no leading/trailing
|
27
|
+
# spaces.
|
28
|
+
#
|
29
|
+
# If a block is given, the environment variable is yield
|
30
|
+
# and the result of the block returned.
|
31
|
+
def env(key, default = nil, &bl)
|
32
|
+
v = ENV[key].to_s.strip
|
33
|
+
v = v.empty? ? default : v
|
34
|
+
v = bl.call(v) if bl && v
|
35
|
+
v
|
36
|
+
end
|
37
|
+
module_function :env
|
38
|
+
|
39
|
+
end # module Env
|
40
|
+
end # module Support
|
41
|
+
end # module Startback
|
@@ -43,7 +43,7 @@ module Startback
|
|
43
43
|
@@default_logger ||= begin
|
44
44
|
l = ::Logger.new(STDOUT)
|
45
45
|
l.formatter = LogFormatter.new
|
46
|
-
l.warn(op: "#{self}", op_data: { msg: "Using default logger
|
46
|
+
l.warn(op: "#{self}", op_data: { msg: "Using default logger to STDOUT" })
|
47
47
|
@@default_logger = l
|
48
48
|
end
|
49
49
|
@@default_logger
|
@@ -67,6 +67,8 @@ module Startback
|
|
67
67
|
log_msg.dup
|
68
68
|
elsif log_msg.is_a?(String)
|
69
69
|
log_msg = { op: "#{log_msg}#{method.nil? ? '' : '#'+method.to_s}" }
|
70
|
+
elsif log_msg.is_a?(Exception)
|
71
|
+
log_msg = { error: log_msg }
|
70
72
|
else
|
71
73
|
log_msg = log_msg.class unless log_msg.is_a?(Module)
|
72
74
|
log_msg = { op: "#{log_msg.name}##{method}" }
|
@@ -105,8 +107,6 @@ module Startback
|
|
105
107
|
}
|
106
108
|
Tools.info(args, op_took: took)
|
107
109
|
result
|
108
|
-
rescue => ex
|
109
|
-
raise
|
110
110
|
end
|
111
111
|
|
112
112
|
# Executes the block without letting errors propagate.
|