Ứng dụng web của chúng tôi đang chạy trong .Net Framework 4.0. UI gọi các phương thức điều khiển thông qua các cuộc gọi ajax.
Chúng tôi cần tiêu thụ dịch vụ REST từ nhà cung cấp của chúng tôi. Tôi đang đánh giá cách tốt nhất để gọi dịch vụ REST trong .Net 4.0. Dịch vụ REST yêu cầu Lược đồ xác thực cơ bản và nó có thể trả về dữ liệu trong cả XML và JSON. Không có yêu cầu tải lên / tải xuống dữ liệu lớn và tôi không thấy gì trong tương lai. Tôi đã xem xét một vài dự án mã nguồn mở cho việc tiêu thụ REST và không tìm thấy bất kỳ giá trị nào trong các dự án đó để biện minh cho sự phụ thuộc bổ sung trong dự án. Bắt đầu đánh giá WebClient
và HttpClient
. Tôi đã tải xuống httpClient cho .Net 4.0 từ NuGet.
Tôi đã tìm kiếm sự khác biệt giữa WebClient
và HttpClient
và trang web này đã đề cập rằng một httpClient có thể xử lý các cuộc gọi đồng thời và nó có thể sử dụng lại DNS, cấu hình cookie và xác thực đã giải quyết. Tôi vẫn chưa thấy các giá trị thực tế mà chúng ta có thể đạt được do sự khác biệt.
Tôi đã thực hiện một bài kiểm tra hiệu suất nhanh để tìm cách WebClient
(đồng bộ hóa cuộc gọi), HttpClient
(đồng bộ hóa và không đồng bộ) thực hiện. và đây là kết quả:
Sử dụng cùng một HttpClient
ví dụ cho tất cả các yêu cầu (tối thiểu - tối đa)
Đồng bộ hóa WebClient: 8 ms - 167 ms
Đồng bộ hóa httpClient: 3 ms - 7228 ms Đồng bộ hóa
httpClient: 985 - 10405 ms
Sử dụng một cái mới HttpClient
cho mỗi yêu cầu (tối thiểu - tối đa)
Đồng bộ hóa WebClient: 4 ms - 297 ms
Đồng bộ hóa httpClient: 3 ms - 7953 ms Đồng bộ hóa httpClient
: 1027 - 10834 ms
Mã
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);
}
}
}
Những câu hỏi của tôi
- Các cuộc gọi REST trả về sau 3-4 giây là chấp nhận được. Các cuộc gọi đến dịch vụ REST được bắt đầu trong các phương thức điều khiển được gọi từ các cuộc gọi ajax. Để bắt đầu, các cuộc gọi chạy trong một luồng khác và không chặn UI. Vì vậy, tôi chỉ có thể gắn bó với các cuộc gọi đồng bộ?
- Đoạn mã trên đã được chạy trong localbox của tôi. Trong thiết lập prod, DNS và tra cứu proxy sẽ được tham gia. Có bất kỳ lợi thế của việc sử dụng
HttpClient
hơnWebClient
? - Là
HttpClient
đồng thời tốt hơnWebClient
? Từ kết quả kiểm tra, tôi thấyWebClient
các cuộc gọi đồng bộ thực hiện tốt hơn. - Sẽ
HttpClient
là lựa chọn thiết kế tốt hơn nếu chúng tôi nâng cấp lên .Net 4.5? Hiệu suất là yếu tố thiết kế quan trọng.
GetDataFromHttpClientAsync
vì nó chạy trước, các yêu cầu khác nhận được lợi ích của việc có khả năng có dữ liệu bị chặn (có thể trên máy cục bộ hoặc bất kỳ proxy minh bạch nào giữa bạn và đích) và sẽ nhanh hơn. Ngoài ra, trong các điều kiện phù hợpvar response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;
có thể dẫn đến bế tắc do bạn làm cạn kiệt các luồng xử lý. Bạn không bao giờ nên chặn một hoạt động phụ thuộc vào nhóm luồng trong các luồng ThreadPool,await
thay vào đó , bạn nên trả lại luồng vào lại nhóm.