Class: Rinda::RingFinger

Inherits:
Object
  • Object
show all
Defined in:
lib/rinda2/ring/finger.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.



102
103
104
105
106
107
108
109
110
# File 'lib/rinda2/ring/finger.rb', line 102

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.



71
72
73
# File 'lib/rinda2/ring/finger.rb', line 71

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).



78
79
80
# File 'lib/rinda2/ring/finger.rb', line 78

def multicast_hops
  @multicast_hops
end

#multicast_interfaceObject

The interface index to send IPv6 multicast packets from.



83
84
85
# File 'lib/rinda2/ring/finger.rb', line 83

def multicast_interface
  @multicast_interface
end

#portObject

The port that RingFinger will send query packets to.



88
89
90
# File 'lib/rinda2/ring/finger.rb', line 88

def port
  @port
end

#primaryObject

Contain the first advertised TupleSpace after lookup_ring_any is called.



93
94
95
# File 'lib/rinda2/ring/finger.rb', line 93

def primary
  @primary
end

Class Method Details

.fingerObject

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



46
47
48
49
50
51
52
# File 'lib/rinda2/ring/finger.rb', line 46

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

.primaryObject

Returns the first advertised TupleSpace.



57
58
59
# File 'lib/rinda2/ring/finger.rb', line 57

def self.primary
  finger.primary
end

.to_aObject

Contains all discovered TupleSpaces except for the primary.



64
65
66
# File 'lib/rinda2/ring/finger.rb', line 64

def self.to_a
  finger.to_a
end

Instance Method Details

#eachObject

Iterates over all discovered TupleSpaces starting with the primary.



122
123
124
125
126
127
128
129
# File 'lib/rinda2/ring/finger.rb', line 122

def each
  Enumerator.new do |y|
    lookup_ring_any unless @primary
    return unless @primary
    y << @primary
    @rings.each { |x| y << x }
  end
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.



136
137
138
139
140
141
142
143
144
# File 'lib/rinda2/ring/finger.rb', line 136

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.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/rinda2/ring/finger.rb', line 150

def lookup_ring_any(timeout=5)
  queue = 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, interface_address = nil) ⇒ Object

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



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rinda2/ring/finger.rb', line 176

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

  soc = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
  begin
    if addrinfo.ipv4_multicast? then
      interface_address ||= '0.0.0.0'
      soc.setsockopt(Socket::Option.ipv4_multicast_loop(1))
      soc.setsockopt(Socket::Option.ipv4_multicast_ttl(@multicast_hops))
      ifreq = IPAddr.new(interface_address).hton
      soc.setsockopt(:IPPROTO_IP, :IP_MULTICAST_IF, ifreq)
    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:



206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/rinda2/ring/finger.rb', line 206

def send_message(address, message) # :nodoc:
  soc = if Array === address
    make_socket(*address)
  else
    make_socket(address)
  end

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

#to_aObject

Contains all discovered TupleSpaces except for the primary.



115
116
117
# File 'lib/rinda2/ring/finger.rb', line 115

def to_a
  @rings
end