Class: DNSSD::ServiceInstance
- Inherits:
-
Object
- Object
- DNSSD::ServiceInstance
- Includes:
- ResourceCache
- Defined in:
- lib/dns-sd/service_instance.rb
Overview
A single instance of a service.
This is where the rubber hits the road: servers to talk to, and instance metadata, is all under here.
Instance Attribute Summary collapse
-
#fqdn ⇒ Resolv::DNS::Name
readonly
The FQDN of this service instance.
-
#name ⇒ String
readonly
The name of this service instance.
-
#service ⇒ DNSSD::Service
readonly
The generic service which this instance implements.
Instance Method Summary collapse
-
#data ⇒ Hash<String, String or nil>
Return the metadata for the service instance.
-
#initialize(fqdn) ⇒ ServiceInstance
constructor
Create the service instance.
-
#targets ⇒ Array<DNSSD::Target>
The things to connect to for this service instance.
Constructor Details
#initialize(fqdn) ⇒ ServiceInstance
Create the service instance.
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/dns-sd/service_instance.rb', line 44 def initialize(fqdn) unless fqdn.is_a?(Resolv::DNS::Name) raise ArgumentError, "FQDN must be a Resolv::DNS::Name (got an instance of #{fqdn.class})" end @fqdn = fqdn @name = fqdn[0].to_s @service = DNSSD::Service.new(Resolv::DNS::Name.new(fqdn[1..-1])) end |
Instance Attribute Details
#fqdn ⇒ Resolv::DNS::Name (readonly)
The FQDN of this service instance.
29 30 31 |
# File 'lib/dns-sd/service_instance.rb', line 29 def fqdn @fqdn end |
#name ⇒ String (readonly)
The name of this service instance.
This is just the left-most component of the service instance FQDN, and can, as such, contain practically anything at all.
23 24 25 |
# File 'lib/dns-sd/service_instance.rb', line 23 def name @name end |
#service ⇒ DNSSD::Service (readonly)
The generic service which this instance implements.
If you happen to forget what protocol to use, this might come in handy.
38 39 40 |
# File 'lib/dns-sd/service_instance.rb', line 38 def service @service end |
Instance Method Details
#data ⇒ Hash<String, String or nil>
Return the metadata for the service instance.
RFC6763 s. 6 describes a means by which specially formatted TXT records can be used to provide metadata for a service instance. If your services populate such data, you can access it here.
68 69 70 71 72 73 74 75 76 77 |
# File 'lib/dns-sd/service_instance.rb', line 68 def data {}.tap do |data| cached_resources(@fqdn, Resolv::DNS::Resource::IN::TXT).each do |rr| rr.strings.each do |s| s =~ /\A([^=]+)(=(.*))?$/ data[$1.to_sym] = $3 end end end end |
#targets ⇒ Array<DNSSD::Target>
The things to connect to for this service instance.
This is what you're here for, I'll bet. Everything comes down to this.
Each DNSSD::Target object in this list contains an FQDN (#hostname) and
port (#port) to connect to, which you can walk in order in order to
get to something that will talk to you.
Every time you call this method, even if the records are cached, you may
get the targets in a different order. This is because we automatically
sort the list of targets according to the rules for SRV record priority
and weight. Thus, it is recommended that every time you want to make a
connection to the service instance, you call #targets again, both
because the DNS records may have expired (and thus will be re-queried),
but also because it'll ensure that the weight-based randomisation of the
server list is respected.
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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/dns-sd/service_instance.rb', line 96 def targets [].tap do |list| left = cached_resources(@fqdn, Resolv::DNS::Resource::IN::SRV) # Happily, this algorithm, whilst a bit involved, maps quite directly # to the description from RFC2782, page 4, of which parts are quoted as # appropriate below. A practical example of how this process runs is # described in the test suite, also, which might help explain what's # happening. # # > This process is repeated for each Priority. until left.empty? # > A client MUST attempt to contact the target host with the # > lowest-numbered priority it can reach; target hosts with the # > same priority SHOULD be tried in an order defined by the weight # > field. prio = left.map { |rr| rr.priority }.uniq.min # > The following algorithm SHOULD be used to order the SRV RRs of the # > same priority: candidates = left.select { |rr| rr.priority == prio } left -= candidates # > arrange all SRV RRs (that have not been ordered yet) in any # > order, except that all those with weight 0 are placed at the # > beginning of the list. # # Because it makes it easier to test, I like to sort by weight and # name (<lawyer>it counts as "any order"</lawyer>). This does mean # that all the zero-weight entries come back in name order, but # if you don't want that behaviour, it's easy enough to give # everything weight=1 and they'll be properly randomised. candidates.sort_by! { |rr| [rr.weight, rr.target.to_s] } # > Continue the ordering process until there are no unordered SRV # > RRs. until candidates.empty? # > Compute the sum of the weights of those RRs, and with each RR # > associate the running sum in the selected order. Then choose a # > uniform random number between 0 and the sum computed # > (inclusive) selector = rand(candidates.inject(1) { |n, rr| n + rr.weight }) # > select the RR whose running sum value is the first in the # > selected order which is greater than or equal to the random # > number selected chosen = candidates.inject(0) do |n, rr| break rr if n + rr.weight >= selector n + rr.weight end # > Remove this SRV RR from the set of the unordered SRV RRs candidates.delete(chosen) list << DNSSD::Target.new(chosen.target.to_s, chosen.port) end end end end |