2012-04-21 25 views
7

Tôi cần phải thực hiện một cơ chế điều chỉnh (yêu cầu mỗi giây) khi sử dụng HttpWebRequest để thực hiện các yêu cầu song song đối với một máy chủ ứng dụng. Ứng dụng C# của tôi phải phát hành không quá 80 yêu cầu mỗi giây đến một máy chủ từ xa. Giới hạn được áp đặt bởi các quản trị viên dịch vụ từ xa không phải là một giới hạn cứng nhưng là "SLA" giữa nền tảng của tôi và của họ.Làm thế nào để giới hạn số lượng HttpWebRequest mỗi giây đối với một máy chủ web?

Làm cách nào để kiểm soát số lượng yêu cầu mỗi giây khi sử dụng HttpWebRequest?

Trả lời

3

Tôi đã gặp vấn đề tương tự và không thể tìm thấy giải pháp đã sẵn sàng, do đó tôi đã tạo một giải pháp và tại đây. Ý tưởng là sử dụng BlockingCollection<T> để thêm các mục cần xử lý và sử dụng Tiện ích mở rộng phản ứng để đăng ký với bộ xử lý có giới hạn tốc độ.

lớp Throttle là phiên bản đổi tên của this rate limiter

public static class BlockingCollectionExtensions 
{ 
    // TODO: devise a way to avoid problems if collection gets too big (produced faster than consumed) 
    public static IObservable<T> AsRateLimitedObservable<T>(this BlockingCollection<T> sequence, int items, TimeSpan timePeriod, CancellationToken producerToken) 
    { 
     Subject<T> subject = new Subject<T>(); 

     // this is a dummyToken just so we can recreate the TokenSource 
     // which we will pass the proxy class so it can cancel the task 
     // on disposal 
     CancellationToken dummyToken = new CancellationToken(); 
     CancellationTokenSource tokenSource = CancellationTokenSource.CreateLinkedTokenSource(producerToken, dummyToken); 

     var consumingTask = new Task(() => 
     { 
      using (var throttle = new Throttle(items, timePeriod)) 
      { 
       while (!sequence.IsCompleted) 
       { 
        try 
        { 
         T item = sequence.Take(producerToken); 
         throttle.WaitToProceed(); 
         try 
         { 
          subject.OnNext(item); 
         } 
         catch (Exception ex) 
         { 
          subject.OnError(ex); 
         } 
        } 
        catch (OperationCanceledException) 
        { 
         break; 
        } 
       } 
       subject.OnCompleted(); 
      } 
     }, TaskCreationOptions.LongRunning); 

     return new TaskAwareObservable<T>(subject, consumingTask, tokenSource); 
    } 

    private class TaskAwareObservable<T> : IObservable<T>, IDisposable 
    { 
     private readonly Task task; 
     private readonly Subject<T> subject; 
     private readonly CancellationTokenSource taskCancellationTokenSource; 

     public TaskAwareObservable(Subject<T> subject, Task task, CancellationTokenSource tokenSource) 
     { 
      this.task = task; 
      this.subject = subject; 
      this.taskCancellationTokenSource = tokenSource; 
     } 

     public IDisposable Subscribe(IObserver<T> observer) 
     { 
      var disposable = subject.Subscribe(observer); 
      if (task.Status == TaskStatus.Created) 
       task.Start(); 
      return disposable; 
     } 

     public void Dispose() 
     { 
      // cancel consumption and wait task to finish 
      taskCancellationTokenSource.Cancel(); 
      task.Wait(); 

      // dispose tokenSource and task 
      taskCancellationTokenSource.Dispose(); 
      task.Dispose(); 

      // dispose subject 
      subject.Dispose(); 
     } 
    } 
} 

kiểm tra Đơn vị:

class BlockCollectionExtensionsTest 
{ 
    [Fact] 
    public void AsRateLimitedObservable() 
    { 
     const int maxItems = 1; // fix this to 1 to ease testing 
     TimeSpan during = TimeSpan.FromSeconds(1); 

     // populate collection 
     int[] items = new[] { 1, 2, 3, 4 }; 
     BlockingCollection<int> collection = new BlockingCollection<int>(); 
     foreach (var i in items) collection.Add(i); 
     collection.CompleteAdding(); 

     IObservable<int> observable = collection.AsRateLimitedObservable(maxItems, during, CancellationToken.None); 
     BlockingCollection<int> processedItems = new BlockingCollection<int>(); 
     ManualResetEvent completed = new ManualResetEvent(false); 
     DateTime last = DateTime.UtcNow; 
     observable 
      // this is so we'll receive exceptions 
      .ObserveOn(new SynchronizationContext()) 
      .Subscribe(item => 
       { 
        if (item == 1) 
         last = DateTime.UtcNow; 
        else 
        { 
         TimeSpan diff = (DateTime.UtcNow - last); 
         last = DateTime.UtcNow; 

         Assert.InRange(diff.TotalMilliseconds, 
          during.TotalMilliseconds - 30, 
          during.TotalMilliseconds + 30); 
        } 
        processedItems.Add(item); 
       }, 
       () => completed.Set() 
      ); 
     completed.WaitOne(); 
     Assert.Equal(items, processedItems, new CollectionEqualityComparer<int>()); 
    } 
} 
+0

có điều gì đó không ổn với URL –

-1

Bài đăng gốc của tôi đã thảo luận cách thêm cơ chế điều chỉnh cho WCF thông qua tiện ích mở rộng hành vi của ứng dụng khách, nhưng sau đó được chỉ ra rằng tôi đã đọc sai câu hỏi (doh!).

Nhìn chung, phương pháp tiếp cận có thể là để kiểm tra với một lớp xác định xem chúng tôi có vi phạm giới hạn tốc độ hay không. Đã có rất nhiều cuộc thảo luận về cách kiểm tra vi phạm về giá.

Throttling method calls to M requests in N seconds

Nếu bạn đang vi phạm các giới hạn tốc độ, sau đó ngủ một khoảng thời gian sửa chữa và kiểm tra lại. Nếu không, hãy tiếp tục và thực hiện cuộc gọi HttpWebRequest.

+0

Trong câu hỏi, tôi không đề cập đến một webservice WCF. Đó là về việc sử dụng lớp HttpWebRequest đơn giản. –

+0

Ah đã muộn rồi và tôi nên đọc câu hỏi kỹ hơn :) Bạn vẫn có thể thử phương pháp trước khi thực hiện cuộc gọi đến HttpWebRequest, kiểm tra với lớp khác để đảm bảo rằng bạn sẽ không vi phạm 80 yêu cầu/giây. Tôi sẽ cập nhật mã của mình ở trên. –

+0

Nó yêu cầu C# không phải Java. – SmallChess

0

Phương pháp mở rộng Throttle() và Sample() (On Observable) cho phép bạn điều chỉnh chuỗi sự kiện nhanh thành chuỗi "chậm".

Here is a blog post with an example của Sample(Timespan) đảm bảo tỷ lệ tối đa.

+0

Vấn đề với Sample() và Throttle() là chúng bỏ qua/vứt bỏ các mẫu để đạt được tốc độ quy định. – georgiosd

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