Notre application Web s'exécute dans .Net Framework 4.0. L'interface utilisateur appelle les méthodes du contrôleur via des appels ajax.
Nous devons consommer le service REST de notre fournisseur. J'évalue la meilleure façon d'appeler le service REST dans .Net 4.0. Le service REST nécessite un schéma d'authentification de base et il peut renvoyer des données à la fois en XML et en JSON. Il n'y a aucune exigence pour le téléchargement / téléchargement de données énormes et je ne vois rien à l'avenir. J'ai examiné quelques projets de code open source pour la consommation REST et je n'ai trouvé aucune valeur dans ceux-ci pour justifier une dépendance supplémentaire dans le projet. Commencé à évaluer WebClient
et HttpClient
. J'ai téléchargé HttpClient pour .Net 4.0 à partir de NuGet.
J'ai cherché des différences entre WebClient
et HttpClient
et ce site a mentionné qu'un seul HttpClient peut gérer les appels simultanés et qu'il peut réutiliser le DNS résolu, la configuration des cookies et l'authentification. Je n'ai pas encore vu les valeurs pratiques que nous pourrions gagner en raison des différences.
J'ai fait un test de performance rapide pour trouver comment WebClient
(appels de synchronisation), HttpClient
(synchronisation et async) fonctionnent. et voici les résultats:
Utilisation de la même HttpClient
instance pour toutes les requêtes (min - max)
Synchronisation WebClient: 8 ms - 167 ms
Synchronisation HttpClient: 3 ms - 7228 ms
Async HttpClient: 985 - 10405 ms
Utiliser un nouveau HttpClient
pour chaque demande (min - max)
Synchronisation WebClient: 4 ms - 297 ms
Synchronisation HttpClient: 3 ms - 7953 ms
Async HttpClient: 1027 - 10834 ms
Code
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);
}
}
}
Mes questions
- Les appels REST reviennent en 3-4 secondes, ce qui est acceptable. Les appels au service REST sont lancés dans des méthodes de contrôleur qui sont appelées à partir d'appels ajax. Pour commencer, les appels s'exécutent dans un thread différent et ne bloquent pas l'interface utilisateur. Alors, puis-je rester avec les appels de synchronisation?
- Le code ci-dessus a été exécuté dans ma boîte locale. Dans la configuration de la prod, la recherche DNS et proxy sera impliquée. Y a-t-il un avantage à utiliser
HttpClient
plusWebClient
? - La
HttpClient
concurrence est-elle meilleure queWebClient
? D'après les résultats du test, je constate que lesWebClient
appels de synchronisation fonctionnent mieux. - Sera
HttpClient
un meilleur choix de conception si nous passons à .Net 4.5? La performance est le facteur clé de conception.
GetDataFromHttpClientAsync
car il s'exécute en premier, les autres invocations bénéficient potentiellement d'avoir des données cahed (que ce soit sur la machine locale ou tout proxy transparent entre vous et la destination) et seront plus rapides. De plus, dans les bonnes conditions,var response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;
un blocage peut se produire car vous épuisez les threads de pool de threads. Vous ne devez jamais bloquer sur une activité qui dépend du pool de threads dans les threads ThreadPool, vous devez le faire à laawait
place pour qu'il renvoie le thread dans le pool.