locked
Streamed TransferMode when using a ServiceRouter RRS feed

  • Question

  • I am currently using a ServiceRouter to route all my services through a single machine before being passed out to the final destination.
    One of my destination endpoints is a streamed HTTP endpoint, and no matter what I do, I cannot successfully call the streamed method through the service-router.
    I get an unknown fault with the 'enable inclusion of the Fault message in the response by adding <serviceDebug/> bla bla bla' - nothing I do actually enables the return of the error messages.
    I have enabled tracing too and the resulting trace file is always truncated when the error occurs meaning that nothing useful gets logged to it.

    My setup is this:
         client --> (net.tcp) --> servicerouter --> (basicHttpBinding) --> destination services

    Note: I can bypass the servicerouter and the streamed service works fine.

    I have tried the following things:
    1. make all endpoints basicHttpBinding                              FAIL
    2. make the servicerouter TransferMode = Streamed         FAIL
    3. make the servicerouter TransferMode = Buffered           FAIL

    Can anyone explain how I can 'route' a streamed endpoint?
    Monday, February 22, 2010 7:43 PM

Answers

  • As I re-read your original question are you concerned about streaming through the Routing Service, or concerned about getting exception details back to the client (or both)?  There are separate things that could be tripping you up :) 

    1) Set transferMode = Streamed on all bindings (client, service, router).  This isn't technically necessary to send a Stream of data (you could buffer the stream), but from your contract it looks like you're going to be sending large things, so it's probably what you want.  Remember that there's a lot of considerations when dealing with streaming large content (here's the link on MSDN)

        <bindings>
          <basicHttpBinding>
            <binding name="streamedHttp" transferMode="Streamed" />
          </basicHttpBinding>
          <netTcpBinding>
            <binding name="streamedTcp" transferMode="Streamed" />
          </netTcpBinding>
        </bindings>
    2) Make sure you are properly handling the stream on the client/service: When I was building a sample to try to reproduce your error, I had to remember a bunch of stuff about streams :)  Calling flush(), resetting the position to 0 before invoking the Service's operation at the client, etc.  Until I did this I was seeing no data at my remote service (although the call was succeeding).

    3) If your messages still can't get through the Routing Service, simplify your filter table config as much as possible.  Just having a single MatchAll filter pointed right at the backend service will help.

    4) Onto the error handling -- If you want to make sure that exceptions thrown by your remote service get returned to your client, you seem to already have the right config, which is to set IncludeExceptionDetailInFaults = true.  Exceptions generated remotely and thrown on the wire as Fault Messages are treated as responses by the Routing Service, so if the remote service throws something it should be returned to the client. 

    5) If you would like exceptions thrown by the RoutingService to be returned (with details) to the client, you should flip the same switch at the Routing Service (by adding the service debug behavior either through code or config).  This will let you see exceptions that the Routing Service runs into when trying to process your messages (CommunicationExceptions when trying to send out the other side, some of the internal router errros, etc).
    <serviceDebug includeExceptionDetailInFaults="True" />
    6) If you're still not getting messages through or getting details at the client, or are but don't know what's going on, turn on tracing at the Routing Service.
      <system.diagnostics>
        <sources>
          <source name="System.ServiceModel.Routing" switchValue="Warning">
            <listeners>
              <add type="System.Diagnostics.DefaultTraceListener" name="Default" />
              <add name="RouterTraceListener" />
            </listeners>
          </source>
        </sources>
        <sharedListeners>
          <add initializeData="RoutingService_tracelog.svclog"
      type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
      name="RouterTraceListener" traceOutputOptions="Timestamp, ThreadId, Callstack" />
        </sharedListeners>
        <trace autoflush="true" />
      </system.diagnostics>

    And try it again :)  Then look inside the svclog. If you have trouble and none of this gives you enough information, paste in whatever is red from the svctracelog, your client's stream handling code and your router config and we'll take it from there.

    -Matt
    Tuesday, February 23, 2010 8:01 PM

All replies

  • When you say serviceRouter I assume you mean the .NET 4 Routing Service (System.ServiceModel.Routing.RoutingService). 
    Are you self-hosting or hosting in IIS?

    The Routing Service has to buffer messages that go through it internally in the following situations:

                    a) if your filters need to inspect the message body (routeOnHeadersOnly=false)

                    b) if you are set up to convert from one protocol to another (we have to copy the message into a new one with the right message version)

                    c) multicasting (we need to create additional copies of the message)

                    d) some error handling patterns where we need to make sure we have a copy of the message before passing it along

    However, in these situations you should still be able to call your streaming operations, there will just be a bit more work done at the RoutingService to create the necessary copies of the message.

    What does your operation/contract on the remote service look like?

    Monday, February 22, 2010 8:38 PM
  • Hello again Matt, yeah sorry, it is the RoutingService again.

    I am doing simple header inspection to determine where to route the messages - no need to look at the body.
    While I do intend to do protocol conversion, I have tested without it, and it still fails.
    No multicasting.

    The contract looks like this:

     

     

    /// <summary>
    /// Used to upload and download files.
    /// </summary>
    [ServiceContract(Namespace = "http://www.tempuri.org/product/1.0/")]
    public interface IFileTransferService
    {
      [
    OperationContract]
      Stream Download(int id);

      [
    OperationContract]
      int Upload(Stream stream);
    }

    And I have this attribute on my service implementation:

    [

     

    ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults=true)]

    Thanks

    Adam

    Monday, February 22, 2010 10:44 PM
  • As I re-read your original question are you concerned about streaming through the Routing Service, or concerned about getting exception details back to the client (or both)?  There are separate things that could be tripping you up :) 

    1) Set transferMode = Streamed on all bindings (client, service, router).  This isn't technically necessary to send a Stream of data (you could buffer the stream), but from your contract it looks like you're going to be sending large things, so it's probably what you want.  Remember that there's a lot of considerations when dealing with streaming large content (here's the link on MSDN)

        <bindings>
          <basicHttpBinding>
            <binding name="streamedHttp" transferMode="Streamed" />
          </basicHttpBinding>
          <netTcpBinding>
            <binding name="streamedTcp" transferMode="Streamed" />
          </netTcpBinding>
        </bindings>
    2) Make sure you are properly handling the stream on the client/service: When I was building a sample to try to reproduce your error, I had to remember a bunch of stuff about streams :)  Calling flush(), resetting the position to 0 before invoking the Service's operation at the client, etc.  Until I did this I was seeing no data at my remote service (although the call was succeeding).

    3) If your messages still can't get through the Routing Service, simplify your filter table config as much as possible.  Just having a single MatchAll filter pointed right at the backend service will help.

    4) Onto the error handling -- If you want to make sure that exceptions thrown by your remote service get returned to your client, you seem to already have the right config, which is to set IncludeExceptionDetailInFaults = true.  Exceptions generated remotely and thrown on the wire as Fault Messages are treated as responses by the Routing Service, so if the remote service throws something it should be returned to the client. 

    5) If you would like exceptions thrown by the RoutingService to be returned (with details) to the client, you should flip the same switch at the Routing Service (by adding the service debug behavior either through code or config).  This will let you see exceptions that the Routing Service runs into when trying to process your messages (CommunicationExceptions when trying to send out the other side, some of the internal router errros, etc).
    <serviceDebug includeExceptionDetailInFaults="True" />
    6) If you're still not getting messages through or getting details at the client, or are but don't know what's going on, turn on tracing at the Routing Service.
      <system.diagnostics>
        <sources>
          <source name="System.ServiceModel.Routing" switchValue="Warning">
            <listeners>
              <add type="System.Diagnostics.DefaultTraceListener" name="Default" />
              <add name="RouterTraceListener" />
            </listeners>
          </source>
        </sources>
        <sharedListeners>
          <add initializeData="RoutingService_tracelog.svclog"
      type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
      name="RouterTraceListener" traceOutputOptions="Timestamp, ThreadId, Callstack" />
        </sharedListeners>
        <trace autoflush="true" />
      </system.diagnostics>

    And try it again :)  Then look inside the svclog. If you have trouble and none of this gives you enough information, paste in whatever is red from the svctracelog, your client's stream handling code and your router config and we'll take it from there.

    -Matt
    Tuesday, February 23, 2010 8:01 PM
  • Thank you for this very descriptive explanation Matt.
    I discovered the problem, my router actually applies a new route in response to events, and is supposed to append the route, but I was creating a new RoutingDescription, and 'Applying' that each time, which I discovered essentially overwrites all previous routes.

    Interesting that the RoutingExtension only has an 'ApplyConfiguration' method, so one can only set the config, but cannot read it back. I guess it makes sense that this overwrites everything otherwise there would be no way to remove routes...

    Your mentioning the 'match-all' filter lead me to this thought process.

    I discovered this by putting a breakpoint within my custom MessageFilter, and noticed that during a routing operation, only 1 MessageFilter was being queried - leading me to believe that there was indeed only ever one filter in the routing table - despite my multiple routes being applied.

    However, I still cannot get the faults to come through - which Im sure will probably bite me at the next issue I have (c;

    Thanks Matt.
    Sunday, February 28, 2010 8:43 PM