Class: Rinda::RingFinger

Inherits:
Object
  • Object
show all
Defined in:
lib/rinda/ring.rb

Overview

RingFinger is used by RingServer clients to discover the RingServer’s TupleSpace. Typically, all a client needs to do is call RingFinger.primary to retrieve the remote TupleSpace, which it can then begin using.

To find the first available remote TupleSpace:

Rinda::RingFinger.primary

To create a RingFinger that broadcasts to a custom list:

rf = Rinda::RingFinger.new  ['localhost', '192.0.2.1']
rf.primary

Rinda::RingFinger also understands multicast addresses and sets them up properly. This allows you to run multiple RingServers on the same host:

rf = Rinda::RingFinger.new ['239.0.0.1']
rf.primary

You can set the hop count (or TTL) for multicast searches using #multicast_hops.

If you use IPv6 multicast you may need to set both an address and the outbound interface index:

rf = Rinda::RingFinger.new ['ff02::1']
rf.multicast_interface = 1
rf.primary

At this time there is no easy way to get an interface index by name.

Constant Summary collapse

@@broadcast_list =
['<broadcast>', 'localhost']
@@finger =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(broadcast_list = @@broadcast_list, port = Ring_PORT) ⇒ RingFinger

Creates a new RingFinger that will look for RingServers at port on the addresses in broadcast_list.

If broadcast_list contains a multicast address then multicast queries will be made using the given multicast_hops and multicast_interface.

[View source]

344
345
346
347
348
349
350
351
352
# File 'lib/rinda/ring.rb', line 344

def initialize(broadcast_list=@@broadcast_list, port=Ring_PORT)
  @broadcast_list = broadcast_list || ['localhost']
  @port = port
  @primary = nil
  @rings = []

  @multicast_hops = 1
  @multicast_interface = 0
end

Instance Attribute Details

#broadcast_listObject

The list of addresses where RingFinger will send query packets.


313
314
315
# File 'lib/rinda/ring.rb', line 313

def broadcast_list
  @broadcast_list
end

#multicast_hopsObject

Maximum number of hops for sent multicast packets (if using a multicast address in the broadcast list). The default is 1 (same as UDP broadcast).


320
321
322
# File 'lib/rinda/ring.rb', line 320

def multicast_hops
  @multicast_hops
end

#multicast_interfaceObject

The interface index to send IPv6 multicast packets from.


325
326
327
# File 'lib/rinda/ring.rb', line 325

def multicast_interface
  @multicast_interface
end

#portObject

The port that RingFinger will send query packets to.


330
331
332
# File 'lib/rinda/ring.rb', line 330

def port
  @port
end

#primaryObject

Contain the first advertised TupleSpace after lookup_ring_any is called.


335
336
337
# File 'lib/rinda/ring.rb', line 335

def primary
  @primary
end

Class Method Details

.fingerObject

Creates a singleton RingFinger and looks for a RingServer. Returns the created RingFinger.

[View source]

288
289
290
291
292
293
294
# File 'lib/rinda/ring.rb', line 288

def self.finger
  unless @@finger
    @@finger = self.new
    @@finger.lookup_ring_any
  end
  @@finger
end

.primaryObject

Returns the first advertised TupleSpace.

[View source]

299
300
301
# File 'lib/rinda/ring.rb', line 299

def self.primary
  finger.primary
end

.to_aObject

Contains all discovered TupleSpaces except for the primary.

[View source]

306
307
308
# File 'lib/rinda/ring.rb', line 306

def self.to_a
  finger.to_a
end

Instance Method Details

#each {|@primary| ... } ⇒ Object

Iterates over all discovered TupleSpaces starting with the primary.

Yields:

[View source]

364
365
366
367
368
369
# File 'lib/rinda/ring.rb', line 364

def each
  lookup_ring_any unless @primary
  return unless @primary
  yield(@primary)
  @rings.each { |x| yield(x) }
end

#lookup_ring(timeout = 5, &block) ⇒ Object

Looks up RingServers waiting timeout seconds. RingServers will be given block as a callback, which will be called with the remote TupleSpace.

[View source]

376
377
378
379
380
381
382
383
384
# File 'lib/rinda/ring.rb', line 376

def lookup_ring(timeout=5, &block)
  return lookup_ring_any(timeout) unless block_given?

  msg = Marshal.dump([[:lookup_ring, DRbObject.new(block)], timeout])
  @broadcast_list.each do |it|
    send_message(it, msg)
  end
  sleep(timeout)
end

#lookup_ring_any(timeout = 5) ⇒ Object

Returns the first found remote TupleSpace. Any further recovered TupleSpaces can be found by calling to_a.

[View source]

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/rinda/ring.rb', line 390

def lookup_ring_any(timeout=5)
  queue = Thread::Queue.new

  Thread.new do
    self.lookup_ring(timeout) do |ts|
      queue.push(ts)
    end
    queue.push(nil)
  end

  @primary = queue.pop
  raise('RingNotFound') if @primary.nil?

  Thread.new do
    while it = queue.pop
      @rings.push(it)
    end
  end

  @primary
end

#make_socket(address) ⇒ Object

Creates a socket for address with the appropriate multicast options for multicast addresses.

[View source]

416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/rinda/ring.rb', line 416

def make_socket(address) # :nodoc:
  addrinfo = Addrinfo.udp(address, @port)

  soc = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
  begin
    if addrinfo.ipv4_multicast? then
      soc.setsockopt(Socket::Option.ipv4_multicast_loop(1))
      soc.setsockopt(Socket::Option.ipv4_multicast_ttl(@multicast_hops))
    elsif addrinfo.ipv6_multicast? then
      soc.setsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_LOOP, true)
      soc.setsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS,
                     [@multicast_hops].pack('I'))
      soc.setsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_IF,
                     [@multicast_interface].pack('I'))
    else
      soc.setsockopt(:SOL_SOCKET, :SO_BROADCAST, true)
    end

    soc.connect(addrinfo)
  rescue Exception
    soc.close
    raise
  end

  soc
end

#send_message(address, message) ⇒ Object

:nodoc:

[View source]

443
444
445
446
447
448
449
450
451
# File 'lib/rinda/ring.rb', line 443

def send_message(address, message) # :nodoc:
  soc = make_socket(address)

  soc.send(message, 0)
rescue
  nil
ensure
  soc.close if soc
end

#to_aObject

Contains all discovered TupleSpaces except for the primary.

[View source]

357
358
359
# File 'lib/rinda/ring.rb', line 357

def to_a
  @rings
end