moleculer 0.1.0
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 +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +44 -0
- data/.travis.yml +23 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +57 -0
- data/LICENSE.txt +21 -0
- data/README.md +59 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/benchmark_server.rb +21 -0
- data/examples/client-server/client.rb +13 -0
- data/examples/client-server/server.rb +25 -0
- data/lib/moleculer/broker.rb +318 -0
- data/lib/moleculer/configuration.rb +109 -0
- data/lib/moleculer/context.rb +24 -0
- data/lib/moleculer/errors/action_not_found.rb +6 -0
- data/lib/moleculer/errors/invalid_action_response.rb +11 -0
- data/lib/moleculer/errors/local_node_already_registered.rb +6 -0
- data/lib/moleculer/errors/node_not_found.rb +6 -0
- data/lib/moleculer/errors/transporter_already_started.rb +6 -0
- data/lib/moleculer/node.rb +105 -0
- data/lib/moleculer/packets/base.rb +47 -0
- data/lib/moleculer/packets/disconnect.rb +10 -0
- data/lib/moleculer/packets/discover.rb +10 -0
- data/lib/moleculer/packets/event.rb +37 -0
- data/lib/moleculer/packets/heartbeat.rb +18 -0
- data/lib/moleculer/packets/info.rb +97 -0
- data/lib/moleculer/packets/req.rb +57 -0
- data/lib/moleculer/packets/res.rb +43 -0
- data/lib/moleculer/packets.rb +25 -0
- data/lib/moleculer/registry.rb +325 -0
- data/lib/moleculer/serializers/json.rb +23 -0
- data/lib/moleculer/serializers.rb +10 -0
- data/lib/moleculer/service/action.rb +60 -0
- data/lib/moleculer/service/base.rb +117 -0
- data/lib/moleculer/service/event.rb +50 -0
- data/lib/moleculer/service/remote.rb +80 -0
- data/lib/moleculer/service.rb +2 -0
- data/lib/moleculer/support/hash_util.rb +48 -0
- data/lib/moleculer/support/log_proxy.rb +67 -0
- data/lib/moleculer/support/open_struct.rb +15 -0
- data/lib/moleculer/support/string_util.rb +25 -0
- data/lib/moleculer/support.rb +4 -0
- data/lib/moleculer/transporters/redis.rb +207 -0
- data/lib/moleculer/transporters.rb +22 -0
- data/lib/moleculer/version.rb +3 -0
- data/lib/moleculer.rb +103 -0
- data/moleculer.gemspec +50 -0
- metadata +238 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
require_relative "action"
|
2
|
+
require_relative "event"
|
3
|
+
|
4
|
+
module Moleculer
|
5
|
+
module Service
|
6
|
+
##
|
7
|
+
# @abstract subclass to define a local service.
|
8
|
+
class Base
|
9
|
+
class << self
|
10
|
+
# @!attribute [rw] service_prefix
|
11
|
+
# @return [string] a prefix to add to the service name. The service_prefix is inherited from the parent class
|
12
|
+
# if it is not already defined in the current class.
|
13
|
+
attr_writer :service_prefix
|
14
|
+
|
15
|
+
##
|
16
|
+
# The broker this service is attached to
|
17
|
+
attr_accessor :broker
|
18
|
+
|
19
|
+
def service_prefix
|
20
|
+
Moleculer.service_prefix
|
21
|
+
end
|
22
|
+
|
23
|
+
def node
|
24
|
+
broker.local_node
|
25
|
+
end
|
26
|
+
|
27
|
+
def broker
|
28
|
+
@broker || Moleculer.broker
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Set the service_name to the provided name. If the node is local it will prefix the service name with the
|
33
|
+
# service prefix
|
34
|
+
#
|
35
|
+
# @param name [String] the name to which the service_name should be set
|
36
|
+
def service_name(name = nil)
|
37
|
+
@service_name = name if name
|
38
|
+
|
39
|
+
return "#{broker.service_prefix}.#{@service_name}" unless broker.service_prefix.nil?
|
40
|
+
|
41
|
+
@service_name
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Defines an action on the service.
|
46
|
+
#
|
47
|
+
# @param name [String|Symbol] the name of the action.
|
48
|
+
# @param method [Symbol] the method to which the action maps.
|
49
|
+
# @param options [Hash] the action options.
|
50
|
+
# @option options [Boolean|Hash] :cache if true, will use default caching options, if a hash is provided caching
|
51
|
+
# options will reflect the hash.
|
52
|
+
# @option options [Hash] params list of param and param types. Can be used to coerce specific params to the
|
53
|
+
# provided type.
|
54
|
+
def action(name, method, options = {})
|
55
|
+
actions[action_name_for(name)] = Action.new(name, self, method, options)
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Defines an event on the service.
|
60
|
+
#
|
61
|
+
# @param name [String|Symbol] the name of the event.
|
62
|
+
# @param method [Symbol] the method to which the event maps.
|
63
|
+
# @param options [Hash] event options.
|
64
|
+
# @option options [Hash] :group the group in which the event should belong, defaults to the service_name
|
65
|
+
def event(name, method, options = {})
|
66
|
+
events[name] = Event.new(name, self, method, options)
|
67
|
+
end
|
68
|
+
|
69
|
+
def actions
|
70
|
+
@actions ||= {}
|
71
|
+
end
|
72
|
+
|
73
|
+
def events
|
74
|
+
@events ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def action_name_for(name)
|
78
|
+
"#{service_name}.#{name}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(broker)
|
83
|
+
@broker = broker
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# returns the action defined on the service class
|
88
|
+
# @see action
|
89
|
+
def actions
|
90
|
+
self.class.actions
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# returns the events defined on the service class
|
95
|
+
# @see events
|
96
|
+
def events
|
97
|
+
self.class.events
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# @return [Moleculer::Broker] the moleculer broker the service is attached to
|
102
|
+
def broker
|
103
|
+
self.class.broker
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.as_json
|
107
|
+
{
|
108
|
+
name: service_name,
|
109
|
+
settings: {},
|
110
|
+
metadata: {},
|
111
|
+
actions: Hash[actions.values.map { |a| [a.name.to_sym, a.as_json] }],
|
112
|
+
events: Hash[events.values.map { |e| [e.name.to_sym, e.as_json] }],
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative "../errors/invalid_action_response"
|
2
|
+
require_relative "../support"
|
3
|
+
module Moleculer
|
4
|
+
module Service
|
5
|
+
##
|
6
|
+
# Represents a service event.
|
7
|
+
class Event
|
8
|
+
# @!attribute [r] name
|
9
|
+
# @return [String] the name of the action
|
10
|
+
# @!attribute [r] service
|
11
|
+
# @return [Moleculer::Service] the service that this event is tied to
|
12
|
+
attr_reader :name, :service
|
13
|
+
|
14
|
+
##
|
15
|
+
# @param name [String] the name of the action
|
16
|
+
# @param service [Moleculer::Service] the service to which the action belongs
|
17
|
+
# @param method [Symbol] the method which the event calls when executed
|
18
|
+
# @param options [Hash] the method options
|
19
|
+
# TODO: add ability to group events
|
20
|
+
def initialize(name, service, method, options = {})
|
21
|
+
@name = name
|
22
|
+
@service = service
|
23
|
+
@method = method
|
24
|
+
@service = service
|
25
|
+
@options = options
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Executes the event
|
30
|
+
# @param data [Hash] the event data
|
31
|
+
def execute(data, broker)
|
32
|
+
@service.new(broker).public_send(@method, data)
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# @return [Moleculer::Node] the node of the service this event is tied to
|
37
|
+
def node
|
38
|
+
@service.node
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# @return [Hash] a hash representing this event as it would be in JSON
|
43
|
+
def as_json
|
44
|
+
{
|
45
|
+
name: name,
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Moleculer
|
2
|
+
##
|
3
|
+
# @private
|
4
|
+
module Service
|
5
|
+
##
|
6
|
+
# Creates a service instance from remote service info
|
7
|
+
#
|
8
|
+
# @param [Hash] service_info remote service information
|
9
|
+
def self.from_remote_info(service_info, service_node)
|
10
|
+
Class.new(Remote) do
|
11
|
+
service_name Support::HashUtil.fetch(service_info, :name)
|
12
|
+
fetch_actions(service_info)
|
13
|
+
fetch_events(service_info)
|
14
|
+
node service_node
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Represents a remote service
|
20
|
+
class Remote < Base
|
21
|
+
class << self
|
22
|
+
def node(n = nil)
|
23
|
+
@node = n if n
|
24
|
+
@node
|
25
|
+
end
|
26
|
+
|
27
|
+
def service_name(name = nil)
|
28
|
+
@service_name = name if name
|
29
|
+
|
30
|
+
@service_name
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def action_name_for(name)
|
36
|
+
name
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_actions(service_info)
|
40
|
+
seq = 0
|
41
|
+
Support::HashUtil.fetch(service_info, :actions).values.each do |a|
|
42
|
+
next if Support::HashUtil.fetch(a, :name) =~ /^\$/
|
43
|
+
|
44
|
+
define_method("action_#{seq}".to_sym) do |ctx|
|
45
|
+
@broker.send(:publish_req,
|
46
|
+
id: ctx.id,
|
47
|
+
action: ctx.action.name,
|
48
|
+
params: ctx.params,
|
49
|
+
meta: ctx.meta,
|
50
|
+
timeout: ctx.timeout,
|
51
|
+
node: self.class.node,
|
52
|
+
request_id: ctx.request_id,
|
53
|
+
stream: false)
|
54
|
+
{}
|
55
|
+
end
|
56
|
+
action(Support::HashUtil.fetch(a, :name), "action_#{seq}".to_sym)
|
57
|
+
seq += 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def fetch_events(service_info)
|
62
|
+
seq = 0
|
63
|
+
Support::HashUtil.fetch(service_info, :events).values.each do |a|
|
64
|
+
name = Support::HashUtil.fetch(a, :name)
|
65
|
+
define_method("event_#{seq}".to_sym) do |data|
|
66
|
+
@broker.send(:publish_event,
|
67
|
+
event: name,
|
68
|
+
data: data,
|
69
|
+
broadcast: false,
|
70
|
+
groups: [],
|
71
|
+
node: self.class.node)
|
72
|
+
end
|
73
|
+
event(name, "event_#{seq}".to_sym)
|
74
|
+
seq += 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative "string_util"
|
2
|
+
|
3
|
+
module Moleculer
|
4
|
+
module Support
|
5
|
+
##
|
6
|
+
# A module of functional methods for working with hashes
|
7
|
+
module HashUtil
|
8
|
+
extend self
|
9
|
+
##
|
10
|
+
# Works like fetch, but instead indifferently uses strings and symbols. It will try both snake case and camel
|
11
|
+
# case versions of the key.
|
12
|
+
#
|
13
|
+
# @param hash [Hash] the hash to fetch from
|
14
|
+
# @param key [Object] the key to fetch from the hash. This uses Hash#fetch internally, and if a string or synbol
|
15
|
+
# is passed the hash will use an indifferent fetch
|
16
|
+
# @param default [Object] the a fallback default if fetch fails. If not provided an exception will be raised if
|
17
|
+
# key cannot be found
|
18
|
+
#
|
19
|
+
# @return [Object] the value at the given key
|
20
|
+
def fetch(hash, key, default = :__no_default__)
|
21
|
+
return fetch_with_string(hash, key, default) if key.is_a?(String) || key.is_a?(Symbol)
|
22
|
+
return hash.fetch(key, default) if default != :__no_default__
|
23
|
+
|
24
|
+
hash.fetch(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def fetch_with_string(hash, key, default)
|
30
|
+
ret = get_camel(hash, key).nil? ? get_underscore(hash, key) : get_camel(hash, key)
|
31
|
+
return default if default != :__no_default__ && ret.nil?
|
32
|
+
raise KeyError, %(key not found: "#{key}") if ret.nil?
|
33
|
+
|
34
|
+
ret
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_camel(hash, key)
|
38
|
+
camelized = StringUtil.camelize(key.to_s)
|
39
|
+
hash[camelized].nil? ? hash[camelized.to_sym] : hash[camelized]
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_underscore(hash, key)
|
43
|
+
underscored = StringUtil.underscore(key.to_s)
|
44
|
+
hash[underscored].nil? ? hash[underscored.to_sym] : hash[underscored]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "ap"
|
2
|
+
|
3
|
+
module Moleculer
|
4
|
+
module Support
|
5
|
+
##
|
6
|
+
# LogProxy acts as a bridge between multiple logger types.
|
7
|
+
# @private
|
8
|
+
class LogProxy
|
9
|
+
def initialize(logger, prefix = "")
|
10
|
+
@logger = logger
|
11
|
+
@prefix = prefix
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# @return [Moleculer::LogProxy] a prefixed child of the log proxy
|
16
|
+
def get_child(prefix)
|
17
|
+
self.class.new(@logger, prefix)
|
18
|
+
end
|
19
|
+
|
20
|
+
def fatal(*args)
|
21
|
+
@logger.fatal(parse_args(args.unshift(@prefix))) if @logger
|
22
|
+
end
|
23
|
+
|
24
|
+
def error(*args)
|
25
|
+
@logger.error(parse_args(args.unshift(@prefix))) if @logger
|
26
|
+
end
|
27
|
+
|
28
|
+
def warn(*args)
|
29
|
+
@logger.warn(parse_args(args.unshift(@prefix))) if @logger
|
30
|
+
end
|
31
|
+
|
32
|
+
def info(*args)
|
33
|
+
@logger.info(parse_args(args.unshift(@prefix))) if @logger
|
34
|
+
end
|
35
|
+
|
36
|
+
def debug(*args)
|
37
|
+
@logger.debug(parse_args(args.unshift(@prefix))) if @logger
|
38
|
+
end
|
39
|
+
|
40
|
+
def trace(*args)
|
41
|
+
return unless @logger
|
42
|
+
|
43
|
+
return @logger.trace(parse_args(args.unshift(@prefix))) if @logger.respond_to?(:trace)
|
44
|
+
|
45
|
+
@logger.debug(parse_args(args.unshift(@prefix)))
|
46
|
+
end
|
47
|
+
|
48
|
+
def level
|
49
|
+
@logger.level
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def parse_args(args)
|
55
|
+
if args.last.is_a?(Hash)
|
56
|
+
args.each_index do |v|
|
57
|
+
args[v] = "\n" + args[v].ai + "\n" unless args[v].is_a?(String)
|
58
|
+
end
|
59
|
+
elsif args.last.is_a?(StandardError)
|
60
|
+
args = [args.slice(0, 1), args.last.message, "\n ", args.last.backtrace.join("\n ")].flatten
|
61
|
+
end
|
62
|
+
|
63
|
+
args.join(" ").strip
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
module Moleculer
|
4
|
+
module Support
|
5
|
+
##
|
6
|
+
# An OpenStruct that supports camelized serialization for JSON
|
7
|
+
class OpenStruct < ::OpenStruct
|
8
|
+
##
|
9
|
+
# @return [Hash] the object prepared for conversion to JSON for transmission
|
10
|
+
def as_json
|
11
|
+
Hash[to_h.map { |item| [StringUtil.camelize(item[0]), item[1]] }]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Moleculer
|
2
|
+
module Support
|
3
|
+
##
|
4
|
+
# A module of functional methods for working with strings.
|
5
|
+
module StringUtil
|
6
|
+
extend self
|
7
|
+
##
|
8
|
+
# Converts a string to lowerCamelCase.
|
9
|
+
#
|
10
|
+
# @param term [String] the term to convert
|
11
|
+
def camelize(term)
|
12
|
+
new_term = term.gsub(/(?:^|_)([a-z])/) { $1.upcase }
|
13
|
+
new_term[0..1].downcase + new_term[2..-1]
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Makes an underscored, lowercase form from the expression in the string.
|
18
|
+
#
|
19
|
+
# @param term[String] the word to convert into an underscored string
|
20
|
+
def underscore(term)
|
21
|
+
term.gsub(/(?<!^)[A-Z]/) { "_#$&" }.downcase
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require "redis"
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
module Moleculer
|
6
|
+
module Transporters
|
7
|
+
##
|
8
|
+
# The Moleculer Redis transporter
|
9
|
+
class Redis
|
10
|
+
##
|
11
|
+
# @private
|
12
|
+
# Represents the publisher connection
|
13
|
+
class Publisher
|
14
|
+
def initialize(config)
|
15
|
+
@uri = config.transporter
|
16
|
+
@logger = config.logger.get_child("[REDIS.TRANSPORTER.PUBLISHER]")
|
17
|
+
@serializer = Serializers.for(config.serializer).new(config)
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Publishes the packet to the packet's topic
|
22
|
+
def publish(packet)
|
23
|
+
topic = packet.topic
|
24
|
+
@logger.trace "publishing packet to '#{topic}'", packet.as_json
|
25
|
+
connection.publish(topic, @serializer.serialize(packet))
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Connects to redis
|
30
|
+
def connect
|
31
|
+
@logger.debug "connecting publisher client on '#{@uri}'"
|
32
|
+
connection
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Disconnects from redis
|
37
|
+
def disconnect
|
38
|
+
@logger.debug "disconnecting publisher client"
|
39
|
+
connection.disconnect!
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def connection
|
45
|
+
@connection ||= ::Redis.new(url: @uri)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Represents the subscriber connection
|
51
|
+
# @private
|
52
|
+
class Subscriber
|
53
|
+
##
|
54
|
+
# Represents a subscription
|
55
|
+
class Subscription
|
56
|
+
def initialize(config:, channel:, block:)
|
57
|
+
@connection = ::Redis.new(url: config.transporter)
|
58
|
+
@channel = channel
|
59
|
+
@block = block
|
60
|
+
@logger = config.logger.get_child("[REDIS.TRANSPORTER.SUBSCRIPTION.#{channel}]")
|
61
|
+
@serializer = Serializers.for(config.serializer).new(config)
|
62
|
+
@node_id = config.node_id
|
63
|
+
|
64
|
+
# it is necessary to send some sort of message to signal the subscriber to disconnect and shutdown
|
65
|
+
# this is an internal message
|
66
|
+
reset_disconnect
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Starts the subscriber
|
71
|
+
def connect
|
72
|
+
@thread = Thread.new do
|
73
|
+
begin
|
74
|
+
@logger.debug "starting subscription to '#{@channel}'"
|
75
|
+
@connection.subscribe(@channel) do |on|
|
76
|
+
on.unsubscribe do
|
77
|
+
unsubscribe
|
78
|
+
end
|
79
|
+
|
80
|
+
on.message do |_, message|
|
81
|
+
packet = process_message(message)
|
82
|
+
next unless packet
|
83
|
+
|
84
|
+
process_packet(packet)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
rescue StandardError => error
|
88
|
+
@logger.fatal error
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def disconnect
|
96
|
+
@logger.debug "unsubscribing from '#{@channel}'"
|
97
|
+
redis = ::Redis.new(url: @uri)
|
98
|
+
redis.publish(@channel, @disconnect_hash.value)
|
99
|
+
redis.disconnect!
|
100
|
+
end
|
101
|
+
|
102
|
+
def reset_disconnect
|
103
|
+
@disconnect_hash ||= Concurrent::AtomicReference.new
|
104
|
+
@disconnect_hash.set("#{@node_id}.#{SecureRandom.hex}.disconnect")
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def deserialize(message)
|
110
|
+
parsed = @serializer.deserialize(message)
|
111
|
+
return nil if parsed["sender"] == @node_id
|
112
|
+
|
113
|
+
parsed
|
114
|
+
end
|
115
|
+
|
116
|
+
def message_is_disconnect?(message)
|
117
|
+
message.split(".")[-1] == "disconnect"
|
118
|
+
end
|
119
|
+
|
120
|
+
def process_packet(packet)
|
121
|
+
return @connection.unsubscribe if packet == :disconnect
|
122
|
+
|
123
|
+
@logger.trace "received packet from #{packet.sender}:", packet.as_json
|
124
|
+
|
125
|
+
@block.call(packet)
|
126
|
+
rescue StandardError => error
|
127
|
+
@logger.error error
|
128
|
+
end
|
129
|
+
|
130
|
+
def process_message(message)
|
131
|
+
return :disconnect if message == @disconnect_hash
|
132
|
+
|
133
|
+
return nil if message_is_disconnect?(message)
|
134
|
+
|
135
|
+
packet_type = Packets.for(@channel.split(".")[1])
|
136
|
+
|
137
|
+
parsed = deserialize(message)
|
138
|
+
|
139
|
+
return nil unless parsed
|
140
|
+
|
141
|
+
|
142
|
+
packet_type.new(parsed)
|
143
|
+
rescue StandardError => error
|
144
|
+
@logger.error error
|
145
|
+
end
|
146
|
+
|
147
|
+
def unsubscribe
|
148
|
+
@logger.debug "disconnecting channel '#{@channel}'"
|
149
|
+
@connection.disconnect!
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def initialize(config)
|
154
|
+
@config = config
|
155
|
+
@uri = config.transporter
|
156
|
+
@logger = config.logger.get_child("[REDIS.TRANSPORTER]")
|
157
|
+
@subscriptions = Concurrent::Array.new
|
158
|
+
end
|
159
|
+
|
160
|
+
def subscribe(channel, &block)
|
161
|
+
@logger.debug "subscribing to channel '#{channel}'"
|
162
|
+
@subscriptions << Subscription.new(
|
163
|
+
channel: channel,
|
164
|
+
block: block,
|
165
|
+
config: @config,
|
166
|
+
).connect
|
167
|
+
end
|
168
|
+
|
169
|
+
def disconnect
|
170
|
+
@logger.debug "disconnecting subscriptions"
|
171
|
+
@subscriptions.each(&:disconnect)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def initialize(config)
|
176
|
+
@config = config
|
177
|
+
end
|
178
|
+
|
179
|
+
def subscribe(channel, &block)
|
180
|
+
subscriber.subscribe(channel, &block)
|
181
|
+
end
|
182
|
+
|
183
|
+
def publish(packet)
|
184
|
+
publisher.publish(packet)
|
185
|
+
end
|
186
|
+
|
187
|
+
def connect
|
188
|
+
publisher.connect
|
189
|
+
end
|
190
|
+
|
191
|
+
def disconnect
|
192
|
+
publisher.disconnect
|
193
|
+
subscriber.disconnect
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def publisher
|
199
|
+
@publisher ||= Publisher.new(@config)
|
200
|
+
end
|
201
|
+
|
202
|
+
def subscriber
|
203
|
+
@subscriber ||= Subscriber.new(@config)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
module Moleculer
|
4
|
+
##
|
5
|
+
# Transporters allow you to run services on multiple nodes. They provide the communication channels for other with
|
6
|
+
# other nodes handling the transfer of events, action calls and responses, ...etc.
|
7
|
+
module Transporters
|
8
|
+
|
9
|
+
##
|
10
|
+
# Returns a new transporter for the provided transporter uri
|
11
|
+
#
|
12
|
+
# @param [String] uri the transporter uri
|
13
|
+
# @param [Moleculer::Broker] broker the broker instance
|
14
|
+
#
|
15
|
+
# @return [Moleculer::Transporters::Base] the transporter instance
|
16
|
+
def self.for(uri)
|
17
|
+
parsed = URI(uri)
|
18
|
+
require_relative("./transporters/#{parsed.scheme}")
|
19
|
+
const_get(parsed.scheme.split("_").map(&:capitalize).join)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|