ฉันกำลังโยกย้ายผู้ใช้นับล้านจากโฆษณาในสถานที่ไปยัง Azure AD B2C โดยใช้ MS Graph API เพื่อสร้างผู้ใช้ใน B2C ฉันได้เขียนแอปพลิเคชันคอนโซล. Net Core 3.1 เพื่อทำการย้ายข้อมูลนี้ เพื่อเพิ่มความเร็วของสิ่งต่าง ๆ ในขณะที่ฉันกำลังเรียกใช้กราฟ API พร้อมกัน มันใช้งานได้ดีมาก
ในระหว่างการพัฒนาฉันพบว่าประสิทธิภาพที่ยอมรับได้ในขณะที่เรียกใช้จาก Visual Studio 2019 แต่สำหรับการทดสอบฉันกำลังเรียกใช้จากบรรทัดคำสั่งใน Powershell 7 จาก Powershell ประสิทธิภาพของการโทรพร้อมกันไปยัง HttpClient นั้นแย่มาก ปรากฏว่ามีการ จำกัด จำนวนการโทรพร้อมกันที่ HttpClient อนุญาตเมื่อเรียกใช้จาก Powershell ดังนั้นการโทรแบบ Batches พร้อมกันที่มีมากกว่า 40 ถึง 50 คำขอจะเริ่มซ้อนกัน ดูเหมือนว่าจะเรียกใช้คำขอที่เกิดขึ้นพร้อมกัน 40 ถึง 50 รายการในขณะที่ปิดกั้นส่วนที่เหลือ
ฉันไม่ต้องการความช่วยเหลือเกี่ยวกับการเขียนโปรแกรม async ฉันกำลังมองหาวิธีที่จะแก้ปัญหาในการถ่ายความแตกต่างระหว่างพฤติกรรมการใช้งาน Visual Studio และการทำงานแบบบรรทัดคำสั่ง Powershell การทำงานในโหมดนำออกใช้จากปุ่มลูกศรสีเขียวของ Visual Studio จะทำงานตามที่คาดไว้ เรียกใช้จากบรรทัดคำสั่งไม่ได้
ฉันเติมรายการงานด้วยการโทรแบบ async จากนั้นรอ Task.WhenAll (งาน) การโทรแต่ละครั้งใช้เวลาระหว่าง 300 และ 400 มิลลิวินาที เมื่อเรียกใช้จาก Visual Studio จะทำงานตามที่คาดไว้ ฉันทำการโทรพร้อมกัน 1,000 การโทรและแต่ละการโทรจะเสร็จสิ้นภายในเวลาที่กำหนด บล็อกงานทั้งหมดใช้เวลาเพียงไม่กี่มิลลิวินาทีนานกว่าการโทรแต่ละครั้งที่ยาวที่สุด
พฤติกรรมเปลี่ยนไปเมื่อฉันเรียกใช้บิลด์เดียวกันจากบรรทัดคำสั่ง Powershell การโทร 40 ถึง 50 ครั้งแรกใช้เวลา 300 ถึง 400 มิลลิวินาทีที่คาดว่าจะได้ แต่การโทรแต่ละครั้งจะเติบโตถึง 20 วินาทีต่อการโทรแต่ละครั้ง ฉันคิดว่าการโทรเป็นแบบอนุกรมดังนั้นจะมีการดำเนินการครั้งละ 40 ถึง 50 ครั้งในขณะที่สายอื่นจะรอ
หลังจากชั่วโมงแห่งการลองผิดลองถูกฉันสามารถ จำกัด ให้แคบลงถึง HttpClient เพื่อแยกปัญหาฉันล้อเลียนการโทรไปที่ HttpClient.SendAsync ด้วยวิธีการที่ใช้ Task.Delay (300) และส่งกลับผลลัพธ์การเยาะเย้ย ในกรณีนี้การเรียกใช้จากคอนโซลจะทำงานเหมือนกันกับการทำงานจาก Visual Studio
ฉันใช้ IHttpClientFactory และฉันได้ลองปรับเปลี่ยนขีด จำกัด การเชื่อมต่อบน ServicePointManager แล้ว
นี่คือรหัสลงทะเบียนของฉัน
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;
}
นี่คือ 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;
}
}
นี่คือรหัสที่ตั้งค่างาน
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();
นี่คือวิธีที่ฉันล้อเลียน 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();
นี่คือตัวชี้วัดสำหรับผู้ใช้ B2C 10 พันคนที่สร้างผ่าน GraphAPI โดยใช้คำขอ 500 คำขอพร้อมกัน คำขอ 500 ครั้งแรกยาวกว่าปกติเนื่องจากกำลังสร้างการเชื่อมต่อ TCP
นี่คือการเชื่อมโยงไปยังตัวชี้วัดคอนโซลทำงาน
นี่คือการเชื่อมโยงไปยังภาพและตัวชี้วัดการทำงานสตูดิโอ
เวลาบล็อกในตัวชี้วัดการทำงาน VS แตกต่างจากที่ฉันพูดในโพสต์นี้เนื่องจากฉันย้ายการเข้าถึงไฟล์แบบซิงโครนัสทั้งหมดไปยังจุดสิ้นสุดของกระบวนการเพื่อพยายามแยกรหัสที่มีปัญหาออกให้มากที่สุดสำหรับการทดสอบการทำงาน
โครงการถูกคอมไพล์โดยใช้. Net Core 3.1 ฉันใช้ Visual Studio 2019 16.4.5