Class: IBM::Bluemix::ServiceDiscovery

Inherits:
Object
  • Object
show all
Defined in:
lib/ibm/bluemix/service_discovery.rb,
lib/ibm/bluemix/service_discovery/version.rb

Overview

ServiceDiscovery provides a simple interface to the IBM Bluemix Service Discovery service. The interface provides methods for all operations provided by Service Discovery as well as some helpers to make working with the service a little easier.

Constant Summary collapse

SERVICE_DISCOVERY_ENDPOINT =

URL for the production Bluemix Service Discovery endpoint

"https://servicediscovery.ng.bluemix.net/api/v1/services"
SERVICE_REGISTRY_ENDPOINT =

URL for the production Bluemix Service Registry endpoint

"https://servicediscovery.ng.bluemix.net/api/v1/instances"
VERSION =
"0.1.1"
@@services =

cache of registered services

{}
@@heartbeats =

cache of threads running heartbeats

{}

Class Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(auth_token) ⇒ ServiceDiscovery

Create a new ServiceDiscovery instance.

Example:

>> sd = ServiceDiscovery.new('token...')

Arguments

auth_token

(String) Valid ‘auth_token` from the IBM Bluemix Service Discovery service



45
46
47
48
49
50
51
52
53
54
# File 'lib/ibm/bluemix/service_discovery.rb', line 45

def initialize(auth_token)
  @auth_token = auth_token

  # defaults
  Unirest.default_header('Authorization', 'Bearer ' + @auth_token)
  Unirest.default_header('Accept','application/json')
  Unirest.default_header('Content-Type','application/json')

  Unirest.timeout(5) # 5s timeout
end

Class Attribute Details

.loggerObject



31
32
33
34
35
# File 'lib/ibm/bluemix/service_discovery.rb', line 31

def logger
  @logger ||= Logger.new($stdout).tap do |log|
    log.progname = self.name
  end
end

Instance Method Details

#delete(service_name) ⇒ Object

Deletes a service entry from Service Discovery

Example:

>> sd.delete('sample_service')

Arguments

service_name

(String) The name of the microservice

Returns

(Boolean) ‘true` if the service was removed, `false` otherwise

Raises



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/ibm/bluemix/service_discovery.rb', line 242

def delete(service_name)

  # error, we didn't register the service yet
  return false unless @@services.has_key? service_name

  service = @@services[service_name]
  ServiceDiscovery.logger.debug 'Deleting: ' + service.to_s

  begin
    response = Unirest.delete service['links']['self']
  rescue Exception => e
    #
    ServiceDiscovery.logger.debug "Exception: #{e.class} #{e.message}"
  end

  if response.code != 200
    #
    # Attempting to send a heartbeat for an expired instance will result in HTTP status code 410 (Gone).
  end
  @@services.delete service_name
  true
end

#discover(service_name) ⇒ Object

Discovers the connection information for a given microservice.

Example:

>> sd.discover('sample_service')

Arguments

service_name

(String) The name of the microservice

Returns

(Hash)

{
   "service_name":"my_service",
   "instances":[
   {
      "endpoint":
      {
         "type": "tcp",
         "value": "192.168.1.32:80",
      },
      "status": "UP",
      "last_heartbeat": "2015-05-01T08:28:06.801064+02:00",
      "metadata": {"key":"value"}
   }]
}

Raises



371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/ibm/bluemix/service_discovery.rb', line 371

def discover(service_name)
  # curl -X GET -H "Authorization: bearer 12o191sqk5h***" https://servicediscovery.ng.bluemix.net/api/v1/services/my_service

  # {
  #    "service_name":"my_service",
  #    "instances":[
  #    {
  #       "endpoint":
  #       {
  #          "type": "tcp",
  #          "value": "192.168.1.32:80",
  #       },
  #       "status": "UP",
  #       "last_heartbeat": "2015-05-01T08:28:06.801064+02:00",
  #       "metadata": {"key":"value"}
  #    }]
  # }

  begin
    response = Unirest.get SERVICE_DISCOVERY_ENDPOINT + '/' + service_name
  rescue Exception => e
    #
    ServiceDiscovery.logger.debug "Exception: #{e.class} #{e.message}"
  end

  if response.code != 200
    #
    # Attempting to send a heartbeat for an expired instance will result in HTTP status code 410 (Gone).
  end

  response.body
end

#heartbeat(service_name, interval = 60) ⇒ Object

Set-up a separate Thread to send continuous heartbeats (renew) calls to indicate the service is alive.

Example:

>> sd.heartbeat('sample_service', 45)

Arguments

service_name

(String) The name of the microservice

interval

(Integer) The number of seconds between heartbeats

Returns

(Boolean) ‘true` if the heartbeat thread was started, `false` otherwise

Raises



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/ibm/bluemix/service_discovery.rb', line 190

def heartbeat(service_name, interval=60)

  # kill the existing thread
  unless @@heartbeats[service_name].nil?
    ServiceDiscovery.logger.debug 'killing an existing heartbeat thread'
    Thread.kill @@heartbeats[service_name]
  end

  # create a new thread that is going to run forever
  @@heartbeats[service_name] = Thread.new{
    while true
      # TODO: how to handle errors in the thread?
      ServiceDiscovery.logger.debug 'sending heartbeat'
      self.renew(service_name)
      sleep interval
    end
  }

  # # something happened?
  # true
end

#infoObject

Returns information about the current local state of Service Discovery. This includes information about the services and the heartbeats.

Example:

>> sd.info

Arguments

none

Returns

(Hash) { services: [‘s1’, ‘s2’], heartbeats: [‘s1’] }

Raises



308
309
310
# File 'lib/ibm/bluemix/service_discovery.rb', line 308

def info
  { services: @@services.keys, heartbeats: heartbeats.keys }
end

#listObject

Get the current list of registered services within Service Discovery

Example:

>> sd.list

Arguments

none

Returns

(Hash) { services: [‘s1’, ‘s2’] }

Raises



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/ibm/bluemix/service_discovery.rb', line 325

def list

  # curl -X GET -H "Authorization: bearer 12o191sqk5h***"https://servicediscovery.ng.bluemix.net/api/v1/services

  begin
    response = Unirest.get SERVICE_DISCOVERY_ENDPOINT
  rescue Exception => e
    #
    ServiceDiscovery.logger.debug "Exception: #{e.class} #{e.message}"
  end

  if response.code != 200
    #
    # Attempting to send a heartbeat for an expired instance will result in HTTP status code 410 (Gone).
  end

  response.body
end

#register(service_name, host, options = {}, meta = {}) ⇒ Object

Register a service with Service Discovery

Example:

>> sd = ServiceDiscovery.new('token...')
=> sd.register('sample_service', '192.168.1.100:8080', { ttl: 60, heartbeat: true }, {})

Arguments

service_name

(String) The name of the microservice

host

(String) The host and port where the microservice can be reached at

options

(Hash) Options, see below

meta

(Hash) Metadata to store with the service registration

Options

heartbeat

(Boolean) Enable or disable automated heartbeat for the service

ttl

(Integer) Expire the service after this many seconds if a heartbeat has not been received. Hearbeat is automatically set to 30% of this value

Returns

resulting payload from the call to Service Discovery

Raises



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/ibm/bluemix/service_discovery.rb', line 77

def register(service_name, host, options = {}, meta = {})

  begin
    response = Unirest.post SERVICE_REGISTRY_ENDPOINT,
                    parameters: {
                      service_name: service_name,
                      endpoint: {
                        type: 'tcp',
                        value: host
                      },
                      status: "UP",
                      ttl: options[:ttl] || 60,
                      metadata: meta
                    }.to_json
  rescue Exception => e
    # TODO: raise custom exception
    ServiceDiscovery.logger.debug "Exception: #{e.class} #{e.message}"
    return nil
  end

  # ServiceDiscovery.logger.debug response

  if response.code != 201
    #
    ServiceDiscovery.logger.error response.code
  end

  # TODO: validate the response.body has the right keys

  @@services[service_name] = response.body

  # response.code # Status code
  # response.headers # Response headers
  # response.body # Parsed body
  # response.raw_body # Unparsed body



  # '{"service_name":"my_service", "endpoint": { "type":"tcp", "value": "host:port" }, "status":"UP", "ttl":25, "metadata":{"key":"value"}}'

  # {
  #    "id":"6ae425dd79f1962e",
  #    "ttl":30,
  #    "links":{
  #       "self": "https://servicediscovery.ng.bluemix.net/api/v1/instances/6ae425dd79f1962e",
  #       "heartbeat": "https://servicediscovery.ng.bluemix.net/api/v1/instances/6ae425dd79f1962e/heartbeat",
  #    }
  # }

  # check if we should enable a heartbeat
  if options[:heartbeat] == true
    heartbeat_ttl = (options[:ttl] * 0.75).round
    self.heartbeat(service_name, heartbeat_ttl)
  end

  return @@services[service_name]
end

#renew(service_name) ⇒ Object

Send a heartbeat request to indicate the service is still alive and well

Example:

>> sd.renew('sample_service')

Arguments

service_name

(String) The name of the microservice

Returns

(Boolean) ‘true` if the service was renewed, `false` otherwise

Raises



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/ibm/bluemix/service_discovery.rb', line 148

def renew(service_name)

  # error, we didn't register the service yet
  return false unless @@services.has_key? service_name

  service = @@services[service_name]

  begin
    ServiceDiscovery.logger.debug 'calling heartbeat url: ' + service['links']['heartbeat']
    response = Unirest.put service['links']['heartbeat'],
                    headers: {
                      'Content-Length': 0
                    }
  rescue Exception => e
    #
  end

  if response.code != 200
    #
    # Attempting to send a heartbeat for an expired instance will result in HTTP status code 410 (Gone).
    return false
  end

  # curl -X PUT -H "Authorization: Bearer 12o191sqk5h***" -H "Content-Length: 0" https://servicediscovery.ng.bluemix.net/api/v1/instances/6ae425dd79f1962e/heartbeat
  true
end

#resetObject

Resets the state of this service. This includes stopping any existing heartbeat Threads as well as deleting all registered services.

Example:

>> sd.reset

Arguments

none

Returns

(Boolean) ‘true` if the reset was successful, `false` otherwise

Raises



279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/ibm/bluemix/service_discovery.rb', line 279

def reset

  # cancel any threads
  @@heartbeats.keys.each do |k|
    self.unheartbeat(k)
  end

  # delete all of the services
  @@services.keys.each do |k|
    self.delete(k)
  end

  true
end

#unheartbeat(service_name) ⇒ Object

Stops a previously established heartbeat Thread

Example:

>> sd.unheartbeat('sample_service')

Arguments

service_name

(String) The name of the microservice

Returns

(Boolean) ‘true` if the heartbeat thread was stopped, `false` otherwise

Raises



225
226
227
# File 'lib/ibm/bluemix/service_discovery.rb', line 225

def unheartbeat(service_name)
  Thread.kill @@heartbeats[service_name]
end