2012-04-03 18 views
12

Tôi có ứng dụng máy chủ ASP.NET 3.5 được viết bằng C#. Nó làm cho các yêu cầu gửi đến một API REST bằng cách sử dụng HttpWebRequest và HttpWebResponse.HttpWebResponse sẽ không mở rộng cho các yêu cầu gửi đi đồng thời

Tôi đã thiết lập một ứng dụng thử nghiệm để gửi các yêu cầu này trên các chuỗi riêng biệt (để mơ hồ bắt chước đồng thời với máy chủ).

Xin lưu ý đây là câu hỏi về Mono/Environment nhiều hơn câu hỏi về mã; vì vậy hãy nhớ rằng mã bên dưới không đúng nguyên văn; chỉ cần cắt/dán các bit chức năng.

Dưới đây là một số mã giả:

// threaded client piece 
int numThreads = 1; 
ManualResetEvent doneEvent; 

using (doneEvent = new ManualResetEvent(false)) 
     { 

      for (int i = 0; i < numThreads; i++) 
      { 

       ThreadPool.QueueUserWorkItem(new WaitCallback(Test), random_url_to_same_host); 

      } 
      doneEvent.WaitOne(); 
     } 

void Test(object some_url) 
{ 
    // setup service point here just to show what config settings Im using 
    ServicePoint lgsp = ServicePointManager.FindServicePoint(new Uri(some_url.ToString())); 

     // set these to optimal for MONO and .NET 
     lgsp.Expect100Continue = false; 
     lgsp.ConnectionLimit = 100; 
     lgsp.UseNagleAlgorithm = true; 
     lgsp.MaxIdleTime = 100000;   

    _request = (HttpWebRequest)WebRequest.Create(some_url); 


    using (HttpWebResponse _response = (HttpWebResponse)_request.GetResponse()) 
    { 
     // do stuff 
    } // releases the response object 

    // close out threading stuff 

    if (Interlocked.Decrement(ref numThreads) == 0) 
    { 
     doneEvent.Set(); 
    } 
} 

Nếu tôi chạy các ứng dụng trên máy tính phát triển địa phương của tôi (Windows 7) trong máy chủ Studio web Visual, tôi có thể lên numThreads và nhận được phản hồi trung bình cùng thời gian có biến thể tối thiểu cho dù đó là 1 "người dùng" hoặc 100.

Xuất bản và triển khai ứng dụng lên Apache2 trên môi trường Mono 2.10.2, thời gian phản hồi quy mô gần như tuyến tính. (ví dụ: 1 sợi = 300ms, 5 chủ đề = 1500 mili giây, 10 luồng = 3000 mili giây). Điều này xảy ra bất kể điểm cuối máy chủ (tên máy chủ khác nhau, mạng khác nhau, v.v.).

Sử dụng IPTRAF (và các công cụ mạng khác), có vẻ như ứng dụng chỉ mở 1 hoặc 2 cổng để định tuyến tất cả các kết nối và các phản hồi còn lại phải đợi.

Chúng tôi đã xây dựng một ứng dụng PHP tương tự và được triển khai trong Mono với cùng yêu cầu và tỷ lệ phản hồi một cách thích hợp.

Tôi đã chạy qua mọi cài đặt cấu hình đơn lẻ mà tôi có thể nghĩ đến cho Mono và Apache và cài đặt CHỈ khác nhau giữa hai môi trường (ít nhất là trong mã) là đôi khi ServicePoint SupportsPipelining = false in Mono, trong khi đó là đúng từ máy của tôi.

Dường như mặc dù ConnectionLimit (mặc định là 2) không bị thay đổi trong Mono vì lý do nào đó nhưng tôi đặt giá trị cao hơn cả mã và web.config cho (các) máy chủ được chỉ định.

Hoặc tôi và nhóm của tôi đang xem cái gì đó quan trọng hoặc đây là một số loại lỗi trong Mono.

+1

Bạn đã tìm thấy giải pháp chưa? – Kugel

Trả lời

8

Tôi tin rằng bạn đang nhấn nút cổ chai trong số HttpWebRequest. Mỗi yêu cầu web sử dụng cơ sở hạ tầng điểm dịch vụ chung trong khuôn khổ .NET. Điều này dường như được dự định để cho phép yêu cầu đến cùng một máy chủ được sử dụng lại, nhưng trong kinh nghiệm của tôi dẫn đến hai nút cổ chai.

Đầu tiên, các điểm dịch vụ chỉ cho phép hai kết nối đồng thời đến một máy chủ đã cho theo mặc định để tuân thủ đặc tả HTTP. Điều này có thể được ghi đè bằng cách đặt thuộc tính tĩnh ServicePointManager.DefaultConnectionLimit thành giá trị cao hơn. Xem trang MSDN này để biết thêm chi tiết. Dường như bạn đã giải quyết vấn đề này cho chính điểm dịch vụ riêng lẻ, nhưng do lược đồ khóa đồng thời ở cấp điểm dịch vụ, việc làm như vậy có thể góp phần vào nút cổ chai.

Thứ hai, dường như có sự cố với độ chi tiết khóa trong chính lớp ServicePoint. Nếu bạn dịch ngược và nhìn vào nguồn cho từ khóa lock, bạn sẽ thấy rằng nó sử dụng chính cá thể đó để đồng bộ hóa và làm như vậy ở nhiều nơi. Với cá thể điểm dịch vụ được chia sẻ giữa các yêu cầu web cho một máy chủ nhất định, trong kinh nghiệm của tôi, điều này có xu hướng thắt cổ chai khi có thêm HttpWebRequests được mở và khiến cho quy mô kém.Điểm thứ hai này chủ yếu là quan sát cá nhân và chọc quanh nguồn, vì vậy hãy lấy nó bằng một hạt muối; Tôi sẽ không coi đó là một nguồn có thẩm quyền.

Thật không may, tôi không tìm thấy sự thay thế hợp lý vào thời điểm tôi đang làm việc với nó. Bây giờ ASP.NET Web API đã được phát hành, bạn có thể muốn cung cấp cho giao diện HttpClient. Hy vọng rằng sẽ giúp.

+0

Jesse, cảm ơn thông tin. Đặt ServicePointManager DefaultConnectionLimit vẫn đơn giản đặt nó trên ServicePoint/endpoint vì vậy tôi không biết liệu điều đó có thể bỏ qua vấn đề khóa mà bạn mô tả ở cấp SP hay không. Tôi chắc chắn sẽ mong đợi ServicePoint để nút cổ chai như các chủ đề/yêu cầu phát triển nhưng không quá đáng kể. Và nó vẫn có vẻ như các yêu cầu đi ra không có vấn đề nhưng các phản ứng chờ đợi. Tôi sẽ phải nhìn vào HttpClient như là một thay thế. – erasend

+0

Re: DefaultConnectionLimit - Tôi không tuyên bố rằng nó sẽ làm việc phép lạ, nhưng nó bỏ qua một cuộc gọi để khóa (này) trong setter cho ConnectionLimit trong ServicePoint chính nó. Đối với việc mở rộng quy mô, ấn tượng của tôi là như nhau. Tôi vẫn không thể tuyên bố thực sự hiểu tại sao nút cổ chai lại gặp khó khăn như vậy. Có một số cấu trúc đồng bộ hóa xung quanh nhận phản hồi và một lần nữa nếu nhận được luồng.Trong trường hợp của bạn, tôi sẽ suy đoán sự sụt giảm từ cục bộ sang phi địa phương là do độ trễ bổ sung với một máy chủ bên ngoài ... nhưng, một lần nữa, đó là đầu cơ thuần túy. –

+0

DefaultConnectionLimit không thay đổi bất cứ điều gì (và loại bỏ ConnectionLimit trên đối tượng SP đã hoàn nguyên nó về mặc định là 2). Sự khác biệt từ địa phương đến phi địa phương chắc chắn là Mono/môi trường liên quan vì kịch bản lệnh PHP không có vấn đề gì - cùng một yêu cầu. Đó là những gì Im cố gắng để có được dưới cùng của, theo giả định rằng đó là cách Mono đang xử lý các phản ứng này. – erasend

8

Tôi biết điều này là khá cũ nhưng tôi đặt này ở đây trong trường hợp nó có thể giúp ai đó khác chạy vào vấn đề này. Chúng tôi đã gặp sự cố tương tự với các yêu cầu HTTPS đi song song. Có một vài vấn đề khi chơi.

Vấn đề đầu tiên là ServicePointManager.DefaultConnectionLimit không thay đổi giới hạn kết nối theo như tôi có thể biết. Đặt giá trị này thành 50, tạo kết nối mới và sau đó kiểm tra giới hạn kết nối trên điểm dịch vụ cho kết nối mới nói 2. Đặt nó trên điểm dịch vụ đó thành 50 lần xuất hiện để hoạt động và tồn tại cho tất cả các kết nối sẽ kết thúc điểm dịch vụ đó.

Vấn đề thứ hai mà chúng tôi gặp phải là luồng. Việc thực hiện hiện tại của hồ bơi thread mono xuất hiện để tạo ra tối đa 2 chủ đề mới mỗi giây. Đây là vĩnh cửu nếu bạn đang thực hiện nhiều yêu cầu song song bắt đầu chính xác cùng lúc. Để chống lại điều này, chúng tôi đã thử đặt ThreadPool.SetMinThreads thành một số cao hơn. Có vẻ như Mono chỉ tạo tối đa 1 chuỗi mới khi bạn thực hiện cuộc gọi này, bất kể delta giữa số chuỗi hiện tại và số mong muốn. Chúng tôi đã có thể làm việc xung quanh điều này bằng cách gọi SetMinThreads trong một vòng lặp cho đến khi hồ bơi thread có số lượng mong muốn của các chủ đề nhàn rỗi.

Tôi mở một lỗi về vấn đề thứ hai bởi vì đó là một trong những Tôi tự tin nhất là không làm việc như mong đợi: https://bugzilla.xamarin.com/show_bug.cgi?id=7055

+0

Có thể xác nhận 'ServicePointManager.DefaultConnectionLimit' không có hiệu lực trong Mono, giới hạn vẫn là 2 – KCD

+0

Đặt nó trong [' app.config' hoặc 'web.config'] (http://stackoverflow.com/a/436477/516748) các công trình ... tuy nhiên đang chứng tỏ khả năng phản tác dụng ở Mono với bất kỳ điều gì khác ngoài 2 giảm thông lượng ồ ạt (?!). Ngoài ra ServicePoint.Connections luôn luôn báo cáo 0 trong khi Windows báo cáo lên đến và chỉ qua ConnectionLimit ... – KCD

0

Nếu @ jake-moshenko là đúng về ServicePointManager.DefaultConnectionLimit không có bất kỳ tác dụng nếu thay đổi trong Mono, vui lòng gửi lỗi này dưới dạng lỗi trong số http://bugzilla.xamarin.com/.

Tuy nhiên tôi sẽ cố gắng một số điều trước khi vứt bỏ này hoàn toàn là một vấn đề Mono:

  1. Hãy thử sử dụng các bộ thu rác sgen thay vì một Boehm cũ, bằng cách thông qua --gc=sgen như một lá cờ để mono.
  2. Nếu phần trên không có tác dụng, hãy nâng cấp lên Mono 3.2 (BTW mặc định là SGEN GC), vì đã có nhiều bản sửa lỗi kể từ khi bạn đặt câu hỏi.
  3. Nếu phần trên không có tác dụng, hãy xây dựng Mono (nhánh chính) của riêng bạn, dưới dạng this important pull request về luồng đã được hợp nhất gần đây.
  4. Nếu những điều trên không có tác dụng, hãy xây dựng Mono của riêng bạn bằng cách thêm this pull request. Nếu nó khắc phục được sự cố của bạn, vui lòng thêm "+1" vào yêu cầu kéo. Nó có thể là một sửa chữa cho bug 7055.
Các vấn đề liên quan