I'm working on a fairly complex Xamarin.Forms application. We make a lot of REST requests. It's been reported that our application isn't respecting DNS failover for load balancing in a timely manner, so I started investigating. I'm running dnsmasq so I can look at when the app makes DNS requests. The code is currently using HttpWebRequest
, and I noticed it's making DNS queries at least 10 minutes apart.
I understand part of this is because most .NET networking bits use a keepalive connection. I certainly see a higher rate of DNS queries if I force the headers to not use keepalive, but that adds network overhead so it's not a desirable solution. But I didn't initially see a clear way to control how HttpWebRequest
makes DNS queries.
It looked promising that I could get its ServicePoint
property and set the ConnectionLeaseTimeout
on that. Unfortunately, that throws NotImplementedException
in Xamarin so it's not going to be part of any solution.
I thought that perhaps HttpClient
would be more configurable. I see a lot of discussion about how to use it properly, and that if you do it that way you need to set ServicePointManager.DnsRefreshTimeout
to a smaller value for use cases where you want to expect DNS to update frequently. But this is usually done in conjunction with getting the ServicePoint
for the deisred endpoint and also modifying ConnectionLeaseTimeout
, which isn't possible.
I've been testing with a very simple app that reuses an HttpClient and makes the same request any time I push a button. Slap this ViewModel behind some Xaml with a button:
using System;
using Xamarin.Forms;
using System.Net.Http;
using System.Net;
namespace TestDns {
public class MainPageViewModel {
private const string _URL = "http://www.example.com";
private HttpClient _httpClient;
private ServicePoint _sp;
public MainPageViewModel() {
var sp = ServicePointManager.FindServicePoint(new Uri(_URL));
_sp = sp;
//_sp.ConnectionLeaseTimeout = 100; // throws NIE
_httpClient = new HttpClient();
ServicePointManager.DnsRefreshTimeout = 100;
}
public Command WhenButtonIsClicked {
get {
return new Command(() => SendRequest());
}
}
private async void SendRequest() {
Console.WriteLine($"{_sp.CurrentConnections}");
var url = "http://www.example.com";
var response = await _httpClient.GetAsync(url);
Console.WriteLine($"{response.Content}");
}
}
}
I didn't expect ConnectionLeaseTimeout
to throw. I expected this code to only cache DNS requests for 100ms, I was going to choose a more reasonable timeframe like 2-3 minutes in more production-oriented tests. But since I can't get this simple example to function like I want, it seems moot to increase the delays.
Surely someone else has had this problem in a Xamarin app? Is my only solution going to be to look deeper and try to use native networking constructs?