emque-consuming 1.0.0.beta4
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.
- checksums.yaml +7 -0
- data/.gitignore +40 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +205 -0
- data/Rakefile +14 -0
- data/bin/emque +5 -0
- data/emque-consuming.gemspec +36 -0
- data/lib/emque/consuming/actor.rb +21 -0
- data/lib/emque/consuming/adapter.rb +47 -0
- data/lib/emque/consuming/adapters/rabbit_mq/manager.rb +111 -0
- data/lib/emque/consuming/adapters/rabbit_mq/retry_worker.rb +59 -0
- data/lib/emque/consuming/adapters/rabbit_mq/worker.rb +87 -0
- data/lib/emque/consuming/adapters/rabbit_mq.rb +26 -0
- data/lib/emque/consuming/application.rb +112 -0
- data/lib/emque/consuming/cli.rb +140 -0
- data/lib/emque/consuming/command_receivers/base.rb +37 -0
- data/lib/emque/consuming/command_receivers/http_server.rb +103 -0
- data/lib/emque/consuming/command_receivers/unix_socket.rb +169 -0
- data/lib/emque/consuming/configuration.rb +63 -0
- data/lib/emque/consuming/consumer/common.rb +61 -0
- data/lib/emque/consuming/consumer.rb +33 -0
- data/lib/emque/consuming/consuming.rb +27 -0
- data/lib/emque/consuming/control/errors.rb +41 -0
- data/lib/emque/consuming/control/workers.rb +23 -0
- data/lib/emque/consuming/control.rb +33 -0
- data/lib/emque/consuming/core.rb +89 -0
- data/lib/emque/consuming/error_tracker.rb +39 -0
- data/lib/emque/consuming/generators/application.rb +95 -0
- data/lib/emque/consuming/helpers.rb +29 -0
- data/lib/emque/consuming/logging.rb +32 -0
- data/lib/emque/consuming/message.rb +22 -0
- data/lib/emque/consuming/pidfile.rb +54 -0
- data/lib/emque/consuming/router.rb +73 -0
- data/lib/emque/consuming/runner.rb +168 -0
- data/lib/emque/consuming/status.rb +26 -0
- data/lib/emque/consuming/tasks.rb +121 -0
- data/lib/emque/consuming/transmitter.rb +31 -0
- data/lib/emque/consuming/version.rb +5 -0
- data/lib/emque/consuming.rb +9 -0
- data/lib/emque-consuming.rb +3 -0
- data/lib/templates/.gitignore.tt +25 -0
- data/lib/templates/Gemfile.tt +6 -0
- data/lib/templates/Rakefile.tt +7 -0
- data/lib/templates/config/application.rb.tt +42 -0
- data/lib/templates/config/environments/development.rb.tt +2 -0
- data/lib/templates/config/environments/production.rb.tt +2 -0
- data/lib/templates/config/environments/staging.rb.tt +2 -0
- data/lib/templates/config/environments/test.rb.tt +2 -0
- data/lib/templates/config/routes.rb.tt +8 -0
- data/spec/application_spec.rb +28 -0
- data/spec/cli_spec.rb +136 -0
- data/spec/configuration_spec.rb +47 -0
- data/spec/consumer_spec.rb +56 -0
- data/spec/control/errors_spec.rb +170 -0
- data/spec/control_spec.rb +15 -0
- data/spec/core_spec.rb +121 -0
- data/spec/dummy/config/application.rb +38 -0
- data/spec/dummy/config/environments/test.rb +0 -0
- data/spec/dummy/config/routes.rb +0 -0
- data/spec/error_tracker_spec.rb +64 -0
- data/spec/pidfile_spec.rb +74 -0
- data/spec/router_spec.rb +14 -0
- data/spec/runner_spec.rb +138 -0
- data/spec/spec_helper.rb +43 -0
- metadata +309 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module Emque
|
2
|
+
module Consuming
|
3
|
+
module Adapters
|
4
|
+
module RabbitMq
|
5
|
+
def self.default_options
|
6
|
+
{
|
7
|
+
:url => "amqp://guest:guest@localhost:5672",
|
8
|
+
:prefetch => nil,
|
9
|
+
:durable => true,
|
10
|
+
:auto_delete => false
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load
|
15
|
+
require_relative "rabbit_mq/manager"
|
16
|
+
require_relative "rabbit_mq/worker"
|
17
|
+
require_relative "rabbit_mq/retry_worker"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.manager
|
21
|
+
Emque::Consuming::Adapters::RabbitMq::Manager
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "emque/consuming/core"
|
2
|
+
require "emque/consuming/actor"
|
3
|
+
require "emque/consuming/consumer"
|
4
|
+
require "emque/consuming/error_tracker"
|
5
|
+
require "emque/consuming/message"
|
6
|
+
|
7
|
+
def emque_autoload(klass, file)
|
8
|
+
Kernel.autoload(klass, file)
|
9
|
+
end
|
10
|
+
|
11
|
+
module Emque
|
12
|
+
module Consuming
|
13
|
+
class ConfigurationError < StandardError; end
|
14
|
+
|
15
|
+
module Application
|
16
|
+
def self.included(descendant)
|
17
|
+
Emque::Consuming.application = descendant
|
18
|
+
|
19
|
+
descendant.class_eval do
|
20
|
+
extend Emque::Consuming::Core
|
21
|
+
include Emque::Consuming::Helpers
|
22
|
+
|
23
|
+
attr_reader :error_tracker, :manager
|
24
|
+
|
25
|
+
private :ensure_adapter_is_configured!, :initialize_error_tracker,
|
26
|
+
:initialize_manager, :log_prefix, :handle_shutdown
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
self.class.instance = self
|
32
|
+
|
33
|
+
logger.info "#{log_prefix}: initializing"
|
34
|
+
|
35
|
+
ensure_adapter_is_configured!
|
36
|
+
|
37
|
+
initialize_manager
|
38
|
+
initialize_error_tracker
|
39
|
+
end
|
40
|
+
|
41
|
+
def notice_error(context)
|
42
|
+
error_tracker.notice_error_for(context)
|
43
|
+
verify_error_status
|
44
|
+
end
|
45
|
+
|
46
|
+
def restart
|
47
|
+
stop
|
48
|
+
initialize_manager
|
49
|
+
error_tracker.occurrences.clear
|
50
|
+
start
|
51
|
+
end
|
52
|
+
|
53
|
+
def start
|
54
|
+
logger.info "#{log_prefix}: starting"
|
55
|
+
manager.async.start
|
56
|
+
end
|
57
|
+
|
58
|
+
def stop
|
59
|
+
logger.info "#{log_prefix}: stopping"
|
60
|
+
manager.stop
|
61
|
+
end
|
62
|
+
|
63
|
+
def verify_error_status
|
64
|
+
if error_tracker.limit_reached?
|
65
|
+
handle_shutdown
|
66
|
+
runner.stop
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# private
|
71
|
+
|
72
|
+
def ensure_adapter_is_configured!
|
73
|
+
if config.adapter.nil?
|
74
|
+
raise AdapterConfigurationError,
|
75
|
+
"Adapter not found! use config.set_adapter(name, options)"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_shutdown
|
80
|
+
context = {
|
81
|
+
:limit => error_tracker.limit,
|
82
|
+
:expiration => error_tracker.expiration,
|
83
|
+
:occurrences => error_tracker.occurrences,
|
84
|
+
:status => runner.status.to_h,
|
85
|
+
:configuration => config.to_h
|
86
|
+
}
|
87
|
+
|
88
|
+
Emque::Consuming.logger.error("Error limit exceeded... shutting down")
|
89
|
+
Emque::Consuming.logger.error(context)
|
90
|
+
|
91
|
+
Emque::Consuming.config.shutdown_handlers.each do |handler|
|
92
|
+
handler.call(context)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize_error_tracker
|
97
|
+
@error_tracker = Emque::Consuming::ErrorTracker.new(
|
98
|
+
:expiration => config.error_expiration,
|
99
|
+
:limit => config.error_limit
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize_manager
|
104
|
+
@manager = config.adapter.manager.new
|
105
|
+
end
|
106
|
+
|
107
|
+
def log_prefix
|
108
|
+
"#{config.app_name.capitalize} Application"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require "optparse"
|
2
|
+
require "emque/consuming"
|
3
|
+
require "emque/consuming/generators/application"
|
4
|
+
|
5
|
+
module Emque
|
6
|
+
module Consuming
|
7
|
+
class Cli
|
8
|
+
APP_CONFIG_FILE = "config/application.rb"
|
9
|
+
COMMANDS = [:console, :new, :start, :stop]
|
10
|
+
IP_REGEX = /^(\d{1,3}\.){3}\d{1,3}$/
|
11
|
+
|
12
|
+
attr_reader :options
|
13
|
+
|
14
|
+
def initialize(argv)
|
15
|
+
self.argv = argv
|
16
|
+
|
17
|
+
extract_command
|
18
|
+
intercept_help
|
19
|
+
|
20
|
+
load_app
|
21
|
+
setup_options
|
22
|
+
parse_options
|
23
|
+
|
24
|
+
execute
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :parser, :command
|
30
|
+
attr_accessor :argv, :runner
|
31
|
+
|
32
|
+
def execute
|
33
|
+
if command == :new
|
34
|
+
Emque::Consuming::Generators::Application.new(options, argv.last).generate
|
35
|
+
else
|
36
|
+
self.runner = Emque::Consuming::Runner.new(options)
|
37
|
+
runner.send(command)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_command
|
42
|
+
if argv.size > 1 and argv[-2] == "new"
|
43
|
+
@command = :new
|
44
|
+
elsif argv.size > 0
|
45
|
+
@command = argv[-1].to_sym
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def intercept_help
|
50
|
+
if command == :new and argv.last.to_sym == command
|
51
|
+
argv << "--help"
|
52
|
+
elsif ! COMMANDS.include?(command)
|
53
|
+
argv << "--help"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_app
|
58
|
+
current_dir = Dir.pwd
|
59
|
+
|
60
|
+
if File.exist?(File.join(current_dir, APP_CONFIG_FILE))
|
61
|
+
require_relative File.join(current_dir, APP_CONFIG_FILE)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_options
|
66
|
+
parser.parse!(argv)
|
67
|
+
end
|
68
|
+
|
69
|
+
def setup_options
|
70
|
+
@options = {
|
71
|
+
:daemon => false
|
72
|
+
}
|
73
|
+
|
74
|
+
@parser = OptionParser.new { |o|
|
75
|
+
o.on("-P", "--pidfile PATH", "Store pid in PATH") do |arg|
|
76
|
+
options[:pidfile] = arg
|
77
|
+
end
|
78
|
+
|
79
|
+
o.on(
|
80
|
+
"-S",
|
81
|
+
"--socket PATH",
|
82
|
+
"PATH to the application's unix socket"
|
83
|
+
) do |arg|
|
84
|
+
options[:socket_path] = arg
|
85
|
+
end
|
86
|
+
|
87
|
+
o.on(
|
88
|
+
"-b",
|
89
|
+
"--bind IP:PORT",
|
90
|
+
"IP & port for the http status application to listen on."
|
91
|
+
) do |arg|
|
92
|
+
ip, port = arg.split(":")
|
93
|
+
port = port.to_i
|
94
|
+
options[:status_host] = ip if ip =~ IP_REGEX
|
95
|
+
options[:status_port] = port if port > 0 && port <= 65535
|
96
|
+
end
|
97
|
+
|
98
|
+
o.on("-d", "--daemon", "Daemonize the application") do
|
99
|
+
options[:daemon] = true
|
100
|
+
end
|
101
|
+
|
102
|
+
o.on(
|
103
|
+
"-e",
|
104
|
+
"--error-limit N",
|
105
|
+
"Set the max errors before application suicide"
|
106
|
+
) do |arg|
|
107
|
+
limit = arg.to_i
|
108
|
+
options[:error_limit] = limit if limit > 0
|
109
|
+
end
|
110
|
+
|
111
|
+
o.on("-s", "--status", "Run the http status application") do
|
112
|
+
options[:status] = :on
|
113
|
+
end
|
114
|
+
|
115
|
+
o.on(
|
116
|
+
"-x",
|
117
|
+
"--error-expiration SECONDS",
|
118
|
+
"Expire errors after SECONDS"
|
119
|
+
) do |arg|
|
120
|
+
exp = arg.to_i
|
121
|
+
options[:error_expiration] = exp if exp > 0
|
122
|
+
end
|
123
|
+
|
124
|
+
o.on("--app-name NAME", "Run the application as NAME") do |arg|
|
125
|
+
options[:app_name] = arg
|
126
|
+
end
|
127
|
+
|
128
|
+
o.on(
|
129
|
+
"--env (ex. production)",
|
130
|
+
"Set the application environment, overrides EMQUE_ENV"
|
131
|
+
) do |arg|
|
132
|
+
options[:env] = arg
|
133
|
+
end
|
134
|
+
|
135
|
+
o.banner = "emque <options> (start|stop|new|console|help) <name (new only)>"
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Emque
|
2
|
+
module Consuming
|
3
|
+
module CommandReceivers
|
4
|
+
NotImplemented = Class.new(StandardError)
|
5
|
+
|
6
|
+
class Base
|
7
|
+
include Emque::Consuming::Helpers
|
8
|
+
|
9
|
+
def restart
|
10
|
+
stop if running?
|
11
|
+
start
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
raise NotImplemented
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
thread.exit if running?
|
20
|
+
status
|
21
|
+
end
|
22
|
+
|
23
|
+
def status
|
24
|
+
thread ? (thread.status || "stopped") : "stopped"
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :thread
|
30
|
+
|
31
|
+
def running?
|
32
|
+
thread && !thread.stop?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "puma/cli"
|
2
|
+
require "emque/consuming/command_receivers/base"
|
3
|
+
|
4
|
+
module Emque
|
5
|
+
module Consuming
|
6
|
+
module CommandReceivers
|
7
|
+
class HttpServer < Base
|
8
|
+
attr_accessor :puma
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
ENV["RACK_ENV"] = Emque::Consuming.application.emque_env
|
12
|
+
initialize_puma
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
puma.options[:app] = Handler.new
|
17
|
+
@thread = Thread.new { puma.run }
|
18
|
+
status
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def initialize_puma
|
24
|
+
self.puma =
|
25
|
+
Puma::CLI.new(
|
26
|
+
[],
|
27
|
+
Puma::Events.new(
|
28
|
+
Logger.new(:info),
|
29
|
+
Logger.new(:error)
|
30
|
+
)
|
31
|
+
)
|
32
|
+
|
33
|
+
puma.options[:binds] = [
|
34
|
+
"tcp://#{config.status_host}"+
|
35
|
+
":#{config.status_port}"
|
36
|
+
]
|
37
|
+
|
38
|
+
puma.define_singleton_method :set_process_title do
|
39
|
+
# we don't want puma to take over the process name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Handler
|
44
|
+
include Emque::Consuming::Helpers
|
45
|
+
|
46
|
+
def call(env)
|
47
|
+
req = env["REQUEST_URI"].split("/")
|
48
|
+
|
49
|
+
case req[1]
|
50
|
+
when "status"
|
51
|
+
return render_status
|
52
|
+
when "control"
|
53
|
+
case req[2]
|
54
|
+
when "errors"
|
55
|
+
if req[3..-1] && runner.control.errors(*req[3..-1]) == true
|
56
|
+
return render_status
|
57
|
+
end
|
58
|
+
else
|
59
|
+
if req[2].is_a?(String) &&
|
60
|
+
app.manager.workers.has_key?(req[2].to_sym) &&
|
61
|
+
runner.control.workers(*req[2..-1]) == true
|
62
|
+
return render_status
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
render_404
|
68
|
+
end
|
69
|
+
|
70
|
+
def render_404
|
71
|
+
[404, {}, ["Not Found"]]
|
72
|
+
end
|
73
|
+
|
74
|
+
def render_status(additional = {})
|
75
|
+
[
|
76
|
+
200,
|
77
|
+
{},
|
78
|
+
[
|
79
|
+
Oj.dump(
|
80
|
+
runner.status.to_hsh.merge(additional),
|
81
|
+
:mode => :compat
|
82
|
+
)
|
83
|
+
]
|
84
|
+
]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Logger
|
89
|
+
attr_accessor :sync, :method
|
90
|
+
|
91
|
+
def initialize(method)
|
92
|
+
self.method = method
|
93
|
+
end
|
94
|
+
|
95
|
+
def puts(str)
|
96
|
+
Emque::Consuming.logger.send(method, str)
|
97
|
+
end
|
98
|
+
alias :write :puts
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "emque/consuming/command_receivers/base"
|
3
|
+
|
4
|
+
module Emque
|
5
|
+
module Consuming
|
6
|
+
module CommandReceivers
|
7
|
+
class UnixSocket < Base
|
8
|
+
|
9
|
+
def start
|
10
|
+
@thread = new_socket_server
|
11
|
+
status
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def app_name
|
17
|
+
config.app_name.capitalize
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_socket_server
|
21
|
+
Thread.new {
|
22
|
+
loop do
|
23
|
+
Socket.unix_server_loop(config.socket_path) do |sock, client_addr|
|
24
|
+
begin
|
25
|
+
receive_command(sock)
|
26
|
+
rescue
|
27
|
+
# nothing to do but restart the socket
|
28
|
+
ensure
|
29
|
+
sock.close
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def receive_command(sock)
|
37
|
+
req = Oj.load(sock.recv(100000), :symbol_keys => true)
|
38
|
+
handler = Handler.new(req)
|
39
|
+
sock.send(handler.respond, 0)
|
40
|
+
rescue Oj::ParseError => e
|
41
|
+
sock.send(bad_request(handler), 0)
|
42
|
+
log_error(e)
|
43
|
+
rescue NoMethodError => e
|
44
|
+
sock.send(bad_request(handler), 0)
|
45
|
+
log_error(e)
|
46
|
+
rescue ArgumentError => e
|
47
|
+
sock.send(bad_request(handler), 0)
|
48
|
+
log_error(e)
|
49
|
+
rescue => e
|
50
|
+
sock.send(e.inspect, 0)
|
51
|
+
log_error(e)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def bad_request(handler)
|
57
|
+
<<-OUT
|
58
|
+
The request was not formatted properly.
|
59
|
+
We suggest using Emque::Consuming::Transmitter to send a requests.",
|
60
|
+
-------
|
61
|
+
#{handler.help rescue "Help broken"}
|
62
|
+
OUT
|
63
|
+
end
|
64
|
+
|
65
|
+
def log_error(e)
|
66
|
+
logger.error(e.inspect)
|
67
|
+
e.backtrace.each do |bt|
|
68
|
+
logger.error(bt)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Handler
|
73
|
+
include Emque::Consuming::Helpers
|
74
|
+
|
75
|
+
COMMANDS = [:configuration, :errors, :restart, :status, :stop]
|
76
|
+
|
77
|
+
def initialize(args:, command:)
|
78
|
+
self.args = args
|
79
|
+
self.command = command.to_sym
|
80
|
+
end
|
81
|
+
|
82
|
+
def help
|
83
|
+
<<-OUT
|
84
|
+
#{app_name} Help
|
85
|
+
|
86
|
+
# Information
|
87
|
+
|
88
|
+
configuration # current configuration of the application
|
89
|
+
help # this menu
|
90
|
+
status # current status of the application
|
91
|
+
|
92
|
+
# Control
|
93
|
+
|
94
|
+
errors clear # reset the error count to 0
|
95
|
+
errors down # decrease the acceptable error threshold by 1
|
96
|
+
errors expire_after <seconds> # changes the expiration time for future errors
|
97
|
+
errors up # increase the acceptable error threshold by 1
|
98
|
+
errors retry # Reprocesses all messages in the error queue
|
99
|
+
restart # restart all workers
|
100
|
+
stop # turn the application off
|
101
|
+
-------
|
102
|
+
OUT
|
103
|
+
end
|
104
|
+
|
105
|
+
def respond
|
106
|
+
if valid_request?
|
107
|
+
method(command).call(*args)
|
108
|
+
else
|
109
|
+
help
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
attr_accessor :args, :command
|
116
|
+
|
117
|
+
def app_name
|
118
|
+
config.app_name.capitalize
|
119
|
+
end
|
120
|
+
|
121
|
+
def configuration
|
122
|
+
<<-OUT
|
123
|
+
#{app_name} Config
|
124
|
+
-------
|
125
|
+
#{config.to_hsh.map { |label, value|
|
126
|
+
"#{label}: #{value.inspect}"
|
127
|
+
}.join("\n")}
|
128
|
+
-------
|
129
|
+
OUT
|
130
|
+
end
|
131
|
+
|
132
|
+
def errors(*args)
|
133
|
+
runner.control.errors(*args) == true ? status : help
|
134
|
+
end
|
135
|
+
|
136
|
+
def restart
|
137
|
+
runner.restart_application
|
138
|
+
"The application was successfully restarted"
|
139
|
+
end
|
140
|
+
|
141
|
+
def status
|
142
|
+
data = runner.status.to_hsh
|
143
|
+
<<-OUT
|
144
|
+
#{app_name} Status
|
145
|
+
-------
|
146
|
+
errors:
|
147
|
+
#{data[:errors].map { |attr, val|
|
148
|
+
" #{attr}: #{val}"
|
149
|
+
}.join("\n")}
|
150
|
+
workers:
|
151
|
+
#{data[:workers].map { |topic, settings|
|
152
|
+
" #{topic}: #{settings[:count]}"
|
153
|
+
}.join("\n")}
|
154
|
+
-------
|
155
|
+
OUT
|
156
|
+
end
|
157
|
+
|
158
|
+
def stop
|
159
|
+
runner.stop
|
160
|
+
end
|
161
|
+
|
162
|
+
def valid_request?
|
163
|
+
COMMANDS.include?(command)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Consuming
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :app_name, :adapter, :error_handlers, :error_limit,
|
7
|
+
:error_expiration, :status, :status_port, :status_host, :socket_path,
|
8
|
+
:shutdown_handlers
|
9
|
+
attr_writer :env, :log_level
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@app_name = ""
|
13
|
+
@error_handlers = []
|
14
|
+
@error_limit = 5
|
15
|
+
@error_expiration = 3600 # 60 minutes
|
16
|
+
@log_level = nil
|
17
|
+
@status_port = 10000
|
18
|
+
@status_host = "0.0.0.0"
|
19
|
+
@status = :off # :on
|
20
|
+
@socket_path = "tmp/emque.sock"
|
21
|
+
@shutdown_handlers = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def env
|
25
|
+
Emque::Consuming.application.emque_env
|
26
|
+
end
|
27
|
+
|
28
|
+
def env_var
|
29
|
+
@env
|
30
|
+
end
|
31
|
+
|
32
|
+
def log_level
|
33
|
+
@log_level ||= Logger::INFO
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_adapter(name, options = {})
|
37
|
+
@adapter = Emque::Consuming::Adapter.new(name, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_hsh
|
41
|
+
{}.tap { |config|
|
42
|
+
[
|
43
|
+
:app_name,
|
44
|
+
:adapter,
|
45
|
+
:env,
|
46
|
+
:error_handlers,
|
47
|
+
:error_limit,
|
48
|
+
:error_expiration,
|
49
|
+
:log_level,
|
50
|
+
:status_port,
|
51
|
+
:status_host,
|
52
|
+
:status,
|
53
|
+
:socket_path,
|
54
|
+
:shutdown_handlers
|
55
|
+
].each { |attr|
|
56
|
+
config[attr] = send(attr)
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
alias :to_h :to_hsh
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "pipe"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Consuming
|
5
|
+
def self.consumer
|
6
|
+
Module.new do
|
7
|
+
define_singleton_method(:included) do |descendant|
|
8
|
+
descendant.send(:include, ::Pipe)
|
9
|
+
descendant.send(:include, ::Emque::Consuming::Consumer::Common)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Consumer
|
15
|
+
module Common
|
16
|
+
def self.included(descendant)
|
17
|
+
descendant.class_eval do
|
18
|
+
attr_reader :message
|
19
|
+
private :handle_error, :pipe
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def consume(handler_method, message)
|
24
|
+
send(handler_method, message)
|
25
|
+
end
|
26
|
+
|
27
|
+
def pipe_config
|
28
|
+
@pipe_config ||= Pipe::Config.new(
|
29
|
+
:error_handlers => [method(:handle_error)],
|
30
|
+
:stop_on => ->(msg, _, _) { !(msg.respond_to?(:continue?) && msg.continue?) }
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def handle_error(e, method:, subject:)
|
35
|
+
context = {
|
36
|
+
:consumer => self.class.name,
|
37
|
+
:message => {
|
38
|
+
:current => subject.values,
|
39
|
+
:original => subject.original
|
40
|
+
},
|
41
|
+
:offset => subject.offset,
|
42
|
+
:partition => subject.partition,
|
43
|
+
:pipe_method => method,
|
44
|
+
:topic => subject.topic
|
45
|
+
}
|
46
|
+
|
47
|
+
# log the error by default
|
48
|
+
Emque::Consuming.logger.error("Error consuming message #{e}")
|
49
|
+
Emque::Consuming.logger.error(context)
|
50
|
+
Emque::Consuming.logger.error e.backtrace.join("\n") unless e.backtrace.nil?
|
51
|
+
|
52
|
+
Emque::Consuming.config.error_handlers.each do |handler|
|
53
|
+
handler.call(e, context)
|
54
|
+
end
|
55
|
+
|
56
|
+
Emque::Consuming.application.instance.notice_error(context)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|