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.



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.



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.



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.



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:



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.



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.



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.



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:



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.



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

def to_a
  @rings
end