Мне нужно выполнять много веб-запросов параллельно (примерно 1000 в секунду).У некоторых из этих веб-запросов истекает время (сервер не отвечает), и я должен быть в состоянии справиться с этим.Многие запросы нужно будет направлять через прокси (и есть много разных прокси, которые нужно будет использовать).
Из-за необходимости использования прокси один HttpClient не может быть повторно использован (что является обычной практикой для выполнения нескольких запросов).Таким образом, наивный способ сделать это состоит в том, чтобы запустить несколько потоков, и в каждом из них создать новый HttpClient со своим собственным обработчиком и выполнить запрос.Но это все еще оставляет проблему оставления многих открытых розеток.Так что на самом деле это не вариант.
На данный момент я делаю следующее:
class GetRequest
{
private int timeout;
private WebRequest req;
private string url;
private ManualResetEvent allDone;
private string response;
private IWebProxy proxy;
public GetRequest(string url, int timeout = 3000, IWebProxy proxy = null)
{
this.url = url;
this.timeout = timeout;
this.response = "";
this.proxy = proxy;
}
public string Get()
{
allDone = new ManualResetEvent(false);
req = WebRequest.Create(url);
req.Proxy = proxy;
req.Headers[HttpRequestHeader.UserAgent] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36";
req.Timeout = this.timeout;
req.BeginGetResponse(GetResponseCallback, req);
allDone.WaitOne(timeout);
Cleanup();
return this.response;
}
private HttpWebResponse webresponse;
private Stream streamResponse;
private StreamReader streamRead;
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
// End the operation
webresponse = (HttpWebResponse)req.EndGetResponse(asynchronousResult);
streamResponse = webresponse.GetResponseStream();
streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
this.response = responseString;
// Close the stream object
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse
webresponse.Close();
allDone.Set();
}
~GetRequest()
{
Cleanup();
}
public void Cleanup()
{
try
{
streamResponse?.Close();
} catch { }
try
{
streamRead?.Close();
}
catch { }
try
{
webresponse?.Close();
}
catch { }
}
}
Затем я могу запустить несколько потоков и создать новые GetRequest
объекты, отправитьих, а затем позвоните Cleanup()
.
Достаточно ли этого, чтобы избежать утечек памяти и висячих сокетов?Есть ли лучший способ сделать это?
(извините за качество этого кода, в данный момент он находится на ранней стадии разработки)