Nosso aplicativo da web está sendo executado no .Net Framework 4.0. A interface do usuário chama métodos de controlador por meio de chamadas ajax.
Precisamos consumir o serviço REST do nosso fornecedor. Estou avaliando a melhor maneira de chamar o serviço REST no .Net 4.0. O serviço REST requer Esquema de autenticação básica e pode retornar dados em XML e JSON. Não há necessidade de fazer upload / download de grandes dados e não vejo nada no futuro. Analisei alguns projetos de código-fonte aberto para consumo REST e não encontrei nenhum valor para justificar dependência adicional no projeto. Começou a avaliar WebClient
e HttpClient
. Eu baixei o HttpClient for .Net 4.0 do NuGet.
Eu procurei por diferenças entre WebClient
e HttpClient
e neste site mencionado esse único HttpClient pode manipular chamadas simultâneas e pode reutilizar resolvido DNS, configuração de cookies e autenticação. Ainda estou para ver valores práticos que podemos obter devido às diferenças.
Fiz um teste rápido de desempenho para descobrir como WebClient
(chamadas de sincronização), HttpClient
(sincronização e assíncrona) se comportam. e aqui estão os resultados:
Usando a mesma HttpClient
instância para todas as solicitações (mín. - máx.)
Sincronização do WebClient: 8 ms - 167 ms
Sincronização do HttpClient: 3 ms - 7228 ms
Assinatura do HttpClient: 985 - 10405 ms
Usando um novo HttpClient
para cada solicitação (min - max)
Sincronização de cliente da Web: 4 ms - 297 ms
Sincronização de HttpClient: 3 ms - 7953 ms
Assinatura de HttpClient: 1027 - 10834 ms
Código
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);
}
}
}
Minhas perguntas
- As chamadas REST retornam em 3-4s, o que é aceitável. As chamadas para o serviço REST são iniciadas nos métodos do controlador que são chamados a partir de chamadas ajax. Para começar, as chamadas são executadas em um thread diferente e não bloqueiam a interface do usuário. Então, posso ficar com as chamadas de sincronização?
- O código acima foi executado no meu localbox. Na configuração do produto, a pesquisa de DNS e proxy estará envolvida. Existe alguma vantagem em usar
HttpClient
maisWebClient
? - A
HttpClient
concorrência é melhor queWebClient
? A partir dos resultados do teste, vejo que asWebClient
chamadas de sincronização têm melhor desempenho. - Será
HttpClient
uma escolha de design melhor se atualizarmos para o .Net 4.5? O desempenho é o principal fator de design.
GetDataFromHttpClientAsync
porque é executado primeiro, as outras invocações se beneficiam de ter dados coletados em potencial (seja na máquina local ou em qualquer proxy transparente entre você e o destino) e serão mais rápidos. Além disso, nas condições corretas,var response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;
pode resultar em um impasse devido ao esgotamento dos encadeamentos do conjunto de encadeamentos. Você nunca deve bloquear uma atividade que dependa do conjunto de encadeamentos nos encadeamentos ThreadPool; emawait
vez disso, retorne o encadeamento novamente para o conjunto.