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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +44 -0
  5. data/.travis.yml +23 -0
  6. data/.yardopts +1 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +6 -0
  9. data/Gemfile.lock +57 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +59 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/examples/benchmark_server.rb +21 -0
  16. data/examples/client-server/client.rb +13 -0
  17. data/examples/client-server/server.rb +25 -0
  18. data/lib/moleculer/broker.rb +318 -0
  19. data/lib/moleculer/configuration.rb +109 -0
  20. data/lib/moleculer/context.rb +24 -0
  21. data/lib/moleculer/errors/action_not_found.rb +6 -0
  22. data/lib/moleculer/errors/invalid_action_response.rb +11 -0
  23. data/lib/moleculer/errors/local_node_already_registered.rb +6 -0
  24. data/lib/moleculer/errors/node_not_found.rb +6 -0
  25. data/lib/moleculer/errors/transporter_already_started.rb +6 -0
  26. data/lib/moleculer/node.rb +105 -0
  27. data/lib/moleculer/packets/base.rb +47 -0
  28. data/lib/moleculer/packets/disconnect.rb +10 -0
  29. data/lib/moleculer/packets/discover.rb +10 -0
  30. data/lib/moleculer/packets/event.rb +37 -0
  31. data/lib/moleculer/packets/heartbeat.rb +18 -0
  32. data/lib/moleculer/packets/info.rb +97 -0
  33. data/lib/moleculer/packets/req.rb +57 -0
  34. data/lib/moleculer/packets/res.rb +43 -0
  35. data/lib/moleculer/packets.rb +25 -0
  36. data/lib/moleculer/registry.rb +325 -0
  37. data/lib/moleculer/serializers/json.rb +23 -0
  38. data/lib/moleculer/serializers.rb +10 -0
  39. data/lib/moleculer/service/action.rb +60 -0
  40. data/lib/moleculer/service/base.rb +117 -0
  41. data/lib/moleculer/service/event.rb +50 -0
  42. data/lib/moleculer/service/remote.rb +80 -0
  43. data/lib/moleculer/service.rb +2 -0
  44. data/lib/moleculer/support/hash_util.rb +48 -0
  45. data/lib/moleculer/support/log_proxy.rb +67 -0
  46. data/lib/moleculer/support/open_struct.rb +15 -0
  47. data/lib/moleculer/support/string_util.rb +25 -0
  48. data/lib/moleculer/support.rb +4 -0
  49. data/lib/moleculer/transporters/redis.rb +207 -0
  50. data/lib/moleculer/transporters.rb +22 -0
  51. data/lib/moleculer/version.rb +3 -0
  52. data/lib/moleculer.rb +103 -0
  53. data/moleculer.gemspec +50 -0
  54. metadata +238 -0
@@ -0,0 +1,18 @@
1
+ require_relative "base"
2
+
3
+ module Moleculer
4
+ module Packets
5
+ ##
6
+ # Represents a DISCOVER packet
7
+ class Heartbeat < Base
8
+
9
+ def initialize(data)
10
+ super(data)
11
+
12
+ @cpu = 0
13
+ end
14
+
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,97 @@
1
+ require "socket"
2
+ require_relative "base"
3
+
4
+ module Moleculer
5
+ module Packets
6
+ ##
7
+ # Represents an INFO packet
8
+ class Info < Base
9
+ include Support
10
+
11
+ ##
12
+ # Represents the client information for a given node
13
+ class Client
14
+ include Support
15
+
16
+ # @!attribute [r] type
17
+ # @return [String] type of client implementation (nodejs, java, ruby etc.)
18
+ # @!attribute [r] version
19
+ # @return [String] the version of the moleculer client
20
+ # @!attribute [r] lang_version
21
+ # @return [String] the client type version
22
+ attr_reader :type,
23
+ :version,
24
+ :lang_version
25
+
26
+ def initialize(data)
27
+ @type = HashUtil.fetch(data, :type)
28
+ @version = HashUtil.fetch(data, :version)
29
+ @lang_version = HashUtil.fetch(data, :lang_version)
30
+ end
31
+
32
+ ##
33
+ # @return [Hash] the object prepared for conversion to JSON for transmission
34
+ def as_json
35
+ {
36
+ type: @type,
37
+ version: @version,
38
+ langVersion: @lang_version,
39
+ }
40
+ end
41
+ end
42
+
43
+ # @!attribute [r] services
44
+ # @return [Array<Moleculer::Services::Remote|Moleculer::Services::Base>] an array of the services the endpoint
45
+ # provides
46
+ # @!attribute [r] config
47
+ # @return [Moleculer::Support::OpenStruct] the configuration of the node
48
+ # @!attribute [r] ip_list
49
+ # @return [Array<String>] a list of the node's used IP addresses
50
+ # @!attribute [r] hostname
51
+ # @return [String] the hostname of the node
52
+ # @!attribute [r] client
53
+ # @return [Moleculer::Packets::Info::Client] the client data for the node
54
+ attr_reader :services,
55
+ :config,
56
+ :ip_list,
57
+ :hostname,
58
+ :client
59
+
60
+ ##
61
+ # @param data [Hash] the packet data
62
+ # @options data [Array<Hash>|Moleculer::Services::Base] services the services information
63
+ # @options data [Hash] config the configuration data for the node
64
+ # @options data [Array<String>] ip_list the list of ip addresses for the node
65
+ # @options data [String] hostname the hostname of the node
66
+ # @options data [Hash] client the client data for the node
67
+ def initialize(data)
68
+ super(data)
69
+ @services = HashUtil.fetch(data, :services)
70
+ @config = OpenStruct.new(Hash[HashUtil.fetch(data, :config).map { |i| [StringUtil.underscore(i[0]), i[1]] }])
71
+ @ip_list = HashUtil.fetch(data, :ip_list)
72
+ @hostname = HashUtil.fetch(data, :hostname)
73
+ @client = Client.new(HashUtil.fetch(data, :client))
74
+ @node = HashUtil.fetch(data, :node, nil)
75
+ end
76
+
77
+ def topic
78
+ if @node
79
+ return "#{super}.#{@node.id}" if @node.is_a? Moleculer::Node
80
+
81
+ return "#{super}.#{@node}"
82
+ end
83
+ super
84
+ end
85
+
86
+ def as_json
87
+ super.merge(
88
+ services: @services,
89
+ config: @config.to_h,
90
+ ipList: @ip_list,
91
+ hostname: @hostname,
92
+ client: @client.as_json,
93
+ )
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,57 @@
1
+ require_relative "base"
2
+
3
+ module Moleculer
4
+ module Packets
5
+ ##
6
+ # Represents a REQ packet
7
+ class Req < Base
8
+ attr_reader :action,
9
+ :params,
10
+ :meta,
11
+ :timeout,
12
+ :level,
13
+ :parent_id,
14
+ :request_id,
15
+ :stream,
16
+ :metrics,
17
+ :stream,
18
+ :id,
19
+ :node
20
+
21
+ def initialize(data)
22
+ super(data)
23
+
24
+ @id = HashUtil.fetch(data, :id)
25
+ @action = HashUtil.fetch(data, :action)
26
+ @params = HashUtil.fetch(data, :params)
27
+ @meta = HashUtil.fetch(data, :meta)
28
+ @timeout = HashUtil.fetch(data, :timeout, nil)
29
+ @level = HashUtil.fetch(data, :level, 1)
30
+ @metrics = HashUtil.fetch(data, :metrics, false)
31
+ @parent_id = HashUtil.fetch(data, :parent_id, nil)
32
+ @request_id = HashUtil.fetch(data, :request_id, nil)
33
+ @stream = false
34
+ @node = HashUtil.fetch(data, :node, nil)
35
+ end
36
+
37
+ def as_json # rubocop:disable Metrics/MethodLength
38
+ super.merge(
39
+ id: @id,
40
+ action: @action,
41
+ params: @params,
42
+ meta: @meta,
43
+ timeout: @timeout,
44
+ level: @level,
45
+ metrics: @metrics,
46
+ parent_id: @parent_id,
47
+ request_id: @request_id,
48
+ stream: @stream,
49
+ )
50
+ end
51
+
52
+ def topic
53
+ "#{super}.#{@node.id}"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ require_relative "base"
2
+
3
+ module Moleculer
4
+ module Packets
5
+ ##
6
+ # Represents a RES packet
7
+ class Res < Base
8
+ attr_reader :id,
9
+ :success,
10
+ :data,
11
+ :error,
12
+ :meta,
13
+ :stream
14
+
15
+ def initialize(data)
16
+ super(data)
17
+
18
+ @id = HashUtil.fetch(data, :id)
19
+ @success = HashUtil.fetch(data, :success)
20
+ @data = HashUtil.fetch(data, :data)
21
+ @error = HashUtil.fetch(data, :error, nil)
22
+ @meta = HashUtil.fetch(data, :meta)
23
+ @stream = HashUtil.fetch(data, :stream, false)
24
+ @node = HashUtil.fetch(data, :node, nil)
25
+ end
26
+
27
+ def topic
28
+ "#{super}.#{@node.id}"
29
+ end
30
+
31
+ def as_json
32
+ super.merge(
33
+ id: @id,
34
+ success: @success,
35
+ data: @data,
36
+ error: @error,
37
+ meta: @meta,
38
+ stream: @stream,
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ require_relative "packets/disconnect"
2
+ require_relative "packets/discover"
3
+ require_relative "packets/event"
4
+ require_relative "packets/heartbeat"
5
+ require_relative "packets/info"
6
+ require_relative "packets/req"
7
+ require_relative "packets/res"
8
+
9
+ module Moleculer
10
+ module Packets
11
+ TYPES = {
12
+ Discover.packet_name => Discover,
13
+ Info.packet_name => Info,
14
+ Req.packet_name => Req,
15
+ Res.packet_name => Res,
16
+ Heartbeat.packet_name => Heartbeat,
17
+ Event.packet_name => Event,
18
+ Disconnect.packet_name => Disconnect,
19
+ }.freeze
20
+
21
+ def self.for(type)
22
+ TYPES[type.to_s.upcase]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,325 @@
1
+ require_relative "errors/local_node_already_registered"
2
+ require_relative "errors/action_not_found"
3
+ require_relative "errors/node_not_found"
4
+ require_relative "support"
5
+
6
+ module Moleculer
7
+ ##
8
+ # The Registry manages the available services on the network
9
+ class Registry
10
+ ##
11
+ # @private
12
+ class NodeList
13
+ def initialize(heartbeat_interval)
14
+ @nodes = Concurrent::Hash.new
15
+ @heartbeat_interval = heartbeat_interval
16
+ end
17
+
18
+ def add_node(node)
19
+ if @nodes[node.id]
20
+ @nodes[node.id][:node] = node
21
+ else
22
+ @nodes[node.id] = { node: node, last_requested_at: Time.now }
23
+ end
24
+ end
25
+
26
+ def remove_node(node_id)
27
+ @nodes.delete(node_id)
28
+ end
29
+
30
+ def fetch_next_node
31
+ node = active_nodes.min_by { |a| a[:last_requested_at] }[:node]
32
+ @nodes[node.id][:last_requested_at] = Time.now
33
+ node
34
+ end
35
+
36
+ def fetch_node(node_id)
37
+ @nodes.fetch(node_id)[:node]
38
+ end
39
+
40
+ def length
41
+ @nodes.length
42
+ end
43
+
44
+ def active_nodes
45
+ @nodes.values.select { |node| (Time.now - node[:node].last_heartbeat_at) < @heartbeat_interval * 3 }
46
+ end
47
+
48
+ def expired_nodes
49
+ @nodes.values.select { |node| (Time.now - node[:node].last_heartbeat_at) > 600 }
50
+ end
51
+ end
52
+
53
+ ##
54
+ # @private
55
+ class ActionList
56
+ def initialize(heartbeat_interval)
57
+ @heartbeat_interval = heartbeat_interval
58
+ @actions = Concurrent::Hash.new
59
+ end
60
+
61
+ def add(name, action)
62
+ @actions[name] ||= NodeList.new(@heartbeat_interval)
63
+ @actions[name].add_node(action.node)
64
+ end
65
+
66
+ def remove_node(node_id)
67
+ @actions.each do |k, a|
68
+ a.remove_node(node_id)
69
+ @actions.delete(k) if a.length.zero?
70
+ end
71
+ end
72
+
73
+ def fetch_action(action_name)
74
+ raise Errors::ActionNotFound, "The action '#{action_name}' was not found." unless @actions[action_name]
75
+
76
+ @actions[action_name].fetch_next_node.actions[action_name]
77
+ end
78
+ end
79
+
80
+ ##
81
+ # @private
82
+ class EventList
83
+ ##
84
+ # @private
85
+ class Item
86
+ def initialize(heartbeat_interval)
87
+ @heartbeat_interval = heartbeat_interval
88
+ @services = Concurrent::Hash.new
89
+ end
90
+
91
+ def add_service(service)
92
+ @services[service.service_name] ||= NodeList.new(@heartbeat_interval)
93
+ @services[service.service_name].add_node(service.node)
94
+ end
95
+
96
+ def fetch_nodes
97
+ @services.values.map(&:fetch_next_node)
98
+ end
99
+
100
+ def remove_node(node_id)
101
+ @services.each do |k, list|
102
+ list.remove_node(node_id)
103
+ @services.delete(k) if list.length.zero?
104
+ end
105
+ end
106
+
107
+ def length
108
+ @services.map(&:length).inject(0) { |s, a| s + a }
109
+ end
110
+ end
111
+
112
+ def initialize(heartbeat_interval)
113
+ @events = Concurrent::Hash.new
114
+ @heartbeat_interval = heartbeat_interval
115
+ end
116
+
117
+ def add(event)
118
+ name = event.name
119
+ @events[name] ||= Item.new(@heartbeat_interval)
120
+ @events[name].add_service(event.service)
121
+ end
122
+
123
+ def remove_node(node_id)
124
+ @events.each do |k, item|
125
+ item.remove_node(node_id)
126
+ @events.delete(k) if item.length.zero?
127
+ end
128
+ end
129
+
130
+ def fetch_events(event_name)
131
+ return [] unless @events[event_name]
132
+
133
+ @events[event_name].fetch_nodes.map { |n| n.events[event_name] }.flatten
134
+ end
135
+ end
136
+
137
+ private_constant :ActionList
138
+ private_constant :EventList
139
+ private_constant :NodeList
140
+
141
+ include Support
142
+
143
+ attr_reader :local_node
144
+
145
+ ##
146
+ # @param config [Moleculer::Config] the moleculer configuration
147
+ def initialize(config)
148
+ @config = config
149
+ @nodes = NodeList.new(@config.heartbeat_interval)
150
+ @actions = ActionList.new(@config.heartbeat_interval)
151
+ @events = EventList.new(@config.heartbeat_interval)
152
+ @services = Concurrent::Hash.new
153
+ @logger = @config.logger.get_child("[REGISTRY]")
154
+ @remove_semaphore = Concurrent::Semaphore.new(1)
155
+ end
156
+
157
+ ##
158
+ # Registers the node with the registry and updates the action/event handler lists.
159
+ #
160
+ # @param [Moleculer::Node] node the node to register
161
+ #
162
+ # @return [Moleculer::Node] the node that has been registered
163
+ def register_node(node)
164
+ return local_node if @local_node && node.id == @local_node.id
165
+
166
+ if node.local?
167
+ raise Errors::LocalNodeAlreadyRegistered, "A LOCAL node has already been registered" if @local_node
168
+
169
+ @logger.info "registering LOCAL node '#{node.id}'"
170
+ @local_node = node
171
+ end
172
+ @logger.info "registering node #{node.id}" unless node.local?
173
+ @nodes.add_node(node)
174
+ update_actions(node)
175
+ update_events(node)
176
+ # always call this last, as it will immediately cause processes waiting for services to show it as ready even if
177
+ # the actions or events lists have not been updated.
178
+ update_services(node)
179
+ node
180
+ end
181
+
182
+ ##
183
+ # Gets the named action from the registry. If a local action exists it will return the local one instead of a
184
+ # remote action.
185
+ #
186
+ # @param action_name [String] the name of the action
187
+ #
188
+ # @return [Moleculer::Service::Action|Moleculer::RemoteService::Action]
189
+ def fetch_action(action_name)
190
+ @actions.fetch_action(action_name)
191
+ end
192
+
193
+ ##
194
+ # Fetches all the events for the given event name that should be used to emit an event. This is load balanced, and
195
+ # will fetch a different node for nodes that duplicate the service/event
196
+ #
197
+ # @param event_name [String] the name of the even to fetch
198
+ #
199
+ # @return [Array<Moleculer::Service::Event>] the events that that should be emitted to
200
+ def fetch_events_for_emit(event_name)
201
+ @events.fetch_events(event_name)
202
+ end
203
+
204
+ ##
205
+ # It fetches the given node, and raises an exception if the node does not exist
206
+ #
207
+ # @param node_id [String] the id of the node to fetch
208
+ #
209
+ # @return [Moleculer::Node] the moleculer node with the given id
210
+ # @raise [Moleculer::Errors::NodeNotFound] if the node was not found
211
+ def fetch_node(node_id)
212
+ @nodes.fetch_node(node_id)
213
+ rescue KeyError
214
+ raise Errors::NodeNotFound, "The node with the id '#{node_id}' was not found."
215
+ end
216
+
217
+ ##
218
+ # Fetches the given node, and returns nil if the node was not found
219
+ #
220
+ # @param node_id [String] the id of the node to fetch
221
+ #
222
+ # @return [Moleculer::Node] the moleculer node with the given id
223
+ def safe_fetch_node(node_id)
224
+ fetch_node(node_id)
225
+ rescue Errors::NodeNotFound
226
+ nil
227
+ end
228
+
229
+ ##
230
+ # Gets the named action from the registry for the given node. Raises an error if the node does not exist or the node
231
+ # does not have the specified action.
232
+ #
233
+ # @param action_name [String] the name of the action
234
+ # @param node_id [String] the id of the node from which to get the action
235
+ #
236
+ # @return [Moleculer::Service::Action] the action from the specified node_id
237
+ # @raise [Moleculer::NodeNotFound] raised when the specified node_id was not found
238
+ # @raise [Moleculer::ActionNotFound] raised when the specified action was not found on the specified node
239
+ def fetch_action_for_node_id(action_name, node_id)
240
+ node = fetch_node(node_id)
241
+ fetch_action_from_node(action_name, node)
242
+ end
243
+
244
+ def missing_services(*services)
245
+ services - @services.keys
246
+ end
247
+
248
+ ##
249
+ # Removes the node with the given id. Because this must act across multiple indexes this action uses a semaphore to
250
+ # reduce the chance of race conditions.
251
+ #
252
+ # @param node_id [String] the node to remove
253
+ def remove_node(node_id, reason = nil)
254
+ @remove_semaphore.acquire
255
+ @logger.info "removing node '#{node_id}'" unless reason
256
+ @logger.info "removing node '#{node_id}' because #{reason}"
257
+ @nodes.remove_node(node_id)
258
+ @actions.remove_node(node_id)
259
+ @events.remove_node(node_id)
260
+ @remove_semaphore.release
261
+ end
262
+
263
+ ##
264
+ # Looks for nodes that have stopped reporting heartbeats and removes them from the registry
265
+ def expire_nodes
266
+ @nodes.expired_nodes.each { |node| remove_node(node[:node].id, "it expired after 10 minutes") }
267
+ end
268
+
269
+ private
270
+
271
+ def get_local_action(name)
272
+ @local_node.actions.select { |a| a.name == name.to_s }.first
273
+ end
274
+
275
+ def update_node_for_load_balancer(node)
276
+ @nodes[node.id][:last_called_at] = Time.now
277
+ end
278
+
279
+ def fetch_event_from_node(event_name, node)
280
+ node.events.fetch(event_name)
281
+ rescue KeyError
282
+ raise Errors::EventNotFound, "The event '#{event_name}' was not found on the node id with id '#{node.id}'"
283
+ end
284
+
285
+ def fetch_action_from_node(action_name, node)
286
+ node.actions.fetch(action_name)
287
+ end
288
+
289
+ def fetch_next_nodes_for_event(event_name)
290
+ service_names = HashUtil.fetch(@events, event_name, {}).keys
291
+ node_names = service_names.map { |s| @services[s] }
292
+ nodes = node_names.map { |names| names.map { |name| @nodes[name] } }
293
+ nodes.map { |node_list| node_list.min_by { |a| a[:last_called_at] }[:node] }
294
+ end
295
+
296
+ def remove_node_from_events(node_id)
297
+ @events.values.each do |event|
298
+ event.values.each do |list|
299
+ list.reject! { |id| id == node_id }
300
+ end
301
+ end
302
+ end
303
+
304
+ def update_actions(node)
305
+ node.actions.each do |name, action|
306
+ @actions.add(name, action)
307
+ end
308
+ @logger.debug "registered #{node.actions.length} action(s) for node '#{node.id}'"
309
+ end
310
+
311
+ def update_events(node)
312
+ node.events.values.each do |events|
313
+ events.each { |e| @events.add(e) }
314
+ end
315
+ @logger.debug "registered #{node.events.length} event(s) for node '#{node.id}'"
316
+ end
317
+
318
+ def update_services(node)
319
+ node.services.values.each do |service|
320
+ @services[service.service_name] ||= NodeList.new(@heartbeat_interval)
321
+ @services[service.service_name].add_node(node)
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,23 @@
1
+ require "json"
2
+
3
+ module Moleculer
4
+ module Serializers
5
+ ##
6
+ # Serializes data packets to and from JSON
7
+ class Json
8
+ def initialize(config)
9
+ @logger = config.logger.get_child("[SERIALIZER]")
10
+ end
11
+
12
+ def serialize(message)
13
+ message.as_json.to_json
14
+ end
15
+
16
+ def deserialize(message)
17
+ JSON.parse(message)
18
+ rescue StandardError => e
19
+ @logger.error e
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ module Moleculer
2
+ module Serializers
3
+
4
+ def self.for(serializer)
5
+ require_relative("serializers/#{serializer}")
6
+ Serializers.const_get(serializer.to_s.split("_").map(&:capitalize).join)
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ require_relative "../errors/invalid_action_response"
2
+ require_relative "../support"
3
+ module Moleculer
4
+ module Service
5
+ ##
6
+ # Represents an action
7
+ class Action
8
+ include Support
9
+
10
+ # @!attribute [r] name
11
+ # @return [String] the name of the action
12
+ attr_reader :name, :service
13
+
14
+ ##
15
+ # @param name [String|Symbol] the name of the action.
16
+ # @param method [Symbol] the service method to be called.
17
+ # @param service [Moleculer::Service::Base] the moleculer service class
18
+ # @param options [Hash] action options.
19
+ # @option options [Boolean|Hash] :cache if true, will use default caching options, if a hash is provided caching
20
+ # options will reflect the hash.
21
+ # @option options [Hash] params list of param and param types. Can be used to coerce specific params to the
22
+ # provided type.
23
+ def initialize(name, service, method, options = {})
24
+ @name = name
25
+ @service = service
26
+ @method = method
27
+ @service = service
28
+ @options = options
29
+ end
30
+
31
+ ##
32
+ # @param context [Moleculer::Context] the execution contextd
33
+ #
34
+ # @return [Moleculer::Support::Hash] returns a hash which will be converted into json for the response.
35
+ def execute(context, broker)
36
+ response = @service.new(broker).public_send(@method, context)
37
+ # rubocop:disable Style/RaiseArgs
38
+ raise Errors::InvalidActionResponse.new(response) unless response.is_a? Hash
39
+
40
+ response
41
+ end
42
+
43
+ def node
44
+ @service.node
45
+ end
46
+
47
+ def as_json
48
+ {
49
+ name: "#{@service.service_name}.#{name}",
50
+ rawName: name,
51
+ cache: HashUtil.fetch(@options, :cache, false),
52
+ metrics: {
53
+ params: false,
54
+ meta: true,
55
+ },
56
+ }
57
+ end
58
+ end
59
+ end
60
+ end