【发布时间】:2014-12-29 19:31:28
【问题描述】:
我是异步编程的新手,我正在尝试使用 httpclient 为页面内容触发批量 URL 请求。 这是我的尝试:
private async void ProcessUrlAsyncWithHttp(HttpClient httpClient, string purl)
{
Stopwatch sw = new Stopwatch();
sw.Start();
HttpResponseMessage response = null;
try
{
Interlocked.Increment(ref _activeRequestsCount);
var request = new HttpRequestMessage()
{
RequestUri = new Uri(purl),
Method = HttpMethod.Get,
};
request.Headers.TryAddWithoutValidation("User-Agent", "MozillaMozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36");
request.Headers.TryAddWithoutValidation("Accept", "text/html,*.*");
request.Headers.TryAddWithoutValidation("Connection", "Keep-Alive");
request.Headers.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate, sdch");
request.Headers.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.8");
response = await httpClient.SendAsync(request).ConfigureAwait(false);
string html = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
response.Dispose();
if (IsCaptcha(html)) throw new Exception("Captcha was returned");
request.Dispose();
Interlocked.Increment(ref _successfulCalls);
}
catch (HttpRequestException hex)
{
Console.WriteLine("http:" + hex.Message);
Interlocked.Increment(ref _failedCalls);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().AssemblyQualifiedName + " " + ex.Message);
Interlocked.Increment(ref _failedCalls);
}
finally
{
Interlocked.Decrement(ref _activeRequestsCount);
Interlocked.Decrement(ref _itemsLeft);
if (response != null) response.Dispose();
if (httpClient != null) httpClient.Dispose();
sw.Stop();
DateTime currentTime = DateTime.UtcNow;
TimeSpan elapsedTillNow = (currentTime - _overallStartTime).Duration();
Console.WriteLine("Left:" + _itemsLeft + ", Current execution:" + sw.ElapsedMilliseconds + " (ms), Average execution:" + Math.Round((elapsedTillNow.TotalMilliseconds / (_totalItems - _itemsLeft)), 0) + " (ms)");
lock(_syncLock)
{
if (_itemsLeft == 0)
{
_overallEndTime = DateTime.UtcNow;
this.DisplayTestResults();
}
}
}
}
如您所见,我将一个 httpclient 传递给该函数,并且每次下载 URL 时它都会被销毁。我知道这是一种矫枉过正的做法,理想情况下我们应该重用 httpclient。但是由于我不能为每个 URL 使用具有不同代理的单个 httpclient(处理程序需要传递给 httpclient 的构造函数并且不能更改,因此如果不重新创建 httpclient 对象就不能提供新的代理),我需要使用这个接近。
在调用方,我有一个非常基本的代码:
public async void TestAsyncWithHttp()
{
ServicePointManager.DefaultConnectionLimit = 10;
//ServicePointManager.UseNagleAlgorithm = false;
List<string> urlList = SetUpURLList();
urlList = urlList.GetRange(1, 50);
_itemsLeft = urlList.Count();
_totalItems = _itemsLeft;
List<string> proxies = new List<string>();
proxies.Add("124.161.94.8:80");
proxies.Add("183.207.228.8:80");
proxies.Add("202.29.97.5:3128");
proxies.Add("210.75.14.158:80");
proxies.Add("203.100.80.81:8080");
proxies.Add("218.207.172.236:80");
proxies.Add("218.59.144.120:81");
proxies.Add("218.59.144.95:80");
proxies.Add("218.28.35.234:8080");
proxies.Add("222.88.236.236:83");
Random rnd = new Random();
foreach (string url in urlList)
{
int ind = rnd.Next(0, proxies.Count-1);
var httpClientHandler = new HttpClientHandler
{
Proxy = new WebProxy(proxies.ElementAt(ind), false),
UseProxy = true
};
HttpClient httpClient = new HttpClient(httpClientHandler);
//HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(2);
ProcessUrlAsyncWithHttp(httpClient, url);
}
}
问题是: 1)为什么每个请求都会关闭 TCP 端口。我想打开端口的最大连接数并在调用中重用它们。例如,在上面的示例中,我可以有 10 个并发连接。因此,我希望它打开 10 个 TCP 端口,然后其余 40 个请求可以串联使用这 10 个端口。这是 httpwebrequest 中预期的正常行为。我有一个使用 httpwebrequest 的工作代码,它描述了这种重用端口的行为。可以根据需要为任何想要查看的人发布该代码。因此,尽管它基于 httpwebrequest,但 httpclient 并没有模仿这种行为,这有点奇怪。
2) 对于此类调用,我们如何将 autoredirect 分配为 false? 3)我打算将此功能用于多个呼叫 - 说大约 50K。代码编写方式中可能需要更正的任何错误 4) 假设我以某种方式设法使用单个 httpclient 对象而不是每个请求一个对象。有什么方法可以确保我读取所有这些单独请求的 cookie 并在必要时更改它们,同时记住我有一个用于整个 URL 请求集的 httpclient 类?
Tks 卡洛尔
【问题讨论】:
-
连接重用不依赖于 HttpClient 重用。每次只需创建一个新客户。连接使用标准 .NET 池基础结构进行池化。
-
是的,我明白这一点。但是使用 httpwebrequest/response 编写了一个类似的代码段,第一次打开的 TCP 端口将用于以后的请求。所以问题是为什么使用类似的 httpclient 代码不会使用端口(当我们知道它在内部使用 httpwebrequest/response 时)
标签: asynchronous dotnet-httpclient