ฉันสามารถใช้เธรดเพื่อทำงานระยะยาวบน IIS ได้หรือไม่


85

ในแอปพลิเคชัน ASP.Net ผู้ใช้คลิกปุ่มบนเว็บเพจจากนั้นจะสร้างอินสแตนซ์อ็อบเจ็กต์บนเซิร์ฟเวอร์ผ่านตัวจัดการเหตุการณ์และเรียกใช้เมธอดบนอ็อบเจ็กต์ วิธีนี้ไปยังระบบภายนอกเพื่อทำสิ่งต่างๆและอาจใช้เวลาสักครู่ ดังนั้นสิ่งที่ฉันต้องการจะทำคือเรียกใช้การเรียกเมธอดนั้นในเธรดอื่นเพื่อให้ฉันสามารถคืนการควบคุมให้กับผู้ใช้ด้วยข้อความ "ส่งคำขอของคุณแล้ว" ฉันมีความสุขพอสมควรที่จะทำสิ่งนี้เป็นไฟและลืมแม้ว่ามันจะดีกว่านี้หากผู้ใช้สามารถสำรวจวัตถุเพื่อดูสถานะได้

สิ่งที่ฉันไม่รู้คือถ้า IIS อนุญาตให้เธรดของฉันทำงานต่อไปแม้ว่าเซสชันผู้ใช้จะหมดอายุ ลองนึกภาพผู้ใช้เริ่มต้นเหตุการณ์และเราสร้างอินสแตนซ์อ็อบเจ็กต์บนเซิร์ฟเวอร์และเริ่มเมธอดในเธรดใหม่ ผู้ใช้พอใจกับข้อความ "ส่งคำขอของคุณแล้ว" และปิดเบราว์เซอร์ของตน ในที่สุดเซสชันผู้ใช้นี้จะหมดเวลาใน IIS แต่เธรดอาจยังคงทำงานอยู่และทำงานได้ IIS จะอนุญาตให้เธรดทำงานต่อไปหรือจะฆ่ามันและกำจัดอ็อบเจ็กต์เมื่อเซสชันผู้ใช้หมดอายุ?

แก้ไข: จากคำตอบและความคิดเห็นฉันเข้าใจว่าวิธีที่ดีที่สุดในการดำเนินการนี้คือการย้ายการประมวลผลที่ใช้เวลานานออกนอก IIS นอกเหนือจากสิ่งอื่น ๆ แล้วสิ่งนี้เกี่ยวข้องกับปัญหาการรีไซเคิลโดเมนแอป ในทางปฏิบัติฉันต้องเอาเวอร์ชัน 1 ออกจากพื้นในเวลาที่ จำกัด และต้องทำงานในเฟรมเวิร์กที่มีอยู่ดังนั้นฉันต้องการหลีกเลี่ยงเลเยอร์บริการดังนั้นความปรารถนาที่จะปิดเธรดภายใน IIS ในทางปฏิบัติ "วิ่งยาว" ที่นี่จะใช้เวลาเพียงไม่กี่นาทีและการทำงานพร้อมกันบนเว็บไซต์จะต่ำดังนั้นจึงไม่เป็นไร แต่รุ่นถัดไปจะต้องแยกเป็นชั้นบริการแยกต่างหาก


1
คุณมีรายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่ต้องดำเนินการและสภาพแวดล้อมการโฮสต์ของคุณหรือไม่? ตัวอย่างเช่นคุณสามารถติดตั้งบริการได้หรือไม่?
senfo

คุณสามารถใช้วิธีการเขียนโปรแกรมAsync และ Await ได้ตลอดเวลา (Visual Studio 2012+) สะอาดกว่าการจัดการเธรดด้วยตัวเองมาก
SausageFingers

คำตอบ:


65

คุณสามารถทำสิ่งที่ต้องการให้สำเร็จได้ แต่โดยทั่วไปแล้วมันเป็นความคิดที่ไม่ดี บล็อก ASP.NET และเอ็นจิ้น CMS หลายตัวใช้แนวทางนี้เนื่องจากต้องการติดตั้งบนระบบโฮสติ้งที่ใช้ร่วมกันและไม่ต้องพึ่งพาบริการ windows ที่ต้องติดตั้ง โดยทั่วไปแล้วพวกเขาจะเริ่มต้นเธรดที่รันอยู่นานใน Global.asax เมื่อแอปเริ่มทำงานและให้กระบวนการเธรดนั้นจัดคิวงาน

นอกเหนือจากการลดทรัพยากรที่มีให้กับ IIS / ASP.NET เพื่อประมวลผลคำขอแล้วคุณยังมีปัญหาเกี่ยวกับเธรดที่ถูกฆ่าเมื่อ AppDomain ถูกรีไซเคิลจากนั้นคุณต้องจัดการกับการคงอยู่ของงานในขณะที่อยู่ในระหว่างการบินเนื่องจาก รวมทั้งเริ่มการทำงานสำรองเมื่อ AppDomain กลับมา

โปรดทราบว่าในหลาย ๆ กรณี AppDomain จะถูกรีไซเคิลโดยอัตโนมัติตามช่วงเวลาเริ่มต้นเช่นเดียวกับหากคุณอัปเดต web.config เป็นต้น

หากคุณสามารถจัดการกับการคงอยู่และลักษณะการทำธุรกรรมของเธรดของคุณที่ถูกฆ่าได้ตลอดเวลาคุณสามารถหลีกเลี่ยงการรีไซเคิล AppDomain ได้โดยมีกระบวนการภายนอกบางอย่างที่ร้องขอบนไซต์ของคุณในบางช่วงเวลา - เพื่อที่ว่าหากไซต์นั้นถูกรีไซเคิลให้คุณ รับประกันว่าจะเริ่มสำรองข้อมูลอีกครั้งโดยอัตโนมัติภายใน X นาที

อีกครั้งนี่เป็นความคิดที่ไม่ดี

แก้ไข: นี่คือตัวอย่างบางส่วนของเทคนิคนี้ที่ใช้งานได้จริง:

Community Server: การใช้ Windows Services เทียบกับ Background Thread เพื่อเรียกใช้โค้ดตามช่วงเวลาที่กำหนดไว้ การสร้าง Background Thread เมื่อเว็บไซต์เริ่มต้นครั้งแรก

แก้ไข (จากอนาคตไกลไกล) - วันนี้ผมจะใช้Hangfire


1
ขอบคุณนี่เป็นข้อมูลเชิงลึก การรีไซเคิลเป็นตัวฆ่าที่ชัดเจนและฉันได้รับจากสิ่งที่คุณพูดว่าฉันสามารถทำสิ่งนี้ได้เป็นเรื่องยากและรวดเร็ว แต่ก็เป็นอันตราย
Frans

ใช่ตรวจสอบสิ่งนี้: professionalaspnet.com/archive/2007/09/02/… และนี่คือหัวข้อเกี่ยวกับวิธีที่เซิร์ฟเวอร์ COmmunity จัดการสิ่งเดียวกัน: dev.communityserver.com/forums/t/459942.aspx
Bryan Batchelder

ในกรณีของฉันฉันสามารถบอก IIS ได้ว่าห้ามรีไซเคิลและห้ามใช้งานไซต์เด็ดขาด สิ่งนี้ทำให้ฉันสามารถทำงานภายใต้สมมติฐานนี้ได้และไซต์จะถูกรีไซเคิลในขณะที่ใช้งานและเมื่อใดก็ตามที่มีการบำรุงรักษาตามแผน เนื่องจากฉันเพิ่งทำไซต์ประเภทผู้ดูแลระบบสิ่งนี้จึงเหมาะสำหรับฉัน
Brett

1
แปลกที่มีการโหวตเพิ่มขึ้นมากมาย สิ่งนี้เป็นไปได้เป็นหนึ่งในสิ่งที่ยอดเยี่ยมเกี่ยวกับ ASP.NET: คำขอทั้งหมดจะได้รับใน AppDomain เดียวกันพร้อมกับเธรดพื้นหลังที่คุณสามารถสร้างได้ ไม่มีเหตุผลใดที่ระบุว่าทำไมนี่จึงเป็น "ความคิดที่ไม่ดี" ที่ทำให้ฉันเชื่อได้ AppDomains สามารถลงได้ใช่ - แต่แทบจะไม่ซ้ำกับสภาพแวดล้อม ASP.NET คุณต้องเล่นได้ดีกับสภาพแวดล้อมโฮสติ้งของคุณ (google for HostingEnvironment.RegisterObject)
ยอห์น

3
.NET 4.5.2 ได้เพิ่มคุณสมบัติใหม่QueueBackgroundWorkItemเพื่อลงทะเบียนงานเบื้องหลังได้อย่างง่ายดายคุณอาจต้องการอัปเดตคำตอบของคุณอีกครั้ง
Scott Chamberlain

40

ฉันไม่เห็นด้วยกับคำตอบที่ยอมรับ

การใช้เธรดพื้นหลัง (หรืองานที่เริ่มต้นด้วยTask.Factory.StartNew) ก็ใช้ได้ใน ASP.NET เช่นเดียวกับสภาพแวดล้อมการโฮสต์คุณอาจต้องการทำความเข้าใจและร่วมมือกับสิ่งอำนวยความสะดวกที่ควบคุมการปิดระบบ

ใน ASP.NET คุณสามารถลงทะเบียนงานที่ต้องหยุดอย่างสง่างามเมื่อปิดเครื่องโดยใช้HostingEnvironment.RegisterObjectวิธีนี้ ดูบทความนี้และความคิดเห็นสำหรับการอภิปราย

(ดังที่เจอราร์ดชี้ให้เห็นในความคิดเห็นของเขาตอนนี้ยังมีการHostingEnvironment.QueueBackgroundWorkItemเรียกร้องRegisterObjectให้ลงทะเบียนตัวกำหนดตารางเวลาเพื่อให้รายการพื้นหลังทำงานโดยรวมแล้ววิธีการใหม่นั้นดีกว่าเนื่องจากเป็นงานตาม)

สำหรับธีมทั่วไปที่คุณมักได้ยินว่าเป็นความคิดที่ไม่ดีให้พิจารณาทางเลือกในการปรับใช้บริการ windows (หรือแอปพลิเคชันกระบวนการพิเศษอื่น ๆ ):

  • ไม่มีการปรับใช้ที่ไม่สำคัญอีกต่อไปด้วยการปรับใช้เว็บ
  • ไม่สามารถปรับใช้บนเว็บไซต์ Azure ได้อย่างหมดจด
  • ขึ้นอยู่กับลักษณะของงานเบื้องหลังกระบวนการต่างๆจะต้องสื่อสารกัน นั่นหมายความว่า IPC หรือบริการบางรูปแบบจะต้องเข้าถึงฐานข้อมูลทั่วไป

โปรดทราบว่าสถานการณ์ขั้นสูงบางสถานการณ์อาจจำเป็นต้องให้เธรดพื้นหลังทำงานในพื้นที่แอดเดรสเดียวกันกับคำร้องขอ ฉันเห็นความจริงที่ว่า ASP.NET สามารถทำสิ่งนี้ได้เป็นข้อได้เปรียบที่ยอดเยี่ยมที่เป็นไปได้ผ่าน. NET


นี่มันเยี่ยมมาก ฉันมีงานที่ต้องทำประมาณ 30 วินาทีให้เสร็จ สมบูรณ์แบบหากคุณเพียงแค่ต้องการหน่วงเวลาแอปพูลให้นานพอที่จะทำงานให้เสร็จ
Scuba Steve

1
จาก. Net Framework 4.5.2 คุณยังสามารถใช้ HostingEnvironment.QueueBackgroundWorkItem (Action <CancellationToken>)
Gerard Carbó

จากการสังเกตของฉันดูเหมือนว่า IIS จะปิด ASP.NET Web API ในที่สุดฉันกำลังใช้งานอยู่และไม่ได้เริ่มการสำรองข้อมูลจนกว่าจะมีคำขอใหม่เข้ามาดังนั้นในเวลาที่กำหนดเมื่อเธรดของฉันควรจะเริ่มต้นแอปพลิเคชันอาจ ไม่ได้ทำงานด้วยซ้ำ ฉันผิดเกี่ยวกับเรื่องนี้หรือไม่?
David Sopko

7

คุณไม่ต้องการใช้เธรดจากพูลเธรด IIS สำหรับงานนี้เนื่องจากจะทำให้เธรดนั้นไม่สามารถประมวลผลคำขอในอนาคตได้ คุณสามารถดูAsynchronous Pages ใน ASP.NET 2.0ได้ แต่นั่นก็ไม่ใช่คำตอบที่ถูกต้องเช่นกัน แต่สิ่งที่มันเสียงเหมือนคุณจะได้รับประโยชน์จากการมองเข้าไปในการจัดคิวข้อความไมโครซอฟท์ โดยพื้นฐานแล้วคุณจะต้องเพิ่มรายละเอียดงานลงในคิวและกระบวนการพื้นหลังอื่น (อาจเป็นบริการ Windows) จะรับผิดชอบในการดำเนินงานนั้น แต่สิ่งที่สำคัญที่สุดคือกระบวนการเบื้องหลังนั้นแยกออกจาก IIS อย่างสมบูรณ์


อ่า MSMQ - หนึ่งในเทคโนโลยีที่ฉันชอบมานาน ขอบคุณสำหรับข้อมูลเชิงลึกและลิงก์
Frans

6

ฉันขอแนะนำให้ใช้HangFireสำหรับข้อกำหนดดังกล่าว มันเป็นไฟที่ดีและเครื่องยนต์ที่ลืมทำงานในพื้นหลังรองรับสถาปัตยกรรมที่แตกต่างกันเชื่อถือได้เพราะได้รับการสนับสนุนโดยการจัดเก็บข้อมูลที่คงอยู่


5

มีเธรดที่ดีและโค้ดตัวอย่างที่นี่: http://forums.asp.net/t/1534903.aspx?PageIndex=2

ฉันเคยเล่นด้วยซ้ำกับความคิดที่จะเรียกเพจ keep alive บนเว็บไซต์ของฉันจากเธรดเพื่อช่วยให้แอพพูลมีชีวิต โปรดทราบว่าหากคุณใช้วิธีนี้ซึ่งคุณต้องการการจัดการการกู้คืนที่ดีมากเนื่องจากแอปพลิเคชันสามารถรีไซเคิลได้ตลอดเวลา ดังที่หลายคนกล่าวถึงนี่ไม่ใช่แนวทางที่ถูกต้องหากคุณสามารถเข้าถึงตัวเลือกบริการอื่น ๆ ได้ แต่สำหรับโฮสติ้งที่ใช้ร่วมกันนี่อาจเป็นหนึ่งในตัวเลือกเดียวของคุณ

เพื่อช่วยให้แอปพูลมีชีวิตอยู่คุณสามารถส่งคำขอไปยังไซต์ของคุณเองได้ในขณะที่เธรดกำลังประมวลผล สิ่งนี้อาจช่วยให้แอปพูลมีชีวิตอยู่หากกระบวนการของคุณใช้เวลานาน

string tempStr = GetUrlPageSource("http://www.mysite.com/keepalive.aspx");


    public static string GetUrlPageSource(string url)
    {
        string returnString = "";

        try
        {
            Uri uri = new Uri(url);
            if (uri.Scheme == Uri.UriSchemeHttp)
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                CookieContainer cookieJar = new CookieContainer();

                req.CookieContainer = cookieJar;

                //set the request timeout to 60 seconds
                req.Timeout = 60000;
                req.UserAgent = "MyAgent";

                //we do not want to request a persistent connection
                req.KeepAlive = false;

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                Stream stream = resp.GetResponseStream();
                StreamReader sr = new StreamReader(stream);

                returnString = sr.ReadToEnd();

                sr.Close();
                stream.Close();
                resp.Close();
            }
        }
        catch
        {
            returnString = "";
        }

        return returnString;
    }

5

เราเริ่มต้นเส้นทางนี้และใช้งานได้จริงเมื่อแอปของเราอยู่บนเซิร์ฟเวอร์เดียว เมื่อเราต้องการขยายไปยังเครื่องหลายเครื่อง (หรือใช้ w3wp หลายเครื่องในเว็บ garen) เราต้องประเมินอีกครั้งและดูวิธีจัดการคิวงานการจัดการข้อผิดพลาดการลองซ้ำและปัญหายุ่งยากในการล็อกอย่างถูกต้องเพื่อให้แน่ใจว่ามีเพียงเครื่องเดียว เซิร์ฟเวอร์หยิบรายการถัดไป

... เราตระหนักดีว่าเราไม่ได้อยู่ในธุรกิจการเขียนเครื่องมือประมวลผลพื้นหลังดังนั้นเราจึงมองหาโซลูชันที่มีอยู่และเราได้พบกับการหยุดทำงานของโครงการ OSS ที่ยอดเยี่ยม

Sergey Odinokov ได้สร้างอัญมณีที่แท้จริงซึ่งง่ายต่อการเริ่มต้นใช้งานและช่วยให้คุณสามารถเปลี่ยนแบ็กเอนด์ของวิธีการทำงานต่อเนื่องและจัดคิวได้ Hangfire ใช้เธรดพื้นหลัง แต่ยังคงทำงานอยู่จัดการการลองใหม่และช่วยให้คุณมองเห็นคิวงานได้ ดังนั้นงานแฮงค์ไฟร์จึงมีประสิทธิภาพและอยู่รอดจากความหลากหลายของแอปโดเมนที่ถูกรีไซเคิลเป็นต้น

การตั้งค่าพื้นฐานใช้เซิร์ฟเวอร์ sql เป็นที่เก็บข้อมูล แต่คุณสามารถเปลี่ยนเป็น Redis หรือ MSMQ ได้เมื่อถึงเวลาขยายขนาด นอกจากนี้ยังมี UI ที่ยอดเยี่ยมสำหรับการแสดงภาพงานและสถานะทั้งหมดรวมทั้งช่วยให้คุณสามารถจัดคิวงานใหม่ได้อีกด้วย

ประเด็นของฉันคือในขณะที่เป็นไปได้ทั้งหมดที่จะทำในสิ่งที่คุณต้องการในเธรดพื้นหลัง แต่ก็มีงานมากมายที่จะทำให้ปรับขนาดได้และมีประสิทธิภาพ เหมาะสำหรับปริมาณงานที่เรียบง่าย แต่เมื่อสิ่งต่าง ๆ ซับซ้อนขึ้นฉันชอบใช้ไลบรารีที่สร้างขึ้นตามวัตถุประสงค์มากกว่าที่จะใช้ความพยายามนี้

สำหรับมุมมองเพิ่มเติมเกี่ยวกับตัวเลือกที่มีให้ดูบล็อกของ Scott Hanselman ซึ่งครอบคลุมตัวเลือกบางอย่างสำหรับการจัดการงานเบื้องหลังใน asp.net (เขาให้รีวิวที่เร่าร้อนจาก Hangfire)

ตามที่ John อ้างถึงมันควรค่าแก่การอ่านบล็อกของ Phil Haack เกี่ยวกับสาเหตุที่วิธีการนี้มีปัญหาและวิธีหยุดการทำงานบนเธรดอย่างสง่างามเมื่อมีการยกเลิกการโหลด appdomain


2

คุณสามารถสร้างบริการ windows เพื่อทำงานนั้นได้หรือไม่? จากนั้นใช้. NET ระยะไกลจากเว็บเซิร์ฟเวอร์เพื่อเรียกใช้บริการ Windows เพื่อดำเนินการ? ถ้าเป็นอย่างนั้นล่ะก็ฉันจะทำยังไง

สิ่งนี้จะช่วยลดความจำเป็นในการถ่ายทอดบน IIS และ จำกัด พลังการประมวลผลบางส่วน

ถ้าไม่เช่นนั้นฉันจะบังคับให้ผู้ใช้นั่งที่นั่นในขณะที่กระบวนการเสร็จสิ้น ด้วยวิธีนี้คุณจะมั่นใจได้ว่าเสร็จสมบูรณ์และไม่ถูกฆ่าโดย IIS


2

ดูเหมือนจะมีวิธีหนึ่งที่ได้รับการสนับสนุนในการโฮสต์งานระยะยาวใน IIS บริการเวิร์กโฟลว์ดูเหมือนออกแบบมาสำหรับการนี้โดยเฉพาะอย่างยิ่งในการร่วมกับWindows Server AppFabric การออกแบบช่วยให้สามารถรีไซเคิลพูลแอปพลิเคชันได้โดยสนับสนุนการคงอยู่และการเริ่มต้นใหม่ของงานที่ดำเนินการมายาวนานโดยอัตโนมัติ


2

คุณสามารถเรียกใช้งานในพื้นหลังและงานเหล่านั้นจะเสร็จสมบูรณ์แม้ว่าคำขอจะสิ้นสุดลง อย่าปล่อยให้ข้อยกเว้นที่ไม่ถูกจับได้ถูกโยนทิ้งไป โดยปกติคุณต้องการโยนข้อยกเว้นของคุณเสมอ หากมีข้อยกเว้นเกิดขึ้นในเธรดใหม่จะทำให้กระบวนการของผู้ปฏิบัติงาน IISล้มเหลว- w3wp.exeเนื่องจากคุณไม่ได้อยู่ในบริบทของคำขออีกต่อไป จากนั้นจะฆ่างานพื้นหลังอื่น ๆ ที่คุณทำงานนอกเหนือจากเซสชันที่สำรองหน่วยความจำในกระบวนการหากคุณใช้งาน นี่จะเป็นการยากที่จะวินิจฉัยซึ่งเป็นสาเหตุที่ทำให้ไม่สามารถปฏิบัติได้


1

เพียงสร้างกระบวนการตัวแทนเพื่อเรียกใช้งาน async ไม่จำเป็นต้องเป็นบริการ windows (แม้ว่าจะเป็นวิธีที่ดีที่สุดในกรณีส่วนใหญ่ MSMQ เป็นวิธีที่เหนือกว่าการฆ่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.