Tôi có thể sử dụng các luồng để thực hiện các công việc lâu dài trên IIS không?


85

Trong ứng dụng ASP.Net, người dùng nhấp vào một nút trên trang web và thao tác này sau đó khởi tạo một đối tượng trên máy chủ thông qua trình xử lý sự kiện và gọi một phương thức trên đối tượng. Phương pháp này được chuyển sang hệ thống bên ngoài để thực hiện công việc và quá trình này có thể mất một lúc. Vì vậy, những gì tôi muốn làm là chạy lệnh gọi phương thức đó trong một chuỗi khác để tôi có thể trả lại quyền kiểm soát cho người dùng với "Yêu cầu của bạn đã được gửi". Tôi rất vui khi làm điều này như fire-and-forget, mặc dù nó sẽ còn tốt hơn nếu người dùng có thể tiếp tục thăm dò đối tượng để biết trạng thái.

Điều tôi không biết là liệu IIS có cho phép luồng của tôi tiếp tục chạy hay không, ngay cả khi phiên người dùng hết hạn. Hãy tưởng tượng, người dùng kích hoạt sự kiện và chúng tôi khởi tạo đối tượng trên máy chủ và kích hoạt phương thức trong một luồng mới. Người dùng hài lòng với thông báo "Yêu cầu của bạn đã được gửi" và đóng trình duyệt của mình. Cuối cùng, phiên người dùng này sẽ hết thời gian trên IIS, nhưng luồng có thể vẫn đang chạy và hoạt động. IIS sẽ cho phép luồng tiếp tục chạy hay nó sẽ giết nó và loại bỏ đối tượng sau khi phiên người dùng hết hạn?

CHỈNH SỬA: Từ các câu trả lời và nhận xét, tôi hiểu rằng cách tốt nhất để thực hiện việc này là chuyển quá trình xử lý lâu dài ra bên ngoài IIS. Ngoài mọi thứ khác, điều này giải quyết vấn đề tái chế tên miền ứng dụng. Trong thực tế, tôi cần khởi động phiên bản 1 trong thời gian có hạn và phải làm việc bên trong một khuôn khổ hiện có, vì vậy tôi muốn tránh lớp dịch vụ, do đó tôi muốn chỉ thực hiện chuỗi bên trong IIS. Trong thực tế, "chạy dài" ở đây sẽ chỉ trong vài phút và đồng thời trên trang web sẽ thấp nên không sao cả. Tuy nhiên, phiên bản tiếp theo chắc chắn sẽ cần tách thành một lớp dịch vụ riêng biệt.


1
Bạn có thêm bất kỳ chi tiết nào về những gì cần được xử lý và môi trường lưu trữ của bạn không? Ví dụ, bạn có thể cài đặt một dịch vụ không?
senfo

Bạn luôn có thể sử dụng phương pháp lập trình Async và Await (Visual Studio 2012+). Gọn gàng hơn nhiều so với việc tự quản lý các luồng.
SausageFingers

Câu trả lời:


65

Bạn có thể hoàn thành những gì bạn muốn, nhưng đó thường là một ý tưởng tồi. Một số blog ASP.NET và công cụ CMS thực hiện cách tiếp cận này, vì chúng muốn có thể cài đặt được trên hệ thống lưu trữ chia sẻ và không phụ thuộc vào dịch vụ windows cần được cài đặt. Thông thường, họ bắt đầu một chuỗi đang chạy dài trong Global.asax khi ứng dụng khởi động và để quy trình chuỗi đó được xếp vào hàng đợi các tác vụ.

Ngoài việc giảm tài nguyên có sẵn cho IIS / ASP.NET để xử lý các yêu cầu, bạn cũng gặp vấn đề với luồng bị ngắt khi AppDomain được tái chế và sau đó bạn phải đối phó với sự tồn tại của tác vụ trong khi nó đang trong quá trình bay, như cũng như bắt đầu sao lưu công việc khi AppDomain hoạt động trở lại.

Hãy nhớ rằng trong nhiều trường hợp, AppDomain được tự động tái chế ở khoảng thời gian mặc định, cũng như nếu bạn cập nhật web.config, v.v.

Nếu bạn có thể xử lý các khía cạnh tồn tại và giao dịch của luồng của bạn bị giết bất cứ lúc nào, thì bạn có thể xử lý việc tái chế AppDomain bằng cách có một số quy trình bên ngoài đưa ra yêu cầu trên trang web của bạn vào một khoảng thời gian nào đó - để nếu trang web được tái chế, bạn được đảm bảo sẽ tự động khởi động lại trong vòng X phút.

Một lần nữa, đây thường là một ý tưởng tồi.

CHỈNH SỬA: Dưới đây là một số ví dụ về kỹ thuật này đang hoạt động:

Máy chủ cộng đồng: Sử dụng Windows Services so với Chuỗi nền để chạy mã theo các khoảng thời gian đã lên lịch Tạo chuỗi nền khi trang web khởi động lần đầu

EDIT (từ tương lai xa) - Những ngày này tôi sẽ sử dụng Hangfire .


1
Cảm ơn, điều này là sâu sắc. Tái chế là một kẻ giết người rõ ràng và tôi lấy từ những gì bạn đang nói rằng tôi có thể làm điều này như một việc khó và nhanh chóng nhưng nó rất nguy hiểm.
Frans

Vâng, ở đây kiểm tra này: professionalaspnet.com/archive/2007/09/02/... Và đây là một chủ đề về cách server community xử lý cùng một điều: dev.communityserver.com/forums/t/459942.aspx
Bryan Batchelder

Trong trường hợp của tôi, tôi có thể chỉ cho IIS không bao giờ tái chế và không bao giờ ngừng hoạt động trên trang web. Điều này khiến tôi có thể làm việc theo giả định này và trang web sẽ chỉ được tái chế trong khi triển khai và bất cứ khi nào xảy ra bảo trì theo kế hoạch. Vì tôi chỉ làm một trang web loại quản trị, điều này làm việc hoàn hảo cho tôi.
Brett

1
Kỳ lạ là điều này có rất nhiều ủng hộ. Điều này có thể xảy ra là một trong những điều rất thú vị về ASP.NET: Đó là tất cả các yêu cầu đều được phục vụ trong cùng một AppDomain cùng với bất kỳ luồng nền nào mà bạn có thể tạo. Không có lý do nào cho thấy đây là một "ý tưởng tồi" khiến tôi thuyết phục. AppDomains có thể đi xuống, vâng - nhưng điều đó hầu như không duy nhất đối với môi trường ASP.NET. Bạn cần phải chơi tốt với môi trường lưu trữ của mình (google cho HostingEnvironment.RegisterObject).
John

3
.NET 4.5.2 đã thêm một tính năng mới QueueBackgroundWorkItemđể dễ dàng đăng ký các tác vụ nền, bạn có thể muốn cập nhật lại câu trả lời của mình.
Scott Chamberlain

39

Tôi không đồng ý với câu trả lời được chấp nhận.

Sử dụng một chuỗi nền (hoặc một tác vụ, bắt đầu bằng Task.Factory.StartNew) là tốt trong ASP.NET. Như với tất cả các môi trường lưu trữ, bạn có thể muốn hiểu và hợp tác với các cơ sở quản lý việc ngừng hoạt động.

Trong ASP.NET, bạn có thể đăng ký công việc cần dừng một cách duyên dáng khi tắt máy bằng HostingEnvironment.RegisterObjectphương pháp này. Xem bài viết này và các ý kiến ​​để thảo luận.

(Như Gerard đã chỉ ra trong nhận xét của mình, bây giờ cũng HostingEnvironment.QueueBackgroundWorkItemcó một cuộc gọi yêu RegisterObjectcầu đăng ký một bộ lập lịch để mục nền hoạt động. Nhìn chung, phương pháp mới tốt hơn vì nó dựa trên nhiệm vụ.)

Đối với chủ đề chung mà bạn thường nghe về đó là một ý tưởng tồi, hãy xem xét phương án thay thế triển khai dịch vụ windows (hoặc một loại ứng dụng bổ sung khác):

  • Không còn triển khai tầm thường với triển khai web
  • Không thể triển khai hoàn toàn trên các trang web Azure
  • Tùy thuộc vào bản chất của nhiệm vụ nền, các quy trình có thể sẽ phải giao tiếp với nhau. Điều đó có nghĩa là một số dạng IPC hoặc dịch vụ sẽ phải truy cập vào một cơ sở dữ liệu chung.

Cũng lưu ý rằng một số trường hợp nâng cao thậm chí có thể cần luồng nền chạy trong cùng một không gian địa chỉ với các yêu cầu. Tôi thấy thực tế là ASP.NET có thể làm điều này là một lợi thế lớn đã trở nên khả thi thông qua .NET.


Điều đó thật tuyệt. Tôi có một nhiệm vụ cần ~ 30 giây để hoàn thành. Hoàn hảo nếu bạn chỉ cần trì hoãn nhóm ứng dụng đủ lâu để hoàn thành công việc.
Scuba Steve

1
Từ .Net Framework 4.5.2, bạn cũng có thể sử dụng HostingEnosystem.QueueBackgroundWorkItem (Action <CancellationToken>)
Gerard Carbó

Theo quan sát của tôi, có vẻ như IIS cuối cùng cũng tắt ASP.NET Web API mà tôi đang chạy và không khởi động lại nó cho đến khi có yêu cầu mới. Vì vậy, vào thời điểm đã định khi chuỗi của tôi được cho là khởi chạy ứng dụng có thể thậm chí không được chạy. Tôi có sai về điều này không?
David Sopko

7

Bạn sẽ không muốn sử dụng một luồng từ nhóm luồng IIS cho tác vụ này vì nó sẽ khiến luồng đó không thể xử lý các yêu cầu trong tương lai. Bạn có thể xem xét các Trang không đồng bộ trong ASP.NET 2.0 , nhưng đó thực sự cũng không phải là câu trả lời đúng. Thay vào đó, điều có vẻ như bạn sẽ được hưởng lợi từ việc xem xét Hàng đợi Thư của Microsoft . Về cơ bản, bạn sẽ thêm chi tiết nhiệm vụ vào hàng đợi và một quy trình nền khác (có thể là Dịch vụ Windows) sẽ chịu trách nhiệm thực hiện tác vụ đó. Nhưng điểm mấu chốt là quá trình nền hoàn toàn bị cô lập với IIS.


À, MSMQ - một trong những công nghệ yêu thích từ lâu của tôi. Cảm ơn cho cái nhìn sâu sắc và liên kết.
Frans

6

Tôi sẽ đề nghị sử dụng HangFire cho các yêu cầu như vậy. Công cụ chữa cháy và quên tuyệt vời của nó chạy trong nền, hỗ trợ kiến ​​trúc khác nhau, đáng tin cậy vì nó được hỗ trợ bởi lưu trữ bền bỉ.


5

Có một chủ đề tốt và mã mẫu ở đây: http://forums.asp.net/t/1534903.aspx?PageIndex=2

Tôi thậm chí còn đùa giỡn với ý tưởng gọi một trang duy trì hoạt động trên trang web của mình từ chuỗi để giúp giữ cho nhóm ứng dụng tồn tại. Hãy nhớ rằng nếu bạn đang sử dụng phương pháp này, bạn cần xử lý khôi phục thực sự tốt, vì ứng dụng có thể tái chế bất kỳ lúc nào. Như nhiều người đã đề cập, đây không phải là cách tiếp cận đúng nếu bạn có quyền truy cập vào các tùy chọn dịch vụ khác, nhưng đối với dịch vụ lưu trữ được chia sẻ, đây có thể là một trong những lựa chọn duy nhất của bạn.

Để giúp giữ cho nhóm ứng dụng tồn tại, bạn có thể đưa ra yêu cầu đối với trang web của riêng mình trong khi chuỗi đang xử lý. Điều này có thể giúp giữ cho nhóm ứng dụng tồn tại nếu quá trình của bạn chạy trong một thời gian dài.

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

Chúng tôi đã bắt đầu theo con đường này và nó thực sự hoạt động tốt khi ứng dụng của chúng tôi ở trên một máy chủ. Khi chúng tôi muốn mở rộng quy mô cho nhiều máy (hoặc sử dụng nhiều w3wp trong một web garen), chúng tôi phải đánh giá lại và xem xét cách quản lý hàng đợi công việc, xử lý lỗi, thử lại và vấn đề phức tạp là khóa chính xác để đảm bảo chỉ một máy chủ chọn mục tiếp theo.

... chúng tôi nhận ra rằng chúng tôi không kinh doanh trong việc viết các công cụ xử lý nền vì vậy chúng tôi đã tìm kiếm các giải pháp hiện có và chúng tôi đã hạ cánh bằng cách sử dụng hangfire dự án OSS tuyệt vời

Sergey Odinokov đã tạo ra một viên ngọc quý thực sự rất dễ bắt đầu và cho phép bạn hoán đổi phần phụ trợ về cách công việc được duy trì và xếp hàng. Hangfire sử dụng các luồng nền, nhưng vẫn tồn tại các công việc, xử lý các lần thử lại và cung cấp cho bạn khả năng hiển thị trong hàng đợi công việc. Vì vậy, các công việc hangfire rất mạnh mẽ và tồn tại sau tất cả những thay đổi về tên miền ứng dụng được tái chế, v.v.

Thiết lập cơ bản của nó sử dụng máy chủ sql làm nơi lưu trữ nhưng bạn có thể đổi lấy Redis hoặc MSMQ khi cần mở rộng quy mô. Nó cũng có một giao diện người dùng tuyệt vời để hiển thị tất cả các công việc và trạng thái của chúng cộng với việc cho phép bạn xếp hàng lại các công việc.

Quan điểm của tôi là mặc dù hoàn toàn có thể làm những gì bạn muốn trong một luồng nền, nhưng còn rất nhiều việc để làm cho nó có thể mở rộng và mạnh mẽ. Nó tốt cho khối lượng công việc đơn giản nhưng khi mọi thứ trở nên phức tạp hơn, tôi thích sử dụng một thư viện được xây dựng có mục đích hơn là thực hiện nỗ lực này.

Để biết thêm một số quan điểm về các tùy chọn có sẵn, hãy xem blog của Scott Hanselman, blog này bao gồm một số tùy chọn để xử lý các công việc nền trong asp.net. (Anh ấy đã đánh giá hangfire rực rỡ)

Cũng như được tham khảo bởi John, đáng để đọc blog của Phil Haack về lý do tại sao cách tiếp cận có vấn đề và cách dừng hoạt động trên chuỗi một cách duyên dáng khi miền ứng dụng được dỡ bỏ.


2

Bạn có thể tạo một dịch vụ windows để thực hiện nhiệm vụ đó không? Sau đó, sử dụng .NET remove from Web Server để gọi Dịch vụ Windows để thực hiện hành động? Nếu đó là trường hợp, đó là những gì tôi sẽ làm.

Điều này sẽ loại bỏ nhu cầu chuyển tiếp trên IIS và hạn chế một số công suất xử lý của nó.

Nếu không thì tôi sẽ buộc người dùng ngồi ở đó trong khi quá trình được thực hiện. Bằng cách đó, bạn đảm bảo nó được hoàn thành và không bị giết bởi IIS.


2

Có vẻ như có một cách được hỗ trợ để lưu trữ công việc lâu dài trong IIS. Workflow Services dường như được thiết kế cho việc này, đặc biệt là khi kết hợp với Windows Server AppFainst . Thiết kế cho phép tái chế hồ bơi ứng dụng bằng cách hỗ trợ tính năng tự động liên tục và tiếp tục công việc lâu dài.


2

Bạn có thể chạy các tác vụ trong nền và chúng sẽ hoàn thành ngay cả khi yêu cầu kết thúc. Đừng để một ngoại lệ không cần thiết được ném ra . Thông thường, bạn luôn muốn ném các ngoại lệ của mình. Nếu một ngoại lệ được đưa ra trong một luồng mới thì nó sẽ làm hỏng quy trình công nhân IIS - w3wp.exe , vì bạn không còn ở trong ngữ cảnh của yêu cầu. Sau đó, điều đó cũng sẽ giết chết bất kỳ tác vụ nền nào khác mà bạn đang chạy ngoài các phiên được sao lưu bộ nhớ trong quá trình nếu bạn đang sử dụng chúng. Điều này sẽ khó chẩn đoán, đó là lý do tại sao phương pháp này không được khuyến khích.


1

Chỉ cần tạo một quy trình thay thế để chạy các tác vụ không đồng bộ; nó không nhất thiết phải là một dịch vụ windows (mặc dù đó là cách tiếp cận tối ưu hơn trong hầu hết các trường hợp. MSMQ là cách giết chết hoàn toàn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.