2009-07-17 45 views
30

Tôi đã tạo ra một mô-đun http và trong khi gỡ lỗi tôi nhận thấy một cái gì đó mà lúc đầu (ít nhất) có vẻ như hành vi kỳ lạ.Phương thức HttpModule Init được gọi nhiều lần - tại sao?

Khi tôi đặt điểm ngắt trong phương pháp init của httpmodule tôi có thể thấy phương thức init của mô-đun http được gọi nhiều lần mặc dù tôi chỉ khởi động trang web để gỡ lỗi và thực hiện một yêu cầu duy nhất (đôi khi nó là chỉ nhấn 1 lần, thời gian khác tới 10 lần).

Tôi biết rằng tôi sẽ mong đợi một số trường hợp của HttpApplication đang chạy và cho mỗi mô-đun http sẽ được tạo ra, nhưng khi tôi yêu cầu một trang duy nhất nó sẽ được xử lý bởi một đối tượng ứng dụng http duy nhất và do đó chỉ cháy các sự kiện được kết hợp một lần, nhưng nó vẫn kích hoạt các sự kiện vài lần cho mỗi yêu cầu không có ý nghĩa - ngoài nó phải được thêm vài lần trong httpApplication đó - có nghĩa là phương thức init httpmodule giống nhau được gọi mỗi lần và không một ứng dụng http mới được tạo ra mỗi khi nó chạm vào điểm ngắt của tôi (xem ví dụ mã của tôi ở phía dưới, v.v.).

Điều gì có thể xảy ra ở đây? có phải vì tôi đang gỡ lỗi và đặt điểm ngắt trong mô-đun http không? Nó đã nhận thấy rằng có vẻ như rằng nếu tôi khởi động trang web để gỡ lỗi và nhanh chóng bước qua breakpoint trong httpmodule nó sẽ chỉ nhấn phương pháp init một lần và cùng đi cho eventhandler này. Quay lại đầu trang | Nếu thay vào đó, hãy để nó treo tại điểm ngắt trong vài giây, phương thức init được gọi nhiều lần (có vẻ như nó phụ thuộc vào thời gian tôi đợi trước khi bước qua điểm ngắt). Có lẽ điều này có thể là một số tính năng xây dựng để đảm bảo rằng httpmodule được khởi tạo và ứng dụng http có thể phục vụ các yêu cầu, nhưng nó cũng có vẻ giống như một cái gì đó mà có thể có hậu quả thảm khốc. Điều này có vẻ hợp lý, vì nó có thể đang cố gắng hoàn thành yêu cầu và vì tôi đã thiết lập điểm ngắt nó nghĩ rằng đã xảy ra lỗi và thử gọi lại phương thức init? nó có thể xử lý yêu cầu không?

Nhưng đây có phải là những gì đang xảy ra và mọi thứ đều ổn (tôi chỉ đoán), hay nó là một vấn đề thực sự?

Điều tôi đặc biệt quan tâm là nếu một thứ gì đó khiến nó treo trên máy chủ "sản xuất/sống" trong vài giây, rất nhiều trình xử lý sự kiện được thêm vào init và do đó mỗi yêu cầu đến trang đột nhiên kích hoạt trình quản lý sự kiện vài lần.

Hành vi này có thể nhanh chóng đưa mọi trang web xuống.

Tôi đã xem mã "." Gốc .net được sử dụng cho httpmodules cho formsauthentication và rolemanagermodule v.v. nhưng mã của tôi không khác biệt gì với các mô-đun đó.

Mã của tôi trông như thế này.

public void Init(HttpApplication app) 
    { 
     if (CommunityAuthenticationIntegration.IsEnabled) 
     { 
      FormsAuthenticationModule formsAuthModule = (FormsAuthenticationModule) app.Modules["FormsAuthentication"];   

      formsAuthModule.Authenticate += new FormsAuthenticationEventHandler(this.OnAuthenticate); 
     } 
    } 

đây là một ví dụ làm thế nào nó được thực hiện trong RoleManagerModule từ NET framework

public void Init(HttpApplication app) 
    { 
     if (Roles.Enabled) 
     { 
      app.PostAuthenticateRequest += new EventHandler(this.OnEnter); 
      app.EndRequest += new EventHandler(this.OnLeave); 
     } 
    } 

Làm bất cứ ai biết điều gì đang xảy ra vậy?

(tôi chỉ hy vọng ai đó ngoài kia có thể cho tôi biết tại sao điều này đang xảy ra và đảm bảo với tôi rằng mọi thứ đều hoàn toàn tốt đẹp) :)


UPDATE:

Tôi đã cố gắng để thu hẹp vấn đề và cho đến nay tôi đã thấy rằng phương pháp Init được gọi là luôn luôn trên một đối tượng mới của mô-đun http của tôi (contary với những gì tôi nghĩ trước).

Tôi có vẻ như yêu cầu đầu tiên (khi khởi động trang) tất cả các đối tượng HttpApplication được tạo và các mô-đun của nó đều cố gắng phân phát yêu cầu đầu tiên và do đó tất cả đều nhấn tổ chức sự kiện đang được thêm vào. Tôi không thể thực sự tìm ra lý do tại sao điều này xảy ra.

Nếu tôi yêu cầu một trang khác, tất cả các ứng dụng của HttpApplication được tạo ra (và moduless của họ) sẽ lại cố gắng phục vụ yêu cầu khiến nó chạm vào eventhandler nhiều lần. Nhưng nó cũng có vẻ như là nếu tôi sau đó nhảy trở lại trang đầu tiên (hoặc một cái khác) chỉ có một HttpApplication sẽ bắt đầu để chăm sóc yêu cầu và tất cả mọi thứ như mong đợi - miễn là tôi không để cho nó treo ở một điểm ngắt.

Nếu tôi để nó treo tại điểm ngắt, nó bắt đầu tạo đối tượng HttpApplication mới và bắt đầu thêm HttpApplications (nhiều hơn 1) để phục vụ/xử lý yêu cầu (hiện đang được phục vụ bởi HttpApplication hiện đang bị dừng tại điểm ngắt).

Tôi đoán hoặc hy vọng rằng đó có thể là cách thông minh "đằng sau hậu trường" giúp phân phối và xử lý tải và/hoặc lỗi. Nhưng tôi không có đầu mối. Tôi hy vọng một số trong đó có thể đảm bảo với tôi rằng nó là hoàn toàn tốt đẹp và làm thế nào nó được cho là?

+2

Tôi nhìn thấy hành vi này trong HttpModule của chúng tôi cũng như –

+0

John: Tôi đã quên đóng chương trình này. Nó xảy ra (ít nhất là trong trường hợp của tôi) bởi vì các mô-đun được nhấn bởi mỗi yêu cầu duy nhất trình duyệt làm cho bất kỳ tài nguyên (hình ảnh, javascripts, stylesheets). Lý do tại sao nó được nhấn là mô-đun định tuyến được sử dụng trong vv MVC. Tất cả các yêu cầu cần phải được xử lý bởi các mô-đun định tuyến và cho tất cả các yêu cầu nhấn mô-đun không may. Tôi đã hỏi một câu hỏi về điều này, nhưng không ai trả lời. – MartinF

Trả lời

7
  1. Kiểm tra HttpContext.Current.Request để xem, cho những gì yêu cầu init của mô-đun được sa thải. Có thể trình duyệt gửi nhiều yêu cầu.

  2. Nếu bạn kết nối với IIS, hãy kiểm tra nhật ký IIS để biết liệu có bất kỳ yêu cầu nào được nhận cho thời gian bạn đang ở điểm ngắt hay không.

+0

Chính xác. Điều này xảy ra vì tất cả các yêu cầu (đối với bất kỳ tài nguyên nào) đều truy cập vào mô-đun. Trong trường hợp của tôi vì mô-đun định tuyến có vẻ như vậy. – MartinF

2

Dưới đây là một số giải thích về những gì bạn nên sử dụng, khi nào và cách chúng hoạt động. When to use Application_Start vs Init in Global.asax?

Edit: More đọc

The ASP Column: HTTP Modules

INFO: Application Instances, Application Events, and Application State in ASP.NET

+0

Cảm ơn bạn đã trả lời. Tôi không sử dụng bất kỳ thứ gì trong số đó. Init trong asax toàn cục là ghi đè init trong HttpApplication. Vấn đề của tôi là phương thức Init trong httpmodule của tôi đang được gọi nhiều lần. Theo tôi hiểu, nó sẽ tạo các mô-đun http cho mỗi ứng dụng http. Trên mỗi yêu cầu, một trong những HttpApplication của instanciated sẽ xử lý yêu cầu, và kích hoạt các sự kiện được thêm vào, nhưng vì lý do nào đó eventhandler * đôi khi * được thêm nhiều hơn một lần làm phương thức init trên cùng một mô-đun http (không phải là một cái mới được tạo cho các ứng dụng httpapplication khác) được gọi là lần máy chủ. – MartinF

+0

Đọc thêm để đọc. –

40

Phương thức Init() thường được gọi nhiều lần. Khi một ứng dụng khởi động, quá trình ASP.NET Worker sẽ khởi tạo như nhiều đối tượng HttpApplication như nó nghĩ nó cần, sau đó nó sẽ gộp chúng lại (ví dụ như tái sử dụng chúng cho các yêu cầu mới, tương tự như kết nối cơ sở dữ liệu tổng hợp).

Bây giờ cho mỗi đối tượng HttpApplication, nó cũng sẽ khởi tạo một bản sao của mỗi IHttpModule được đăng ký và gọi phương thức Init nhiều lần. Vì vậy, nếu 5 đối tượng HttpApplication được tạo, 5 bản sao của IHttpModule của bạn sẽ được tạo và phương thức Init của bạn được gọi là 5 lần. Có lý?

Bây giờ tại sao nó khởi tạo 5 HttpApplications objects says? Cũng có thể trang ASPX của bạn có liên kết đến các tài nguyên khác mà trình duyệt của bạn sẽ cố tải xuống, css, javascript, WebResource.aspx, có thể là một iframe ở đâu đó. Hoặc có thể là ASP.NET Worker Process 'đang trong tâm trạng' để bắt đầu hơn 1 đối tượng HttpApplication, đó thực sự là một chi tiết/tối ưu hóa nội bộ của quá trình ASP.NET chạy dưới IIS (hoặc VS được xây dựng trong máy chủ web).

Nếu bạn muốn mã đó là bảo đảm để chạy chỉ một lần (và không muốn sử dụng sự kiện Application_StartUp trong Global.asax), bạn có thể thử những điều sau đây trong IHttpModule của bạn:

private static bool HasAppStarted = false; 
private readonly static object _syncObject = new object(); 

public void Init(HttpApplication context) 
{ 
    if (!HasAppStarted) 
    { 
     lock (_syncObject) 
     { 
      if (!HasAppStarted) 
      { 
       // Run application StartUp code here 

       HasAppStarted = true; 
      } 
     } 
    } 
} 

tôi đã làm một cái gì đó tương tự và nó có vẻ làm việc, mặc dù tôi muốn chào đón phê bình của công việc của tôi trong trường hợp tôi đã bỏ lỡ một cái gì đó.

+10

Đây là nơi để sử dụng 'Interlocked.CompareExchange' thay vì khóa :) –

+1

Thậm chí có yêu cầu tạo tệp tài nguyên gây ra tạo lớp ứng dụng và mô-đun riêng của nó và do đó kích hoạt sự kiện này không? –

1

Kiểm tra trên khóa khóa IHttpModule cho tất cả các yêu cầu và sau đó, nó giải phóng toàn bộ ứng dụng. Nếu cuộc gọi IHttpModule của bạn yêu cầu nhiều lần là cần thiết để gọi HttpApplication phương pháp CompleteRequest và xử lý các trường hợp HttpApplication của IHttpModule trong sự kiện EndRequest để loại bỏ hiện của HttpApplication như thế này:

public class TestModule :IHttpModule 
    { 
     #region IHttpModule Members 

     public void Dispose() 
     { 

     } 

     public void Init(HttpApplication context) 
     { 
      context.BeginRequest += new EventHandler(context_BeginRequest); 
      context.EndRequest += new EventHandler(context_EndRequest); 
     } 

     void context_EndRequest(object sender, EventArgs e) 
     { 
      HttpApplication app = sender as HttpApplication; 
      app.CompleteRequest(); 
      app.Dispose(); 
     } 

     void context_BeginRequest(object sender, EventArgs e) 
     { 
      //your code here 
     } 

     #endregion 
    } 

Nếu bạn cần các yêu cầu IHttpModule mỗi lần mà không cần đăng ký lại khi sử dụng mã này ở trên.

+0

Đây là một điểm tốt, nhưng đối với một số ứng dụng (như của tôi) mã sẽ được thực hiện là một vài phần triệu giây, do đó, khóa chắc chắn là chi phí-hiệu quả. Nhưng cảm ơn ý tưởng, tôi nghĩ rằng việc này sẽ cải thiện khả năng viết mã của tôi. – Abacus

Các vấn đề liên quan