類別 Rinda::RingServer

透過 UDP 廣播,RingServer 允許 Rinda::TupleSpace 定位。預設服務位置使用下列步驟

  1. RingServer 開始在網路廣播 UDP 位址上監聽。

  2. RingFinger 傳送一個 UDP 封包,其中包含 DRb URI,它會在其中等待回覆。

  3. RingServer 收到 UDP 封包,並透過 DRb 服務連線回提供的 DRb URI

RingServer 需要一個 TupleSpace

ts = Rinda::TupleSpace.new
rs = Rinda::RingServer.new

RingServer 也可以在多播位址上監聽公告。這允許多個 RingServer 在同一個主機上執行。若要使用網路廣播和多播

ts = Rinda::TupleSpace.new
rs = Rinda::RingServer.new ts, %w[Socket::INADDR_ANY, 239.0.0.1 ff02::1]

公開類別方法

new(ts, addresses=[Socket::INADDR_ANY], port=Ring_PORT) 按一下以切換來源

port 上的 addresses 中宣告 ts

如果省略 addresses,則只使用 UDP 廣播位址。

addresses 可以包含多個位址。如果在 addresses 中提供多播位址,則 RingServer 會監聽多播查詢。

如果您使用 IPv4 多播,您可能需要設定加入多播群組的入站介面的位址。

ts = Rinda::TupleSpace.new
rs = Rinda::RingServer.new(ts, [['239.0.0.1', '9.5.1.1']])

您可以將位址設定為 Array ObjectArray 的第一個元素是多播位址,第二個元素是入站介面位址。如果省略第二個元素,則使用 ‘0.0.0.0’。

如果您使用 IPv6 多播,您可能需要設定本機介面位址和入站介面索引

rs = Rinda::RingServer.new(ts, [['ff02::1', '::1', 1]])

第一個元素是多播位址,第二個元素是入站介面位址。第三個元素是入站介面索引。

目前沒有簡單的方法可以透過名稱取得介面索引。

如果省略第二個元素,則使用 ‘::1’。如果省略第三個元素,則使用 0(預設介面)。

# File lib/rinda/ring.rb, line 94
def initialize(ts, addresses=[Socket::INADDR_ANY], port=Ring_PORT)
  @port = port

  if Integer === addresses then
    addresses, @port = [Socket::INADDR_ANY], addresses
  end

  @renewer = Renewer.new

  @ts = ts
  @sockets = []
  addresses.each do |address|
    if Array === address
      make_socket(*address)
    else
      make_socket(address)
    end
  end

  @w_services = write_services
  @r_service  = reply_service
end

公開執行個體方法

do_reply() 按一下以切換來源

TupleSpace 中提取查詢元組,並將其 DRb 物件傳送給本機 TupleSpace 的位址。

# File lib/rinda/ring.rb, line 218
def do_reply
  tuple = @ts.take([:lookup_ring, nil], @renewer)
  Thread.new { tuple[1].call(@ts) rescue nil}
rescue
end
do_write(msg) 按一下以切換來源

msg 中萃取回應 URI,並將其新增至 TupleSpace,其中會由 reply_service 擷取以進行通知。

# File lib/rinda/ring.rb, line 193
def do_write(msg)
  Thread.new do
    begin
      tuple, sec = Marshal.load(msg)
      @ts.write(tuple, sec)
    rescue
    end
  end
end
make_socket(address, interface_address=nil, multicast_interface=0) 按一下以切換來源

address 處建立一個 socket

如果 address 是多播地址,則可以選擇設定 interface_addressmulticast_interface

建立的 socket 會繫結至 interface_address。如果您使用 IPv4 多播,則會使用 interface_address 的介面作為輸入介面。如果省略或將 interface_address 設為 nil,則會使用 ‘0.0.0.0’ 或 ‘::1’。

如果您使用 IPv6 多播,則會使用 multicast_interface 作為輸入介面。multicast_interface 是網路介面索引。如果省略 multicast_interface,則會使用 0(預設介面)。

# File lib/rinda/ring.rb, line 132
def make_socket(address, interface_address=nil, multicast_interface=0)
  addrinfo = Addrinfo.udp(address, @port)

  socket = Socket.new(addrinfo.pfamily, addrinfo.socktype,
                      addrinfo.protocol)

  if addrinfo.ipv4_multicast? or addrinfo.ipv6_multicast? then
    if Socket.const_defined?(:SO_REUSEPORT) then
      socket.setsockopt(:SOCKET, :SO_REUSEPORT, true)
    else
      socket.setsockopt(:SOCKET, :SO_REUSEADDR, true)
    end

    if addrinfo.ipv4_multicast? then
      interface_address = '0.0.0.0' if interface_address.nil?
      socket.bind(Addrinfo.udp(interface_address, @port))

      mreq = IPAddr.new(addrinfo.ip_address).hton +
        IPAddr.new(interface_address).hton

      socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
    else
      interface_address = '::1' if interface_address.nil?
      socket.bind(Addrinfo.udp(interface_address, @port))

      mreq = IPAddr.new(addrinfo.ip_address).hton +
        [multicast_interface].pack('I')

      socket.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
    end
  else
    socket.bind(addrinfo)
  end

  socket
rescue
  socket = socket.close if socket
  raise
ensure
  @sockets << socket if socket
end
reply_service() 按一下以切換來源

建立一個執行緒,以從 TupleSpace 通知正在等待的用戶端。

# File lib/rinda/ring.rb, line 206
def reply_service
  Thread.new do
    loop do
      do_reply
    end
  end
end
shutdown() 按一下以切換來源

關閉 RingServer

# File lib/rinda/ring.rb, line 227
def shutdown
  @renewer.renew = false

  @w_services.each do |thread|
    thread.kill
    thread.join
  end

  @sockets.each do |socket|
    socket.close
  end

  @r_service.kill
  @r_service.join
end
write_services() 按一下以切換來源

建立執行緒以擷取 UDP 封包,並傳遞至 do_write 進行解碼。

# File lib/rinda/ring.rb, line 178
def write_services
  @sockets.map do |s|
    Thread.new(s) do |socket|
      loop do
        msg = socket.recv(1024)
        do_write(msg)
      end
    end
  end
end