aggro 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/.travis.yml +15 -0
- data/Gemfile +9 -0
- data/README.md +5 -1
- data/Rakefile +10 -0
- data/aggro.gemspec +8 -1
- data/lib/aggro.rb +191 -7
- data/lib/aggro/abstract_store.rb +12 -0
- data/lib/aggro/aggregate.rb +98 -0
- data/lib/aggro/aggregate_ref.rb +68 -6
- data/lib/aggro/attribute_dsl.rb +96 -0
- data/lib/aggro/binding_dsl.rb +45 -0
- data/lib/aggro/block_helper.rb +14 -0
- data/lib/aggro/channel.rb +37 -0
- data/lib/aggro/client.rb +12 -0
- data/lib/aggro/cluster_config.rb +57 -0
- data/lib/aggro/command.rb +16 -0
- data/lib/aggro/concurrent_actor.rb +26 -0
- data/lib/aggro/event_bus.rb +94 -0
- data/lib/aggro/event_dsl.rb +53 -0
- data/lib/aggro/event_proxy.rb +23 -0
- data/lib/aggro/event_serializer.rb +14 -0
- data/lib/aggro/file_store.rb +97 -0
- data/lib/aggro/file_store/reader.rb +21 -0
- data/lib/aggro/file_store/writer.rb +27 -0
- data/lib/aggro/handler/command.rb +60 -0
- data/lib/aggro/handler/create_aggregate.rb +42 -0
- data/lib/aggro/handler/get_events.rb +30 -0
- data/lib/aggro/handler/query.rb +60 -0
- data/lib/aggro/handler/start_saga.rb +56 -0
- data/lib/aggro/local_node.rb +28 -0
- data/lib/aggro/locator.rb +32 -0
- data/lib/aggro/message/ask.rb +16 -0
- data/lib/aggro/message/command.rb +36 -0
- data/lib/aggro/message/create_aggregate.rb +16 -0
- data/lib/aggro/message/endpoint.rb +16 -0
- data/lib/aggro/message/events.rb +24 -0
- data/lib/aggro/message/get_events.rb +16 -0
- data/lib/aggro/message/heartbeat.rb +16 -0
- data/lib/aggro/message/invalid_target.rb +20 -0
- data/lib/aggro/message/ok.rb +20 -0
- data/lib/aggro/message/publisher_endpoint_inquiry.rb +16 -0
- data/lib/aggro/message/query.rb +36 -0
- data/lib/aggro/message/result.rb +16 -0
- data/lib/aggro/message/start_saga.rb +28 -0
- data/lib/aggro/message/unhandled_operation.rb +20 -0
- data/lib/aggro/message/unknown_operation.rb +20 -0
- data/lib/aggro/message_parser.rb +10 -0
- data/lib/aggro/message_router.rb +26 -0
- data/lib/aggro/nanomsg_transport.rb +31 -0
- data/lib/aggro/nanomsg_transport/client.rb +35 -0
- data/lib/aggro/nanomsg_transport/connection.rb +98 -0
- data/lib/aggro/nanomsg_transport/publish.rb +17 -0
- data/lib/aggro/nanomsg_transport/publisher.rb +37 -0
- data/lib/aggro/nanomsg_transport/raw_reply.rb +18 -0
- data/lib/aggro/nanomsg_transport/raw_request.rb +18 -0
- data/lib/aggro/nanomsg_transport/reply.rb +17 -0
- data/lib/aggro/nanomsg_transport/request.rb +17 -0
- data/lib/aggro/nanomsg_transport/server.rb +84 -0
- data/lib/aggro/nanomsg_transport/socket_error.rb +20 -0
- data/lib/aggro/nanomsg_transport/subscribe.rb +27 -0
- data/lib/aggro/nanomsg_transport/subscriber.rb +82 -0
- data/lib/aggro/node.rb +29 -0
- data/lib/aggro/node_list.rb +39 -0
- data/lib/aggro/projection.rb +13 -0
- data/lib/aggro/query.rb +11 -0
- data/lib/aggro/saga.rb +94 -0
- data/lib/aggro/saga_runner.rb +87 -0
- data/lib/aggro/saga_runner/start_saga.rb +12 -0
- data/lib/aggro/saga_status.rb +29 -0
- data/lib/aggro/server.rb +88 -0
- data/lib/aggro/subscriber.rb +48 -0
- data/lib/aggro/subscription.rb +41 -0
- data/lib/aggro/transform/boolean.rb +16 -0
- data/lib/aggro/transform/email.rb +26 -0
- data/lib/aggro/transform/id.rb +34 -0
- data/lib/aggro/transform/integer.rb +22 -0
- data/lib/aggro/transform/money.rb +22 -0
- data/lib/aggro/transform/noop.rb +16 -0
- data/lib/aggro/transform/string.rb +16 -0
- data/lib/aggro/transform/time_interval.rb +24 -0
- data/lib/aggro/version.rb +1 -1
- data/spec/lib/aggro/abstract_store_spec.rb +15 -0
- data/spec/lib/aggro/aggregate_ref_spec.rb +63 -12
- data/spec/lib/aggro/aggregate_spec.rb +207 -0
- data/spec/lib/aggro/channel_spec.rb +87 -0
- data/spec/lib/aggro/client_spec.rb +26 -0
- data/spec/lib/aggro/cluster_config_spec.rb +33 -0
- data/spec/lib/aggro/command_spec.rb +52 -0
- data/spec/lib/aggro/concurrent_actor_spec.rb +44 -0
- data/spec/lib/aggro/event_bus_spec.rb +20 -0
- data/spec/lib/aggro/event_serializer_spec.rb +28 -0
- data/spec/lib/aggro/file_store/reader_spec.rb +32 -0
- data/spec/lib/aggro/file_store/writer_spec.rb +67 -0
- data/spec/lib/aggro/file_store_spec.rb +51 -0
- data/spec/lib/aggro/handler/command_spec.rb +78 -0
- data/spec/lib/aggro/handler/create_aggregate_spec.rb +64 -0
- data/spec/lib/aggro/handler/get_events_handler_spec.rb +45 -0
- data/spec/lib/aggro/handler/query_spec.rb +78 -0
- data/spec/lib/aggro/handler/start_saga_spec.rb +64 -0
- data/spec/lib/aggro/local_node_spec.rb +52 -0
- data/spec/lib/aggro/locator_spec.rb +61 -0
- data/spec/lib/aggro/message/ask_spec.rb +23 -0
- data/spec/lib/aggro/message/command_spec.rb +50 -0
- data/spec/lib/aggro/message/create_aggregate_spec.rb +28 -0
- data/spec/lib/aggro/message/endpoint_spec.rb +23 -0
- data/spec/lib/aggro/message/events_spec.rb +37 -0
- data/spec/lib/aggro/message/get_events_spec.rb +33 -0
- data/spec/lib/aggro/message/heartbeat_spec.rb +23 -0
- data/spec/lib/aggro/message/invalid_target_spec.rb +28 -0
- data/spec/lib/aggro/message/ok_spec.rb +27 -0
- data/spec/lib/aggro/message/publisher_endpoint_inquiry_spec.rb +23 -0
- data/spec/lib/aggro/message/query_spec.rb +50 -0
- data/spec/lib/aggro/message/start_saga_spec.rb +37 -0
- data/spec/lib/aggro/message/unhandled_operation_spec.rb +28 -0
- data/spec/lib/aggro/message/unknown_operation_spec.rb +28 -0
- data/spec/lib/aggro/message_parser_spec.rb +16 -0
- data/spec/lib/aggro/message_router_spec.rb +35 -0
- data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +21 -0
- data/spec/lib/aggro/nanomsg_transport_spec.rb +37 -0
- data/spec/lib/aggro/node_list_spec.rb +38 -0
- data/spec/lib/aggro/node_spec.rb +44 -0
- data/spec/lib/aggro/projection_spec.rb +22 -0
- data/spec/lib/aggro/query_spec.rb +47 -0
- data/spec/lib/aggro/saga_runner_spec.rb +84 -0
- data/spec/lib/aggro/saga_spec.rb +126 -0
- data/spec/lib/aggro/saga_status_spec.rb +56 -0
- data/spec/lib/aggro/server_spec.rb +118 -0
- data/spec/lib/aggro/subscriber_spec.rb +59 -0
- data/spec/lib/aggro/subscription_spec.rb +50 -0
- data/spec/lib/aggro/transform/boolean_spec.rb +23 -0
- data/spec/lib/aggro/transform/email_spec.rb +13 -0
- data/spec/lib/aggro/transform/id_spec.rb +70 -0
- data/spec/lib/aggro/transform/integer_spec.rb +30 -0
- data/spec/lib/aggro/transform/money_spec.rb +34 -0
- data/spec/lib/aggro/transform/string_spec.rb +15 -0
- data/spec/lib/aggro/transform/time_interval_spec.rb +29 -0
- data/spec/lib/aggro_spec.rb +63 -19
- data/spec/spec_helper.rb +21 -2
- metadata +283 -3
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/request'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Public: Client for making requests against a nanomsg server.
|
6
|
+
class Client
|
7
|
+
def initialize(endpoint)
|
8
|
+
ObjectSpace.define_finalizer self, method(:close_socket)
|
9
|
+
|
10
|
+
@endpoint = endpoint
|
11
|
+
end
|
12
|
+
|
13
|
+
def post(message)
|
14
|
+
request_socket.send_msg message
|
15
|
+
|
16
|
+
request_socket.recv_msg
|
17
|
+
end
|
18
|
+
|
19
|
+
def close_socket
|
20
|
+
request_socket.terminate if @open
|
21
|
+
@request_socket = nil
|
22
|
+
@open = false
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def request_socket
|
28
|
+
@request_socket ||= begin
|
29
|
+
@open = true
|
30
|
+
Request.new(@endpoint)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'nn-core'
|
3
|
+
require 'aggro/nanomsg_transport/socket_error'
|
4
|
+
|
5
|
+
module Aggro
|
6
|
+
module NanomsgTransport
|
7
|
+
# Private: Base class for nanomsg socket wrappers.
|
8
|
+
class Connection
|
9
|
+
attr_reader :socket
|
10
|
+
|
11
|
+
def initialize(endpoint)
|
12
|
+
@endpoint = endpoint
|
13
|
+
@rcv_buffer = FFI::MemoryPointer.new(:pointer)
|
14
|
+
|
15
|
+
allocate_socket
|
16
|
+
setup_socket
|
17
|
+
set_endpoint
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_socket
|
21
|
+
set_socket_option NNCore::NN_LINGER, 100
|
22
|
+
set_socket_option NNCore::NN_SNDBUF, 131_072
|
23
|
+
set_socket_option NNCore::NN_RCVBUF, 131_072
|
24
|
+
end
|
25
|
+
|
26
|
+
def send_msg(stringable)
|
27
|
+
msg = stringable.to_s
|
28
|
+
nbytes = NNCore::LibNanomsg.nn_send(@socket, msg, msg.bytesize, 0)
|
29
|
+
assert(nbytes)
|
30
|
+
|
31
|
+
nbytes
|
32
|
+
end
|
33
|
+
|
34
|
+
def recv_fd
|
35
|
+
get_socket_option NNCore::NN_RCVFD
|
36
|
+
end
|
37
|
+
|
38
|
+
def recv_msg
|
39
|
+
nbytes = NNCore::LibNanomsg.nn_recv(@socket, @rcv_buffer,
|
40
|
+
NNCore::NN_MSG, 0)
|
41
|
+
|
42
|
+
assert(nbytes)
|
43
|
+
|
44
|
+
str = @rcv_buffer.read_pointer
|
45
|
+
response = str.read_string(nbytes)
|
46
|
+
NNCore::LibNanomsg.nn_freemsg str
|
47
|
+
|
48
|
+
response
|
49
|
+
rescue SocketError => e
|
50
|
+
raise e unless e.errno == NNCore::EAGAIN
|
51
|
+
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def terminate
|
56
|
+
assert NNCore::LibNanomsg.nn_close(@socket)
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def assert(rc)
|
62
|
+
fail SocketError.new NNCore::LibNanomsg.nn_errno unless rc >= 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_socket_option(setting, level = NNCore::NN_SOL_SOCKET)
|
66
|
+
result = FFI::MemoryPointer.new(:int32)
|
67
|
+
size = FFI::MemoryPointer.new(:size_t)
|
68
|
+
size.write_int result.size
|
69
|
+
|
70
|
+
rc = NNCore::LibNanomsg.nn_getsockopt(@socket, level, setting,
|
71
|
+
result, size)
|
72
|
+
|
73
|
+
assert(rc)
|
74
|
+
|
75
|
+
result.read_int
|
76
|
+
end
|
77
|
+
|
78
|
+
def prepare_socket_option(value)
|
79
|
+
if value.is_a? String
|
80
|
+
[value, value.bytesize]
|
81
|
+
else
|
82
|
+
option = FFI::MemoryPointer.new(:int32)
|
83
|
+
option.write_int(value)
|
84
|
+
|
85
|
+
[option, 4]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_socket_option(setting, value, level = NNCore::NN_SOL_SOCKET)
|
90
|
+
option, option_length = prepare_socket_option(value)
|
91
|
+
|
92
|
+
rc = NNCore::LibNanomsg.nn_setsockopt(@socket, level, setting,
|
93
|
+
option, option_length)
|
94
|
+
assert(rc)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/connection'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Private: Wrapper for a nanomsg PUB node.
|
6
|
+
class Publish < Connection
|
7
|
+
def allocate_socket
|
8
|
+
@socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_PUB)
|
9
|
+
assert @socket
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_endpoint
|
13
|
+
assert NNCore::LibNanomsg.nn_bind(@socket, @endpoint)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/publish'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Public: Handles publishing messages on a given endpoint.
|
6
|
+
class Publisher
|
7
|
+
def initialize(endpoint)
|
8
|
+
ObjectSpace.define_finalizer self, method(:close_socket)
|
9
|
+
|
10
|
+
@endpoint = endpoint
|
11
|
+
end
|
12
|
+
|
13
|
+
def close_socket
|
14
|
+
pub_socket.terminate if @open
|
15
|
+
@pub_socket = nil
|
16
|
+
@open = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def open_socket
|
20
|
+
return @pub_socket if @open
|
21
|
+
|
22
|
+
@open = true
|
23
|
+
@pub_socket = Publish.new(@endpoint)
|
24
|
+
end
|
25
|
+
|
26
|
+
def publish(message)
|
27
|
+
pub_socket.send_msg message
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def pub_socket
|
33
|
+
@pub_socket || open_socket
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/connection'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Private: Wrapper for a nanomsg XREP node.
|
6
|
+
class RawReply < Connection
|
7
|
+
def allocate_socket
|
8
|
+
@socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP_RAW,
|
9
|
+
NNCore::NN_REP)
|
10
|
+
assert @socket
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_endpoint
|
14
|
+
assert NNCore::LibNanomsg.nn_bind(@socket, @endpoint)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/connection'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Private: Wrapper for a nanomsg XREQ node.
|
6
|
+
class RawRequest < Connection
|
7
|
+
def allocate_socket
|
8
|
+
@socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP_RAW,
|
9
|
+
NNCore::NN_REQ)
|
10
|
+
assert @socket
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_endpoint
|
14
|
+
assert NNCore::LibNanomsg.nn_bind(@socket, @endpoint)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/connection'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Private: Wrapper for a nanomsg REP node.
|
6
|
+
class Reply < Connection
|
7
|
+
def allocate_socket
|
8
|
+
@socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_REP)
|
9
|
+
assert @socket
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_endpoint
|
13
|
+
assert NNCore::LibNanomsg.nn_connect(@socket, @endpoint)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/connection'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Private: Wrapper for a nanomsg REQ node.
|
6
|
+
class Request < Connection
|
7
|
+
def allocate_socket
|
8
|
+
@socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_REQ)
|
9
|
+
assert @socket
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_endpoint
|
13
|
+
assert NNCore::LibNanomsg.nn_connect(@socket, @endpoint)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'nio'
|
2
|
+
|
3
|
+
require 'aggro/nanomsg_transport/raw_reply'
|
4
|
+
require 'aggro/nanomsg_transport/raw_request'
|
5
|
+
require 'aggro/nanomsg_transport/reply'
|
6
|
+
|
7
|
+
module Aggro
|
8
|
+
module NanomsgTransport
|
9
|
+
# Public: Server to handle messages from nanomsg clients.
|
10
|
+
class Server
|
11
|
+
DEFAULT_WORKER_COUNT = 16
|
12
|
+
|
13
|
+
class ServerAlreadyRunning < RuntimeError; end
|
14
|
+
|
15
|
+
def initialize(endpoint, callable = nil, &block)
|
16
|
+
fail ArgumentError unless callable || block_given?
|
17
|
+
|
18
|
+
@callable = block_given? ? block : callable
|
19
|
+
@endpoint = endpoint
|
20
|
+
@selectors = DEFAULT_WORKER_COUNT.times.map { NIO::Selector.new }
|
21
|
+
@inproc_endpoint = "inproc://aggro-server-#{SecureRandom.hex}"
|
22
|
+
|
23
|
+
ObjectSpace.define_finalizer self, method(:stop)
|
24
|
+
end
|
25
|
+
|
26
|
+
def start
|
27
|
+
fail ServerAlreadyRunning if @running
|
28
|
+
|
29
|
+
@running = true
|
30
|
+
start_master
|
31
|
+
DEFAULT_WORKER_COUNT.times { |i| start_worker i }
|
32
|
+
|
33
|
+
sleep 0.01 while @selectors.any?(&:empty?)
|
34
|
+
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
return self unless @running
|
40
|
+
|
41
|
+
@running = false
|
42
|
+
@selectors.each(&:wakeup)
|
43
|
+
|
44
|
+
sleep 0.01 until @selectors.any?(&:empty?)
|
45
|
+
|
46
|
+
@raw_reply.terminate
|
47
|
+
@raw_request.terminate
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def handle_request(socket)
|
55
|
+
message = socket.recv_msg
|
56
|
+
socket.send_msg @callable.call(message) if @running
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_master
|
60
|
+
@master_thread = Concurrent::SingleThreadExecutor.new.post do
|
61
|
+
@raw_reply = RawReply.new(@endpoint)
|
62
|
+
@raw_request = RawRequest.new(@inproc_endpoint)
|
63
|
+
|
64
|
+
NNCore::LibNanomsg.nn_device @raw_request.socket, @raw_reply.socket
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def start_worker(i)
|
69
|
+
Concurrent::SingleThreadExecutor.new.post do
|
70
|
+
reply = Reply.new(@inproc_endpoint)
|
71
|
+
io = IO.new(reply.recv_fd, 'rb', autoclose: false)
|
72
|
+
|
73
|
+
@selectors[i].register io, :r
|
74
|
+
|
75
|
+
@selectors[i].select { handle_request(reply) } while @running
|
76
|
+
|
77
|
+
@selectors[i].deregister io
|
78
|
+
io.close
|
79
|
+
reply.terminate
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Aggro
|
2
|
+
module NanomsgTransport
|
3
|
+
# Public: An error calling the nanomsg API.
|
4
|
+
class SocketError < RuntimeError
|
5
|
+
attr_reader :errno
|
6
|
+
|
7
|
+
def initialize(errno)
|
8
|
+
@errno = errno
|
9
|
+
end
|
10
|
+
|
11
|
+
def error_message
|
12
|
+
NNCore::LibNanomsg.nn_strerror(errno)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"Last nanomsg API call failed with '#{error_message}'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/connection'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Private: Wrapper for a nanomsg SUB node.
|
6
|
+
class Subscribe < Connection
|
7
|
+
def add_subscription(topic)
|
8
|
+
set_socket_option NNCore::NN_SUB_SUBSCRIBE, topic, NNCore::NN_SUB
|
9
|
+
end
|
10
|
+
|
11
|
+
def allocate_socket
|
12
|
+
@socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_SUB)
|
13
|
+
assert @socket
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_endpoint
|
17
|
+
assert NNCore::LibNanomsg.nn_connect(@socket, @endpoint)
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_socket
|
21
|
+
super
|
22
|
+
|
23
|
+
set_socket_option NNCore::NN_RCVTIMEO, 100
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'aggro/nanomsg_transport/subscribe'
|
2
|
+
|
3
|
+
module Aggro
|
4
|
+
module NanomsgTransport
|
5
|
+
# Public: Handles subscribing to messages on a given endpoint.
|
6
|
+
class Subscriber
|
7
|
+
class SubscriberAlreadyRunning < RuntimeError; end
|
8
|
+
|
9
|
+
def initialize(endpoint, callable = nil, &block)
|
10
|
+
fail ArgumentError unless callable || block_given?
|
11
|
+
|
12
|
+
@callable = block_given? ? block : callable
|
13
|
+
@endpoint = endpoint
|
14
|
+
@mutex = Mutex.new
|
15
|
+
@selector = NIO::Selector.new
|
16
|
+
|
17
|
+
ObjectSpace.define_finalizer self, method(:stop)
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_subscription(topic)
|
21
|
+
start unless @running
|
22
|
+
|
23
|
+
@mutex.synchronize do
|
24
|
+
sub_socket.add_subscription(topic)
|
25
|
+
end
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def start
|
31
|
+
@mutex.synchronize do
|
32
|
+
return self if @running
|
33
|
+
|
34
|
+
@running = true
|
35
|
+
start_on_thread
|
36
|
+
|
37
|
+
sleep 0.01 while @selector.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def stop
|
44
|
+
@mutex.synchronize do
|
45
|
+
return self unless @running
|
46
|
+
|
47
|
+
@running = false
|
48
|
+
@selector.wakeup
|
49
|
+
|
50
|
+
sleep 0.01 until @selector.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def handle_message
|
59
|
+
message = sub_socket.recv_msg
|
60
|
+
@callable.call(message) if message
|
61
|
+
end
|
62
|
+
|
63
|
+
def sub_socket
|
64
|
+
@sub_socket ||= Subscribe.new(@endpoint)
|
65
|
+
end
|
66
|
+
|
67
|
+
def start_on_thread
|
68
|
+
Concurrent::SingleThreadExecutor.new.post do
|
69
|
+
io = IO.new(sub_socket.recv_fd, 'rb', autoclose: false)
|
70
|
+
@selector.register io, :r
|
71
|
+
|
72
|
+
@selector.select { handle_message } while @running
|
73
|
+
|
74
|
+
@selector.deregister io
|
75
|
+
io.close
|
76
|
+
sub_socket.terminate
|
77
|
+
@sub_socket = nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|