Tôi đang di chuyển hàng triệu người dùng từ AD tại chỗ sang Azure AD B2C bằng MS Graph API để tạo người dùng trong B2C. Tôi đã viết một ứng dụng bảng điều khiển .Net Core 3.1 để thực hiện việc di chuyển này. Để tăng tốc mọi thứ cùng với việc tôi thực hiện các cuộc gọi đồng thời tới API đồ thị. Điều này đang làm việc tuyệt vời - loại.
Trong quá trình phát triển, tôi đã trải nghiệm hiệu năng chấp nhận được khi chạy từ Visual Studio 2019, nhưng để thử nghiệm tôi đang chạy từ dòng lệnh trong Powershell 7. Từ Powershell, hiệu suất của các cuộc gọi đồng thời đến HttpClient là rất tệ. Dường như có giới hạn về số lượng cuộc gọi đồng thời mà HTTPClient cho phép khi chạy từ Powershell, vì vậy các cuộc gọi trong các đợt đồng thời lớn hơn 40 đến 50 yêu cầu bắt đầu xếp chồng lên nhau. Nó dường như đang chạy 40 đến 50 yêu cầu đồng thời trong khi chặn phần còn lại.
Tôi không tìm kiếm sự trợ giúp với lập trình async. Tôi đang tìm cách khắc phục sự khác biệt giữa hành vi thời gian chạy của Visual Studio và hành vi thời gian chạy dòng lệnh của Powershell. Chạy trong chế độ phát hành từ nút mũi tên màu xanh lá cây của Visual Studio hoạt động như mong đợi. Chạy từ dòng lệnh không.
Tôi điền vào một danh sách nhiệm vụ với các cuộc gọi không đồng bộ và sau đó chờ đợi Nhiệm vụ.When ALL (nhiệm vụ). Mỗi cuộc gọi mất từ 300 đến 400 mili giây. Khi chạy từ Visual Studio, nó hoạt động như mong đợi. Tôi thực hiện đồng thời 1000 đợt gọi và mỗi cuộc gọi hoàn thành trong thời gian dự kiến. Toàn bộ khối tác vụ chỉ mất vài mili giây so với cuộc gọi cá nhân dài nhất.
Hành vi thay đổi khi tôi chạy cùng một bản dựng từ dòng lệnh Powershell. 40 đến 50 cuộc gọi đầu tiên mất 300 đến 400 mili giây dự kiến nhưng sau đó thời gian gọi riêng lẻ tăng lên 20 giây mỗi cuộc gọi. Tôi nghĩ rằng các cuộc gọi được tuần tự hóa, vì vậy chỉ có 40 đến 50 được thực hiện tại một thời điểm trong khi những người khác chờ đợi.
Sau nhiều giờ dùng thử và lỗi, tôi đã có thể thu hẹp nó xuống thành HTTPClient. Để tách biệt vấn đề, tôi đã mô phỏng các cuộc gọi đến HttpClient.SendAsync bằng một phương thức thực hiện Task.Delay (300) và trả về kết quả giả. Trong trường hợp này, chạy từ bàn điều khiển hoạt động giống hệt như chạy từ Visual Studio.
Tôi đang sử dụng IHttpClientFactory và thậm chí tôi đã thử điều chỉnh giới hạn kết nối trên ServicePointManager.
Đây là mã đăng ký của tôi.
public static IServiceCollection RegisterHttpClient(this IServiceCollection services, int batchSize)
{
ServicePointManager.DefaultConnectionLimit = batchSize;
ServicePointManager.MaxServicePoints = batchSize;
ServicePointManager.SetTcpKeepAlive(true, 1000, 5000);
services.AddHttpClient(MSGraphRequestManager.HttpClientName, c =>
{
c.Timeout = TimeSpan.FromSeconds(360);
c.DefaultRequestHeaders.Add("User-Agent", "xxxxxxxxxxxx");
})
.ConfigurePrimaryHttpMessageHandler(() => new DefaultHttpClientHandler(batchSize));
return services;
}
Đây là DefaultHttpClientHandler.
internal class DefaultHttpClientHandler : HttpClientHandler
{
public DefaultHttpClientHandler(int maxConnections)
{
this.MaxConnectionsPerServer = maxConnections;
this.UseProxy = false;
this.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
}
}
Đây là mã thiết lập các nhiệm vụ.
var timer = Stopwatch.StartNew();
var tasks = new Task<(UpsertUserResult, TimeSpan)>[users.Length];
for (var i = 0; i < users.Length; ++i)
{
tasks[i] = this.CreateUserAsync(users[i]);
}
var results = await Task.WhenAll(tasks);
timer.Stop();
Đây là cách tôi chế nhạo HTTPClient.
var httpClient = this.httpClientFactory.CreateClient(HttpClientName);
#if use_http
using var response = await httpClient.SendAsync(request);
#else
await Task.Delay(300);
var graphUser = new User { Id = "mockid" };
using var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonConvert.SerializeObject(graphUser)) };
#endif
var responseContent = await response.Content.ReadAsStringAsync();
Dưới đây là số liệu cho 10k người dùng B2C được tạo thông qua GraphAPI bằng 500 yêu cầu đồng thời. 500 yêu cầu đầu tiên dài hơn bình thường vì các kết nối TCP đang được tạo.
Đây là một liên kết đến bảng điều khiển chạy số liệu .
Đây là một liên kết đến các số liệu chạy Visual Studio .
Thời gian chặn trong số liệu chạy VS khác với những gì tôi đã nói trong bài đăng này vì tôi đã chuyển tất cả quyền truy cập tệp đồng bộ vào cuối quy trình trong nỗ lực cô lập mã có vấn đề nhất có thể cho quá trình chạy thử.
Dự án được biên dịch bằng .Net Core 3.1. Tôi đang sử dụng Visual Studio 2019 16.4.5.