Наше веб-приложение работает в .Net Framework 4.0. Пользовательский интерфейс вызывает методы контроллера через вызовы ajax.
Нам нужно воспользоваться услугой REST от нашего поставщика. Я оцениваю лучший способ вызвать службу REST в .Net 4.0. Службе REST требуется базовая схема аутентификации, и она может возвращать данные как в формате XML, так и в формате JSON. Нет необходимости загружать / скачивать огромные данные, и я ничего не вижу в будущем. Я взглянул на несколько проектов с открытым исходным кодом для потребления REST и не нашел в них никакой ценности, чтобы оправдать дополнительную зависимость в проекте. Начал оценивать WebClient
и HttpClient
. Я скачал HttpClient для .Net 4.0 с NuGet.
Я искал различия между WebClient
и, HttpClient
и на этом сайте упоминалось, что один HttpClient может обрабатывать одновременные вызовы и может повторно использовать разрешенный DNS, конфигурацию cookie и аутентификацию. Мне еще предстоит увидеть практические ценности, которые мы можем получить из-за различий.
Я сделал быстрый тест производительности, чтобы найти, как WebClient
(синхронизировать вызовы), HttpClient
(синхронизировать и асинхронно) выполняют. и вот результаты:
Использование одного и того же HttpClient
экземпляра для всех запросов (мин. - макс.)
Синхронизация WebClient: 8 мс - 167 мс
Синхронизация HttpClient: 3 мс - 7228 мс
Асинхронность HttpClient: 985 - 10405 мс
Использование нового HttpClient
для каждого запроса (мин. - макс.)
Синхронизация WebClient: 4 мс - 297 мс
Синхронизация HttpClient: 3 мс - 7953 мс
Асинхронность HttpClient: 1027 - 10834 мс
Код
public class AHNData
{
public int i;
public string str;
}
public class Program
{
public static HttpClient httpClient = new HttpClient();
private static readonly string _url = "http://localhost:9000/api/values/";
public static void Main(string[] args)
{
#region "Trace"
Trace.Listeners.Clear();
TextWriterTraceListener twtl = new TextWriterTraceListener(
"C:\\Temp\\REST_Test.txt");
twtl.Name = "TextLogger";
twtl.TraceOutputOptions = TraceOptions.ThreadId | TraceOptions.DateTime;
ConsoleTraceListener ctl = new ConsoleTraceListener(false);
ctl.TraceOutputOptions = TraceOptions.DateTime;
Trace.Listeners.Add(twtl);
Trace.Listeners.Add(ctl);
Trace.AutoFlush = true;
#endregion
int batchSize = 1000;
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = batchSize;
ServicePointManager.DefaultConnectionLimit = 1000000;
Parallel.For(0, batchSize, parallelOptions,
j =>
{
Stopwatch sw1 = Stopwatch.StartNew();
GetDataFromHttpClientAsync<List<AHNData>>(sw1);
});
Parallel.For(0, batchSize, parallelOptions,
j =>
{
Stopwatch sw1 = Stopwatch.StartNew();
GetDataFromHttpClientSync<List<AHNData>>(sw1);
});
Parallel.For(0, batchSize, parallelOptions,
j =>
{
using (WebClient client = new WebClient())
{
Stopwatch sw = Stopwatch.StartNew();
byte[] arr = client.DownloadData(_url);
sw.Stop();
Trace.WriteLine("WebClient Sync " + sw.ElapsedMilliseconds);
}
});
Console.Read();
}
public static T GetDataFromWebClient<T>()
{
using (var webClient = new WebClient())
{
webClient.BaseAddress = _url;
return JsonConvert.DeserializeObject<T>(
webClient.DownloadString(_url));
}
}
public static void GetDataFromHttpClientSync<T>(Stopwatch sw)
{
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync(_url).Result;
var obj = JsonConvert.DeserializeObject<T>(
response.Content.ReadAsStringAsync().Result);
sw.Stop();
Trace.WriteLine("HttpClient Sync " + sw.ElapsedMilliseconds);
}
public static void GetDataFromHttpClientAsync<T>(Stopwatch sw)
{
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync(_url).ContinueWith(
(a) => {
JsonConvert.DeserializeObject<T>(
a.Result.Content.ReadAsStringAsync().Result);
sw.Stop();
Trace.WriteLine("HttpClient Async " + sw.ElapsedMilliseconds);
}, TaskContinuationOptions.None);
}
}
}
Мои вопросы
- REST-вызовы возвращаются через 3-4 секунды, что приемлемо. Вызовы службы REST инициируются в методах контроллера, которые вызываются из вызовов ajax. Начнем с того, что вызовы выполняются в другом потоке и не блокируют пользовательский интерфейс. Итак, я могу просто придерживаться синхронизации вызовов?
- Приведенный выше код был запущен в моем localbox. В настройке prod будут задействованы DNS и прокси. Есть ли преимущество использования
HttpClient
болееWebClient
? - Является ли
HttpClient
параллелизм лучшеWebClient
? Из результатов теста я вижу, чтоWebClient
синхронизация вызовов работает лучше. - Будет
HttpClient
ли лучший выбор дизайна, если мы перейдем на .Net 4.5? Производительность является ключевым фактором дизайна.
GetDataFromHttpClientAsync
потому что он выполняется первым, другие вызовы получают выгоду от потенциальной возможности кэширования данных (будь то на локальном компьютере или на любом прозрачном прокси между вами и пунктом назначения) и будут быстрее. Кроме того, при правильных условияхvar response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;
может привести к тупиковой ситуации из-за истощения потоков резьбы пула. Никогда не следует блокировать действие, которое зависит от пула потоков в потоках ThreadPool,await
вместо этого следует возвращать поток обратно в пул.