将不同 URL 的 HTTP 请求名称解析缓存到同一主机。可能的?
Cache HTTP request name resolution for different URLs to same host. Possible?
问题摘要:我需要调用 HTTP 资源 A,同时在 同一主机 上使用先前对资源 B 的 HTTP 请求的名称解析。
案例 1. 对同一资源的连续调用在第一次调用后产生 更快的结果。
Profiler 告诉我第一次和第二次调用之间的区别在于 DNS 名称解析 (GetHostAddresses)
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/b.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/b.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
案例 2. 对同一主机上不同资源的连续调用产生 相同的延迟。
Profiler 告诉我他们都调用了 DNS 名称解析。
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/a.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/b.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
我想知道为什么第 2 次调用不能使用第一次调用的 DNS 缓存?同一个主机。
主要问题 - 如何改变它?
EDIT 以上行为还涵盖了 HttpClient class 的使用。看来这是我使用的少数网络服务器特有的,其他服务器上不会发生此问题。我无法弄清楚具体发生了什么,但我怀疑有问题的网络服务器(Amazon CloudFront 和 Akamai)在提供服务后强制关闭连接,忽略我的请求 keep-alive headers。我现在要关闭这个,因为不可能提出一个有意识的问题..
System.Net.Http.HttpClient
不存在您的问题,请试试。它可以重用现有连接(此类调用不需要 DNS 缓存)。看起来这正是您想要实现的目标。作为奖励,它支持 HTTP/2(可以在 HttpClient
实例创建时通过 属性 赋值启用)。
WebRequest
是古老的,Microsoft 不推荐用于新开发。在 .NET 5 中 HttpClient
相当快(两倍?)。
每个应用程序创建一次 HttpClient
实例 (link)。
private static readonly HttpClient client = new HttpClient();
模拟您的请求。注意 await
仅在标记为 async
.
的方法中可用
string text = await client.GetStringAsync("https://www.somehost.com/resources/b.txt");
您也可以在不产生并发线程的情况下一次执行多个请求。
string[] urls = new string[]
{
"https://www.somehost.com/resources/a.txt",
"https://www.somehost.com/resources/b.txt"
};
List<Task<string>> tasks = new List<Task<string>>();
foreach (string url in urls)
{
tasks.Add(client.GetStringAsync(url));
}
string[] results = await Task.WhenAll(tasks);
如果您不熟悉异步编程,例如async/await
,从this article开始。
您还可以设置一次处理请求的数量限制。让我们将相同的请求执行 1000 次,一次限制为 10 个请求。
static async Task Main(string[] args)
{
Stopwatch sw = new StopWatch();
string url = "https://www.somehost.com/resources/a.txt";
using SemaphoreSlim semaphore = new SemaphoreSlim(10);
List<Task<string>> tasks = new List<Task<string>>();
sw.Start();
for (int i = 0; i < 1000; i++)
{
await semaphore.WaitAsync();
tasks.Add(GetPageAsync(url, semaphore));
}
string[] results = await Task.WhenAll(tasks);
sw.Stop();
Console.WriteLine($"Elapsed: {sw.Elapsemilliseconds}ms");
}
private static async Task GetPageAsync(string url, SemaphoreSlim semaphore)
{
try
{
return await client.GetStringAsync(url);
}
finally
{
semaphore.Release();
}
}
你可以测量时间。
问题摘要:我需要调用 HTTP 资源 A,同时在 同一主机 上使用先前对资源 B 的 HTTP 请求的名称解析。
案例 1. 对同一资源的连续调用在第一次调用后产生 更快的结果。
Profiler 告诉我第一次和第二次调用之间的区别在于 DNS 名称解析 (GetHostAddresses)
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/b.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/b.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
案例 2. 对同一主机上不同资源的连续调用产生 相同的延迟。 Profiler 告诉我他们都调用了 DNS 名称解析。
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/a.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
var request = (HttpWebRequest)WebRequest.Create("https://www.somehost.com/resources/b.txt");
using (var response = (HttpWebResponse)request.GetResponse()) {}
我想知道为什么第 2 次调用不能使用第一次调用的 DNS 缓存?同一个主机。
主要问题 - 如何改变它?
EDIT 以上行为还涵盖了 HttpClient class 的使用。看来这是我使用的少数网络服务器特有的,其他服务器上不会发生此问题。我无法弄清楚具体发生了什么,但我怀疑有问题的网络服务器(Amazon CloudFront 和 Akamai)在提供服务后强制关闭连接,忽略我的请求 keep-alive headers。我现在要关闭这个,因为不可能提出一个有意识的问题..
System.Net.Http.HttpClient
不存在您的问题,请试试。它可以重用现有连接(此类调用不需要 DNS 缓存)。看起来这正是您想要实现的目标。作为奖励,它支持 HTTP/2(可以在 HttpClient
实例创建时通过 属性 赋值启用)。
WebRequest
是古老的,Microsoft 不推荐用于新开发。在 .NET 5 中 HttpClient
相当快(两倍?)。
每个应用程序创建一次 HttpClient
实例 (link)。
private static readonly HttpClient client = new HttpClient();
模拟您的请求。注意 await
仅在标记为 async
.
string text = await client.GetStringAsync("https://www.somehost.com/resources/b.txt");
您也可以在不产生并发线程的情况下一次执行多个请求。
string[] urls = new string[]
{
"https://www.somehost.com/resources/a.txt",
"https://www.somehost.com/resources/b.txt"
};
List<Task<string>> tasks = new List<Task<string>>();
foreach (string url in urls)
{
tasks.Add(client.GetStringAsync(url));
}
string[] results = await Task.WhenAll(tasks);
如果您不熟悉异步编程,例如async/await
,从this article开始。
您还可以设置一次处理请求的数量限制。让我们将相同的请求执行 1000 次,一次限制为 10 个请求。
static async Task Main(string[] args)
{
Stopwatch sw = new StopWatch();
string url = "https://www.somehost.com/resources/a.txt";
using SemaphoreSlim semaphore = new SemaphoreSlim(10);
List<Task<string>> tasks = new List<Task<string>>();
sw.Start();
for (int i = 0; i < 1000; i++)
{
await semaphore.WaitAsync();
tasks.Add(GetPageAsync(url, semaphore));
}
string[] results = await Task.WhenAll(tasks);
sw.Stop();
Console.WriteLine($"Elapsed: {sw.Elapsemilliseconds}ms");
}
private static async Task GetPageAsync(string url, SemaphoreSlim semaphore)
{
try
{
return await client.GetStringAsync(url);
}
finally
{
semaphore.Release();
}
}
你可以测量时间。