As promised in my previous post, I’m continuing my mission to inform you of new changes in WCF 4.5.
This is the ninth post in the WCF 4.5 series. This post continues the previous posts on web-hosting features, and this time it is about the improved streaming capabilities of WCF when it is hosted in IIS.
If you’ve ever tried creating a WCF service that uses streamed requests (for example a file upload service) and host it in IIS, you may have noticed a strange behavior in your WCF service – it would seem that WCF is late in receiving the request, as if it was entirely loaded into the memory, and then passed to WCF. So is it streamed? or is it actually buffered? well, it’s both.
When you host a WCF service in IIS you also get a bit of the ASP.NET pipeline on the side, even if you don’t use the ASP.NET compatibility mode, this is documented in the WCF Services and ASP.NET article on MSDN (look for the part about the PostAuthenticateRequest event). In .NET 4, there is a design flaw in ASP.NET which causes the requests sent to WCF to be buffered in ASP.NET. This buffering behavior causes several major side-effects:
1. There is a latency between the time the streamed message is received by ASP.NET and the time the WCF service method is actually invoked.
2. There is some memory consumption due to the buffering – the exact amount of memory consumed depends on the size of the message sent by the client, but it can even get to several hundred MBs if you increase the MaxRequestLength of ASP.NET, the MaxAllowedContentLength of IIS 7, and of course the MaxReceivedMessageSize and MaxBufferSize of WCF.
3. When ASP.NET buffers the request, it uses both memory and disk. The requestLengthDiskThreshold configuration setting of ASP.NET controls when ASP.NET starts to use the disk. If you upload multiple files to WCF at once, you will start to see some delays due to multiple files being written to the disk at once. BTW, the files are written to an “upload” folder under the web application’s temporary asp.net folder (under c:\windows\Microsoft.NET\Framework\vX.X.XXXX\Temporary ASP.NET Files\) and are removed after the request is handled.
To show this behavior, I have created a client application that uploads a 500MB file to a WCF service. The WCF service is hosted in IIS and is set to a streamed request (you can download the StreamingInIIS sample solution from here). The following output shows some information about the time it took for the service to receive and handle the request, and the consumed memory:
1 Client started upload on 17/01/2012 19:03:25
2 Available memory before starting is: 2701MB
3 Client finished upload on 17/01/2012 19:03:44
4 Available memory after finishing is: 2699MB
5 Available memory on ASP.NET is: 2701MB
6 ASP.NET received upload at: 17/01/2012 19:03:28
7 Available memory on WCF is: 2122MB
8 WCF started receiving file at: 17/01/2012 19:03:38
9 WCF finished receiving file at: 17/01/2012 19:03:43
File size is: 524288000
Press any key to continue . . .
Some things to note about these results:
1. Client started / Client finished (line 1+3) – the total time the client waited for the service was 19 seconds; this includes the upload time, the buffering time of ASP.NET, and the time WCF handled the received stream.
2. ASP.NET started receiving the stream 3 seconds after the client began sending it (line 6).
3. WCF started receiving the stream 10 seconds after ASP.NET received started receiving it, and a total of 13 seconds from the time the client started sending it (line 8). In total, it took WCF 5 seconds to read the entire stream from ASP.NET (line 8+9).
4. Before the client sent the message, the available memory in the machine was 2701MB, which is also the available memory when ASP.NET first received the message. By the time WCF got the request and started handling it, the available memory was 2122MB – about 580MB were consumed from the memory for this operation (lines 2, 5, and 7).
5. As for the generated temp file, here is a screenshot of the temporary ASP.NET folder content:
Note: to show the ASP.NET information I used the ASP.NET compatibility mode. You can turn it off in the sample code if you want to verify that the problem also exists when we don’t use the compatibility mode (look at the difference between the time the client sent the request and the time WCF actually started handling the request – there should be a big latency).
So we get that WCF 4 doesn’t handle well streamed content over IIS, but what about WCF 4.5? what has changed?
In WCF 4.5 this just doesn’t happen – with .NET 4.5, ASP.NET doesn’t buffer the request, but rather forwards it directly to WCF, so we don’t get any latency, no memory consumption, and no disk usage.
Want to see some proof? I ran the same demo code in Windows Server 8 with WCF 4.5 over IIS. I used a smaller file size (200MB), since this is a VM with less memory, however you can still see the difference quite clearly:
1 Client started upload on 11/27/2011 7:23:18 AM
2 Available memory before starting is: 942MB
3 Client finished upload on 11/27/2011 7:23:46 AM
4 Available memory after finishing is: 942MB
5 Available memory on ASP.NET is: 941MB
6 ASP.NET received upload at: 11/27/2011 7:23:20 AM
7 Available memory on WCF is: 942MB
8 WCF started receiving file at: 11/27/2011 7:23:20 AM
9 WCF finished receiving file at: 11/27/2011 7:23:46 AM
File size is: 209715200
Press any key to continue . . .
First thing to note – memory consumption hasn’t changed throughout the execution – remains steady at ~942MB (lines 2+4+5+7).
As for the latency – WCF received the request at the same time ASP.NET received it (lines 6+8), which is 2 seconds after the client begins sending it.
Oh, and since ASP.NET passed the stream directly to WCF, no temp file was created !!
So there you have it – proper streaming in WCF 4.5 over IIS.
Stay tuned for more posts about the new features of WCF 4.5. You can also follow me on Twitter (@IdoFlatow) to get updates as soon as new posts are published.