2009-01-16 33 views
10

Phương pháp sau (trong đó tôi hy vọng không phạm sai lầm trong khi đơn giản hóa đáng kể cho bài đăng này) đang hoạt động đúng và thiết lập bằng chế độ Truyền trực tiếp qua giao thức net.tcp. Vấn đề là hiệu suất là tồi tệ hơn đáng kể so với tải xuống cùng một tệp thông qua IIS trên http. Tại sao điều này lại là, và tôi có thể thay đổi những gì để cải thiện hiệu suất?Tải xuống tệp qua WCF chậm hơn thông qua IIS

Stream WebSiteStreamedServiceContract.DownloadFile(string filePath) { 
    return File.OpenRead(filePath); 
} 

Cuối cùng, WCF có chịu trách nhiệm xử lý đúng luồng của tôi không và thực hiện tốt công việc này? Nếu không, tôi phải làm gì?

Cảm ơn bạn.

+0

Hi Greg! Tôi đang đấu tranh để thiết lập một kênh được truyền qua netTcp. Theo câu hỏi của bạn, có vẻ như bạn đã thành công trong việc làm như vậy. Bạn có thể chia sẻ thông tin về cấu hình máy chủ và máy khách không? Cảm ơn nhiều trước !! – Nayan

+0

@Nayan Tôi nghĩ bạn nên tạo một câu hỏi mới và chỉ cho tôi. Câu trả lời có lẽ sẽ kết thúc quá lớn để nhận xét. –

Trả lời

31

Sau 8 tháng làm việc về vấn đề này, 3 trong số đó với Microsoft, đây là giải pháp. Câu trả lời ngắn gọn là phía máy chủ (phía gửi tệp lớn) cần thiết để sử dụng như sau cho một ràng buộc:

<customBinding> 
    <binding name="custom_tcp"> 
    <binaryMessageEncoding /> 
    <tcpTransport connectionBufferSize="256192" maxOutputDelay="00:00:30" transferMode="Streamed"> 
    </tcpTransport> 
    </binding> 
</customBinding> 

Chìa khóa ở đây là thuộc tính connectionBufferSize. Một số thuộc tính khác có thể cần phải được thiết lập (maxReceivedMessageSize, vv), nhưng connectionBufferSize là thủ phạm.

Không có mã nào phải được thay đổi ở phía máy chủ.

Không có mã nào phải được thay đổi ở phía máy khách.

Không có cấu hình nào phải được thay đổi ở phía máy khách.

Dưới đây là câu trả lời dài:

Tôi nghi ngờ tất cả cùng rằng lý do net.tcp trên WCF còn chậm là vì nó được gửi khối nhỏ thông tin rất thường xuyên chứ không phải khối lớn thông tin ít thường xuyên hơn, và điều đó điều này làm cho nó hoạt động kém trên các mạng có độ trễ cao (internet). Điều này hóa ra là đúng, nhưng đó là một con đường dài để đến đó.

Có một số thuộc tính trên netTcpBinding rằng âm thanh hứa hẹn: maxBufferSize là rõ ràng nhất, và maxBytesPerRead và những người khác nghe có vẻ hy vọng là tốt. Ngoài ra, có thể tạo ra các luồng phức tạp hơn luồng trong câu hỏi ban đầu - bạn cũng có thể chỉ định kích cỡ bộ đệm ở đó - trên cả máy khách lẫn phía máy chủ. Vấn đề là không ai trong số này có bất kỳ tác động nào. Một khi bạn sử dụng một netTcpBinding, bạn đang hosed.

Lý do là điều chỉnh maxBufferSize trên netTcpBinding điều chỉnh bộ đệm trên lớp giao thức. Nhưng không có gì bạn có thể làm với một netTcpBinding sẽ bao giờ điều chỉnh lớp vận chuyển cơ bản. Đây là lý do tại sao chúng tôi thất bại quá lâu để tiến tới.

Ràng buộc tùy chỉnh giải quyết vấn đề vì tăng connectionBufferSize trên lớp vận chuyển làm tăng lượng thông tin được gửi cùng một lúc và do đó quá trình truyền ít nhạy cảm hơn với độ trễ.

Khi giải quyết vấn đề này, tôi đã nhận thấy rằng maxBufferSize và maxBytesPerRead đã có tác động hiệu suất trên các mạng có độ trễ thấp (và cục bộ). Microsoft nói với tôi rằng maxBufferSize và connectionBufferSize là độc lập và tất cả các kết hợp của các giá trị của chúng (bằng nhau, maxBufferSize lớn hơn connectionBufferSize, maxBufferSize nhỏ hơn connectionBufferSize) là hợp lệ. Chúng tôi đang thành công với một maxBufferSize và maxBytesPerRead của 65536 byte. Một lần nữa, mặc dù, điều này có rất ít tác động đến hiệu suất mạng có độ trễ cao (vấn đề ban đầu).

Nếu bạn đang tự hỏi maxOutputDelay là gì, đó là lượng thời gian được phân bổ để lấp đầy bộ đệm kết nối trước khi khung công tác ném ngoại lệ IO. Bởi vì chúng tôi đã tăng kích thước bộ đệm, chúng tôi cũng tăng lượng thời gian được phân bổ để lấp đầy bộ đệm.

Với giải pháp này, hiệu suất của chúng tôi tăng khoảng 400% và hiện giờ tốt hơn IIS một chút. Có một số yếu tố khác ảnh hưởng đến hiệu suất tương đối và tuyệt đối trên IIS qua HTTP và WCF trên net.tcp (và WCF trên http, cho rằng vấn đề), nhưng đây là kinh nghiệm của chúng tôi.

+0

Greg, bạn nói Không cấu hình phải được thay đổi ở phía khách hàng - làm thế nào? Tôi hiện đang sử dụng basicHttpBinding trên cả máy chủ và máy khách, và Mtom, yêu cầu cấu hình trên cả máy khách và dịch vụ, do đó tôi không thấy cách khách hàng có thể nhận ra một nút tùy chỉnh mới một cách kỳ diệu? – joedotnot

+0

@joe, trước đây chúng tôi đã sử dụng NetTcpBinding (chúng tôi tạo một ràng buộc và sau đó chuyển nó vào ChannelFactory - chúng tôi không sử dụng cách tạo proxy tự động) và chúng tôi đang tiếp tục sử dụng NetTcpBinding. Tôi khá chắc chắn không có gì thay đổi ở tất cả, nhưng tôi sẽ ngừng ngắn hứa hẹn và nói rằng điểm thực của tôi là gì, đó là không có giá trị paramter (MaxBytesPerRead, vv) ở phía khách hàng là nguyên nhân của vấn đề ban đầu. –

3

Tôi không biết câu trả lời cho câu hỏi đầu tiên của bạn (tôi nghĩ bạn cần cung cấp thêm mã để hiển thị những gì bạn đang làm cho cả hai bài kiểm tra), nhưng câu hỏi thứ hai liên quan đến việc xử lý luồng, câu trả lời là bạn cần tự làm.

Here is an excellent blog entry trong đó có một số mã tuyệt vời chỉ cho mục đích này.

1

Tôi đã nhận lời khuyên của Greg-Smalter và thay đổi ConnectionBufferSize trên NetTcpBinding bằng cách sử dụng sự phản chiếu và giải quyết được sự cố của tôi. Streaming tài liệu lớn đang la hét nhanh chóng ngay bây giờ. Đây là mã.

 var transport = binding.GetType().GetField("transport", BindingFlags.NonPublic | BindingFlags.Instance) 
      ?.GetValue(binding); 

     transport?.GetType().GetProperty("ConnectionBufferSize", BindingFlags.Public | BindingFlags.Instance)?.SetValue(transport, 256192); 
0

Đây là một cách khác để thực hiện điều đó mà không phải đối phó với Phản ánh. Chỉ cần quấn NetTcpBinding vào CustomBinding.

var binding = new CustomBinding(new NetTcpBinding 
{ 
    MaxReceivedMessageSize = 2147483647, 
    MaxBufferSize = 2147483647, 
    MaxBufferPoolSize = 2147483647, 
    ReceiveTimeout = new TimeSpan(4, 1, 0), 
    OpenTimeout = new TimeSpan(4, 1, 0), 
    SendTimeout = new TimeSpan(4, 1, 0), 
    CloseTimeout = new TimeSpan(4, 1, 0), 
    ReaderQuotas = XmlDictionaryReaderQuotas.Max, 
    Security = 
      { 
       Mode = SecurityMode.None, 
       Transport = {ClientCredentialType = TcpClientCredentialType.None} 
      }, 
      TransferMode = TransferMode.Streamed, 
      HostNameComparisonMode = HostNameComparisonMode.StrongWildcard 
     }); 

binding.Elements.Find<TcpTransportBindingElement>().ConnectionBufferSize = 665600; 
Các vấn đề liên quan