Comments (9)
I used IEnumerable<...> everywhere, you may prefer arrays or other ... enumerables π (hell, IEnumerable<...> may cause some issues).
Right, I don't like to use IEnumerable for those kind of things, especially because it gives kinda random results every time it gets iterated ;)
I think, for this library, it is enough to have the existing methods and just extend the returned ServiceHostEntry
to contain the missing fields (this would be a non-breaking change)
I'd rather not implement any logic on top of that within this library and leave that up to consumers.
You could use it with the example you have there, or do something totally different ;)
from dnsclient.net.
Thanks for the reply ;)
That actually makes more sense, yup
from dnsclient.net.
Hi @RobThree,
Those methods are meant for convenience / to simplify the resolve part. HostEntry
is a framework type, I added Port
to it because services usually have one ^^
Priority, weight and ttl could be added to that as properties, too I guess.
That being said, the resolved result will already be ordered, you don't usually have to do that again.
A DNS Server usually decides to re-order results to give you some kind of load balancing (e.g. in case there are multiple endpoints for the same service). So, if you order it again, you'll actually loose that.
from dnsclient.net.
Those methods are meant for convenience / to simplify the resolve part.
That was clear to me π
Priority, weight and ttl could be added to that as properties, too I guess.
That was, sort of, the intent of my question indeed.
That being said, the resolved result will already be ordered, you don't usually have to do that again.
I don't see any explicit ordering being done? Or I'm missing it?
A DNS Server usually decides to re-order results to give you some kind of load balancing (e.g. in case there are multiple endpoints for the same service).
The priority and weight are intended for clients to be able to determine which record to pick. Assume the following SRV:
prio 0 : weight: 10 port: 9991 host: hostA.example.org
prio 10: weight: 10 port: 9992 host: hostB.example.org
prio 10: weight: 40 port: 9993 host: hostC.example.org
prio 10: weight: 50 port: 9994 host: hostD.example.org
prio 20: weight: 1 port: 9995 host: hostE.example.org
prio 20: weight: 9 port: 9996 host: hostF.example.org
A client should first try host A (highest priority, only one in it's "group"). If host A fails, the client should pick one from hosts B, C or D with a 10% 'chance' of picking host B, 40% chance of picking host C and 50% chance of picking host D. If the picked host (either B, C or D) fails again, the next priority group should be used: hosts E and F. Again, the client should choose between E or F by using a 10% chance of picking E and 90% chance of F.
This is how the 'load balancing' works. The weights and priorities determine when, and how often, a host should be used (assuming the client respects the actual SRV record, ofcourse).
So you see, when using SRV records you will need the priority and weights (the TTL can be useful to 'have' to, but I can live without).
I can do the SRV query just fine, don't get me wrong; I simply use myLookup.Query("_service._tcp.example.com", QueryType.SRV)
and that works fine. I just think it's odd that the ResolveService
extensionmethod gets rid of the information required for clients to work properly.
I will shortly post a proposed implementation of the ResolveService
extensionmethod which, I think, is better suited. Ofcourse, it's up to you to decide wether you like to use it or ignore it π
from dnsclient.net.
I think the simplified implementation came mostly from me using this together with Consul for service discovery. Consul's DNS endpoint always returns filtered and weighted entries already.
But you are right, for other use-cases that information (prio/weight) is needed.
Sure, go ahead and send a PR ;)
from dnsclient.net.
Sorry, no PR (yet) but just to demonstrate my idea...
First, a little setup:
public class SrvRecord
{
public string HostName { get; set; }
public int Port { get; set; }
public IPAddress[] AddressList { get; set; }
public int Priority { get; set; }
public int Weight { get; set; }
}
Assume this is an SRV record returned by a DNS server (close to what you currently have, except I didn't bother with ResourceRecordInfo
and stuff... Remember, this is just to demonstrate.
Next, assume the SRV records as mentioned here. The ResolveService
method could return a structure that does two things:
- Allows access to all returned records (all 6 SRV records in our example)
- Takes care of priorities/weights so the user doesn't have to (and potentially get it wrong)
So, let's make SrvResult
(or whatever) class that does just that:
/// <summary>
/// Class to hold results from an SRV lookup
/// </summary>
public class SrvResult : IEnumerable<SrvRecord>
{
// We need an RNG to pick "random" records
private static readonly Random _rng = new Random();
// We need to keep track of the SRV records this result represents
private IEnumerable<SrvRecord> _records;
/// <summary>
/// Gets the groups of SRV records (grouped by priority)
/// </summary>
public IEnumerable<IEnumerable<SrvRecord>> Groups => _records.GroupBy(r => r.Priority)
.OrderBy(g => g.Key).Select(g => g);
/// <summary>
/// Creates a new instance of SrvResult
/// </summary>
/// <param name="records">The SRV records this SrvResult represents</param>
public SrvResult(IEnumerable<SrvRecord> records)
{
if (records is null || !records.Any())
throw new ArgumentNullException(nameof(records));
_records = records;
}
/// <summary>
/// "Enumerates" SRV records
/// </summary>
/// <returns>Returns SRV records based on priorities and weights in an order you can
/// immediately consume</returns>
public IEnumerator<SrvRecord> GetEnumerator()
{
// We return exactly ONE record from each prioritygroup
foreach (var group in Groups)
{
// Get all records from this group (same priority)
var grouprecords = group.ToArray();
// Determine total weight
var totalweight = grouprecords.Sum(r => r.Weight);
if (totalweight == 0)
{
// Return random record
yield return grouprecords[_rng.Next(grouprecords.Length)];
}
else
{
// See http://www.perlmonks.org/?node_id=242751 for "magic" below
// In short:
//
// 1. Pick a random number less than the total
// 2. At each position of the array, subtract the weight at that position
// 3. As soon as the result is negative, return that position
var rnd_val = _rng.Next(totalweight);
var i = -1;
while (rnd_val >= 0)
rnd_val -= grouprecords[++i].Weight;
//Return record based on weight
yield return grouprecords[i];
}
}
}
/// <summary>
/// Explicit interface implementation
/// </summary>
/// <returns>Returns SRV records based on priorities and weights in an order you can
/// immediately consume</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<SrvRecord>)this).GetEnumerator();
}
}
And now, let's demonstrate:
private static void Main(string[] args)
{
// Assume we have the following result from a lookup (so, we "fake" a result)
var result = new SrvResult(new[]
{
new SrvRecord { Priority = 0, Weight = 10, Port = 9991, HostName = "hostA.example.org", AddressList = new[] { IPAddress.Parse("1.2.3.4") } },
new SrvRecord { Priority = 10, Weight = 10, Port = 9992, HostName = "hostB.example.org", AddressList = new[] { IPAddress.Parse("1.2.3.5") } },
new SrvRecord { Priority = 10, Weight = 40, Port = 9993, HostName = "hostC.example.org", AddressList = new[] { IPAddress.Parse("1.2.3.6") } },
new SrvRecord { Priority = 10, Weight = 50, Port = 9994, HostName = "hostD.example.org", AddressList = new[] { IPAddress.Parse("1.2.3.7") } },
new SrvRecord { Priority = 20, Weight = 1, Port = 9995, HostName = "hostE.example.org", AddressList = new[] { IPAddress.Parse("1.2.3.8") } },
new SrvRecord { Priority = 20, Weight = 9, Port = 9996, HostName = "hostF.example.org", AddressList = new[] { IPAddress.Parse("1.2.3.9") } }
});
// Simple demonstration we can get to all individual records if we want to
Console.WriteLine($"Result: {result.Groups.SelectMany(r => r).Count()} records total");
// Simple demonstration to show records are returned as specified by their priority / weight
// You may need to hit a key several times to see that the records returned will vary on each iteration
while (true)
{
Console.WriteLine("Iterating the result:");
// This is how a "client" could use the result to approach hosts in the desired
// order without bothering with weights, priorities etc.
foreach (var entry in result)
Console.WriteLine($"{entry.HostName}:{entry.Port} ({string.Join(",", entry.AddressList.Select(a => a.ToString()))})");
Console.WriteLine("Press any key to iterate again");
Console.ReadKey();
}
}
Every time the result is iterated you will see the hosts in the order specified by the SRV priorities and then, within a priority, based on the weights within that priority, a single host.
The SrvResult
(meh name...) offers access to all SrvRecords
should a client want access to the "raw data". Another property (that doesn't return an IEnumerable of IEnumerables...) could be added:
/// <summary>
/// Gets the SRV records (ordered by priority, then by hostname)
/// </summary>
public IEnumerable<SrvRecord> Records => _records.OrderBy(r => r.Priority)
.ThenBy(r => r.HostName);
The SrvResult
, however, also implements the IEnumerable<SrvRecord>
interface. It can be iterated and hosts will be returned in order a client should approach them (should a previous host fail or whatever).
I used IEnumerable<...>
everywhere, you may prefer arrays or other ... enumerables π (hell, IEnumerable<...> may cause some issues).
Also, by the way, offtopic but still: I like your library a lot! Good work! π I noticed you are moving to DnsOptions
to pass to the DnsLookup
constructor; good idea! I'm totally on board with that! Looking forward to the next release that contains this change!
from dnsclient.net.
... especially because it gives kinda random results every time it gets iterated ;)
... which, in this particular case, is exactly what was intended π
I think, for this library, it is enough to have the existing methods and just extend the returned ServiceHostEntry to contain the missing fields (this would be a non-breaking change)
Sure, that's an option too. Good enough for me π
I'd rather not implement any logic on top of that within this library and leave that up to consumers.
If you want such logic in your library or not is, ofcourse, up to you. I'm fine with that. However, maybe another option would be to create a helper-class (or even extension method) that implements the logic 'outside' of the result itself.
You could use it with the example you have there, or do something totally different ;)
I know π I'm using the Query("...", QueryType.SRV)
now and populate my own object with the results. That works fine. It's just that it's not simple to get the "picking" or "selection" of SRV records right; I've seen many plain wrong implementations on the web so offering the correct implementation in your library would be (IMHO, to be clear) a nice feature. But, again, it's up to you. Thanks for your feedback and thanks for (considering) adding the Priority and Weight "back" in. π
from dnsclient.net.
Hey @RobThree
Just revisited this one again, I think the simplest solution is when I add the original SrvRecord
to the ServiceHostEntry
public class ServiceHostEntry : IPHostEntry
{
public int Port { get; set; }
public SrvRecord SrvRecord { get; set; } <== NEW
}
The SrvRecord has the weight and prio on it so you can further filter/sort the results after calling ResolveService
.
What do you think?
from dnsclient.net.
What do you think?
It's been a while since I opened this issue. I'll need to dive back in the matter and (re)familiarize myself.
From what I see right now: A SRV lookup results in one or more ServiceHostEntry
objects (or none); each ServiceHostEntry
each having it's own AddressList[]
....
Without looking up anything, only basing my opinion on information in this issue...
Wouldn't an SrvRecord property make it awkward to get to the weight and priority? I can get straight to the port and addresslist (which always will contain one, and only one, address, right?) but would have to use the SrvRecord property to get to the Priority and Weight?
Why not:
public class ServiceHostEntry : IPHostEntry
{
public int Port { get; set; }
public int Priority { get; set; }
public int Weight { get; set; }
}
I may be way off base though; as said: it's been a while and I'd have to look longer/deeper. I will, but am currently a bit (too) busy.
from dnsclient.net.
Related Issues (20)
- Stackoverflow error after millions of lookups HOT 7
- Hi rate of garbage collection HOT 3
- Star CNAME lookup HOT 7
- Error System.TypeInitializationException: βThe type initializer for βMongoDB.Driver.Core.Misc.DnsClientWrapperβ threw an exception.β HOT 7
- Are there plans to support DNAME & LOC record types? HOT 2
- iOS build issues while adding DNSClient package HOT 1
- ios build issues
- How to get the parent nameservers? HOT 1
- HasError=false / no error message when hostname (not FQDN) has no ip address HOT 2
- NameServer timed out HOT 1
- mock-friendly API for ResolveService(Async) HOT 2
- Trailing `.` in MxRecord.DomainName.Value HOT 1
- Memory-leak in latest versions in combination with full .net framework HOT 1
- Exceptions in DnsClient.LookupClient.HandleDnsResponeParseException HOT 2
- How can i set my own dns servers?
- Characters should not be escaped within the audit trail <character-string>
- Verifying MX records? HOT 1
- Incorrect usage of Name from NRPT table HOT 8
- Move cache checks outside loop in ResolveQuery and ResolveQueryAsync HOT 2
- Timeout when querying TXT records HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dnsclient.net.