La nostra app Web è in esecuzione in .Net Framework 4.0. L'interfaccia utente chiama i metodi del controller tramite chiamate ajax.
Dobbiamo consumare il servizio REST dal nostro fornitore. Sto valutando il modo migliore per chiamare il servizio REST in .Net 4.0. Il servizio REST richiede uno schema di autenticazione di base e può restituire dati sia in XML che in JSON. Non è necessario caricare / scaricare dati enormi e non vedo nulla in futuro. Ho dato un'occhiata ad alcuni progetti di codice open source per il consumo REST e non ho trovato alcun valore in quelli che giustifichino una dipendenza aggiuntiva nel progetto. Iniziato a valutare WebClient
e HttpClient
. Ho scaricato HttpClient per .Net 4.0 da NuGet.
Ho cercato differenze tra WebClient
e HttpClient
e questo sito ha menzionato il fatto che singolo HttpClient può gestire chiamate simultanee e può riutilizzare DNS risolto, configurazione dei cookie e autenticazione. Devo ancora vedere i valori pratici che potremmo ottenere a causa delle differenze.
Ho eseguito un rapido test delle prestazioni per scoprire come si comportano WebClient
(sincronizzazione chiamate), HttpClient
(sincronizzazione e asincronizzazione). e qui ci sono i risultati:
Utilizzo della stessa HttpClient
istanza per tutte le richieste (min - max)
Sincronizzazione WebClient: 8 ms - 167 ms
Sincronizzazione HttpClient: 3 ms - 7228 ms
Asincronizzazione HttpClient: 985 - 10405 ms
Utilizzo di un nuovo HttpClient
per ogni richiesta (min - max)
Sincronizzazione WebClient: 4 ms - 297 ms
Sincronizzazione HttpClient: 3 ms - 7953 ms
Sincronizzazione HttpClient: 1027 - 10834 ms
Codice
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);
}
}
}
Le mie domande
- Le chiamate REST ritornano in 3-4s, il che è accettabile. Le chiamate al servizio REST vengono avviate nei metodi del controller che vengono invocati dalle chiamate Ajax. Per cominciare, le chiamate vengono eseguite in un thread diverso e non bloccano l'interfaccia utente. Quindi, posso semplicemente attenermi alle chiamate di sincronizzazione?
- Il codice sopra è stato eseguito nella mia localbox. Nella configurazione del prod, saranno coinvolti la ricerca DNS e proxy. C'è qualche vantaggio nell'usare
HttpClient
overWebClient
? - La
HttpClient
concorrenza è migliore diWebClient
? Dai risultati del test, vedo che leWebClient
chiamate di sincronizzazione funzionano meglio. - Sarà
HttpClient
una scelta di progettazione migliore se eseguiamo l'aggiornamento a .Net 4.5? Le prestazioni sono il fattore chiave di progettazione.
GetDataFromHttpClientAsync
perché viene eseguito per primo, le altre invocazioni trarranno vantaggio dal potenziale possesso di dati cahed (sia sul computer locale o qualsiasi proxy trasparente tra te e la destinazione) e saranno più veloci. Inoltre, nelle giuste condizionivar response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;
può causare un deadlock a causa dell'esaurimento dei thread del threadpool. Non dovresti mai bloccare un'attività che dipende dal pool di thread nei thread ThreadPool, dovrestiawait
invece restituire il thread al pool.