วิธีรันงานพื้นหลังใน ASP.NET Core
บทนำ
การประมวลผลไฟล์ (เช่น การส่งออกงานนำเสนอเป็น PDF) เป็นงานทั่วไปที่ทำบนเซิร์ฟเวอร์ การทำงานนี้ภายในตัวจัดการคำขอ (ในขณะที่ไคลเอนต์รอ) มีข้อเสียต่อไปนี้:
- UI แย่. หน้าเว็บค้างและผู้ใช้ต้องรอผลลัพธ์ การรีโหลดหน้าเว็บจะยกเลิกงาน.
- การหมดเวลาในการดำเนินการ. เราไม่สามารถรับประกันว่าการประมวลผลจะเสร็จภายในระยะเวลาที่กำหนดได้ ดังนั้นผู้ใช้อาจเห็นข้อความ “operation timeout”.
- อัตราผ่านและความสามารถขยายตัวต่ำ. ASP.NET Core ถูกออกแบบให้ประมวลผลคำขอหลายแบบแบบอะซิงโครนัส งานที่ใช้ CPU มากและใช้เวลานานจะบล็อกเธรดและลดอัตราผ่านของเซิร์ฟเวอร์.
- ความทนต่อข้อผิดพลาดต่ำ. หากมีสิ่งใดเกิดขึ้นระหว่างงานที่ใช้เวลานาน (เช่น ปัญหาการเชื่อมต่อ) การประมวลผลล้มเหลวและต้องเริ่มต้นใหม่ตั้งแต่ต้น.
วิธีที่ดีกว่าbetter approach คือการกำหนดงานให้ทำงานแบบอะซิงโครนัส ประมวลผลในพื้นหลัง และส่งผลลัพธ์กลับเมื่อพร้อม
ในโมเดลนี้ ผู้ใช้สามารถดูสถานะปัจจุบัน (และสามารถออกหรือรีโหลดหน้า) ทรัพยากรของเซิร์ฟเวอร์สามารถขยายได้อย่างมีประสิทธิภาพและปรับแต่งได้ตามต้องการ และสามารถใช้แนวทางการลองใหม่ได้
โซลูชันการประมวลผลพื้นหลังที่ทั่วไปประกอบด้วย:
- API สำหรับกำหนดเวลางาน.
- API สำหรับติดตามสถานะงาน.
- ตัวทำงานพื้นหลังเพื่อประมวลผลงานที่กำหนดเวลา.
- API สำหรับจัดเก็บและดึงผลลัพธ์.
ตัวอย่างงานพื้นหลัง
เพื่อสาธิตวิธีการนี้ ให้พิจารณาแอปตัวอย่าง ASP.NET Core 3.1 แอปนี้มีหน้าที่ผู้ใช้สามารถอัปโหลดงานนำเสนอและคลิก Export to PDF; งานนำเสนอนั้นจะถูกอัปโหลดและแปลงเป็น PDF โดยตัวทำงานพื้นหลัง.
เว็บแอป
เว็บแอปตัวอย่าง (โครงการ BackgroundJobDemo) มี:
- หน้าอัปโหลดไฟล์ (Razor page “Upload”).
- หน้าแสดงความก้าวหน้า (Razor page “Progress” พร้อมฟังก์ชัน JavaScript ไม่กี่ตัวที่ตรวจสอบและแสดงสถานะ).
- ตัวควบคุม (
JobStatusController) ที่ให้สถานะการประมวลผล (api/status/{jobId}). - ตัวควบคุม (
JobResultController) ที่ส่งคืนไฟล์ PDF ที่ส่งออก (api/result/{id}). - ตัวทำงานพื้นหลังที่อิงบริการโฮสติ้งของ ASP.NET Core (ดูคลาส
WorkerService).
หน้า Razor, ตัวควบคุมและตัวทำงานพื้นหลังมอบหมายการทำงานจริงผ่านอินเทอร์เฟซที่กำหนดในโครงการ BackgroundJobDemo.Common การดำเนินการที่เป็นรูปธรรมของการจัดการงานและการประมวลผลจะจัดให้ในโครงการแยก (BackgroundJobDemo.Local, BackgroundJobDemo.Aws ฯลฯ) และสามารถสลับได้ในเมธอด Startup.ConfigureServices.
เพื่อการสาธิต หน้า “Upload” ใช้การผูกโมเดลแบบบัฟเฟอร์ แต่สำหรับการอัปโหลดไฟล์ขนาดใหญ่ การสตรีมแบบไม่บัฟเฟอร์เป็น แนะนำ. สำหรับการใช้งานจริง ควรพิจารณา ด้านความปลอดภัย. หน้า “Progress” โพลล์สถานะงานที่กำหนดเวลาผ่าน JavaScript ทุกสองวินาที (ช่วงเวลานี้สามารถกำหนดค่าได้). การโพลล์เป็นวิธีทั่วไป แต่สำหรับสถานการณ์ที่ซับซ้อนยิ่งขึ้นอาจต้องการการแจ้งเตือนเรียลไทม์ผ่าน WebSockets (การสื่อสารแบบเรียลไทม์อยู่นอกขอบเขตของบทความนี้). SignalR เป็นเครื่องมือที่ง่ายแต่ทรงพลังสำหรับการสื่อสารแบบเรียลไทม์.
การโฮสต์ตัวทำงานพื้นหลังในกระบวนการของเซิร์ฟเวอร์สะดวกสำหรับแอปพลิเคชันง่าย ๆ แต่มี ข้อเสีย. วิธีที่แข็งแรงและขยายได้ดีขึ้นคือการปรับใช้ตัวทำงานในกระบวนการแยก (ดู เช่น แอปพลิเคชันคอนโซล BackgroundJobDemo.Worker).
การนำไปใช้พื้นฐาน
โครงการ BackgroundJobDemo.Local มีการนำไปใช้การจัดการงานอย่างง่ายโดยใช้ฐานข้อมูล SQLite (เส้นทางฐานข้อมูลกำหนดผ่าน LocalConfig.DbFilePath; ดู Startup.ConfigureServices). ไฟล์ที่อัปโหลดและประมวลผลจะถูกจัดเก็บในระบบไฟล์ (เส้นทางโฟลเดอร์จัดเก็บกำหนดผ่าน LocalConfig.FileStorageFolderPath; ดู Startup.ConfigureServices). เพื่อการทนต่อข้อผิดพลาดและประสิทธิภาพที่ดียิ่งขึ้นในแอปพลิเคชันจริง ควรดำเนินการกำหนดเวลางานผ่านคิวข้อความ (เช่น RabbitMQ, AWS SQS, Azure Storage Queue).
การนำไปใช้แบบกระจายบน Amazon Web Services
โครงการ BackgroundJobDemo.Aws ทำการประมวลผลงานบน Amazon Web Services และแสดงสถาปัตยกรรมแบบกระจายที่สามารถขยายแนวนอนได้ มีส่วนประกอบดังต่อไปนี้:
- เว็บแอป — ทำงานโต้ตอบกับผู้ใช้และกำหนดเวลางานส่งออก PPTX ไปเป็น PDF เป็นต้น.
- ตัวทำงาน — ประมวลผลการส่งออก (ในกระบวนการ, นอกกระบวนการ, หรือ AWS Lambda).
- คิวข้อความ — จัดเก็บงานที่ต้องประมวลผล (Amazon SQS).
- ที่เก็บไฟล์ — จัดเก็บไฟล์ที่อัปโหลดและประมวลผล (Amazon S3).
- ที่เก็บคีย์–ค่า — ติดตามสถานะการประมวลผลงาน (Amazon DynamoDB).
สถาปัตยกรรมกระจายแบบทั่วไปอาศัย คิวข้อความ: เว็บแอปวางงานพื้นหลังลงในคิว; ตัวทำงานพื้นหลังดึงงานจากคิวและทำงานที่ต้องการ. สิ่งนี้ทำให้ส่วนประกอบแยกออกจากกันและทำให้การประมวลผลเป็นแบบอะซิงโครนัสและเชื่อถือได้. คิวรับประกันการส่งมอบและใช้ visibility timeout: เมื่อหนึ่งตัวทำงานรับข้อความ ข้อความนั้นจะซ่อนจากตัวทำงานอื่น; ตัวทำงานที่กำลังประมวลผลเท่านั้นที่ลบมันเมื่อเสร็จ. หากการประมวลผลไม่เสร็จภายใน visibility timeout (เช่น เนื่องจากความล้มเหลวหรือปัญหาเครือข่าย) ข้อความที่ไม่ได้ประมวลผลจะปรากฏอีกครั้ง.
การนำไปใช้ของเราจะใช้ Amazon Simple Queue Service (SQS) ซึ่งเป็นคิวข้อความที่จัดการแบบครบวงจรสำหรับไมโครเซอร์วิส ระบบกระจาย และแอปพลิเคชันแบบไม่มีเซิร์ฟเวอร์.
คิวข้อความออกแบบมาสำหรับข้อความขนาดเบา (เช่น ขนาดสูงสุดของข้อความ SQS คือ 256 KB) ดังนั้นข้อความควรมีเพียงรายละเอียดงานเท่านั้น ข้อมูลขนาดใหญ่ (เช่นไฟล์ที่ต้องประมวลผล) ควรจัดเก็บแยกต่างหากและอ้างอิงจากข้อความ. Amazon S3 ถูกใช้เพื่อจัดเก็บไฟล์ที่อัปโหลดและประมวลผล.
ที่เก็บคีย์–ค่าจำเป็นสำหรับการเก็บและดึงผลลัพธ์ของงานโดย ID ตัวอย่างนี้ใช้ Amazon DynamoDB, ซึ่งเป็นบริการฐานข้อมูล NoSQL ที่เร็วและยืดหยุ่น.
เพื่อเรียกใช้แอปสาธิตกับ Amazon Web Services:
- ในภูมิภาค AWS เดียวกัน ให้สร้างและกำหนดค่า:
- คิว SQS,
- bucket S3,
- ตาราง DynamoDB.
- เชื่อมต่อเว็บแอปกับบริการเหล่านี้โดยเรียก AddAws ใน
Startup.ConfigureServicesโดยระบุ URL ของคิว SQS, ชื่อ bucket S3, ชื่อ ตาราง DynamoDB, และภูมิภาค AWS.