How is this possible? I thought one way calls were fire and forget. The method is marked as one-way. The callback concurrency mode is set to Multiple and the UseSychronizationContext of the callback class is set to false. The data being sent is not more than 1KB yet every time I send about 30-40 small messages concurrently, the calls start to block and eventually some of them timeout. I've benchmarked my client->server calls at about 16000/sec. When I try to call back to client, I can only muster about 2 per second, and this on a OneWay call!
My binding configuration for the server looks like so:
<system.serviceModel>
<bindings>
<netNamedPipeBinding>
<binding name="netNamedPipeBinding1" receiveTimeout="23:00:00" maxReceivedMessageSize="1048576" maxBufferPoolSize="1048576" maxConnections="500">
<readerQuotas maxStringContentLength="99999999" maxArrayLength="9999999" maxBytesPerRead="999999"/>
<security mode="None"/>
</binding>
</netNamedPipeBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="highThroughPut">
<serviceThrottling maxConcurrentCalls="3000" maxConcurrentInstances="3000" maxConcurrentSessions="3000"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="OLII.Apps.Services.Data.DataServices.DataService" behaviorConfiguration="highThroughPut">
<endpoint bindingConfiguration="netNamedPipeBinding1" address="net.pipe://localhost/DataListener" binding="netNamedPipeBinding" contract="OLLI.Apps.Services.ProxyClients.DataServerProxyClient.IDataListenerService"/>
</service>
</services>
</system.serviceModel>
My callback contract looks like so:
public interface IDataCallbackClient
{
[OperationContract(IsOneWay = true)]
void GetData(string file, int id);
}
My client callback class looks like so:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
public class DataCallback : IDataCallbackClient
{
public void GetData(string file, int id)
{
//If I put Thread.Sleep(5000); When the server calls this method, the first few go through, and subsequent calls block. If I do a return statement here, all the calls go through really fast on the server side.
//Does some processing with file and id. It then goes back to server with data.
}
}
I figured it out. I had calls to my service that were blocking and starving the thread pool in the process. I was also invoking calls on the thread pool from inside my wcf service, which is a bad practice since those methods are invoked on the thread pool themselves. Looks like when you make a one way call, and the thread pool is starved, the one way call will timeout since it doesn't have a thread to execute on.
Thanks
Related
I am building a system with 2 WCF Services. Both are IIS Hosted. At the moment they both reside in a single VS2010 website app, running on my local IIS7 (Windows 7) using the Derfault Website. I have enabled net.tcp on both.
Service1
accepts HTTP posts using webHttpBinding
wraps the data in a serializable composite object
sends the composite object to Service2 (we hope) using netMsmqBinding
Service2
receives said message and does something with it
Service 1 works as expected, however instead of placing the message on the configured Private Queue, our code is creating a new Queue under "Outgoing Queues" with the handle
DIRECT=TCP:127.0.0.1\private$\Service2/Service2.svc
note the forward slash
Of course Service2 never sees the message - this is the first time I have attempted this structure so I am not certain that Service2 misses the message because of its location, but based on what I have read it would seem so - I have not come across anything mentioning this Queue-creation behaviour.
Questions:
Am I doing this correctly (is there something wrong in the structure, web.config or code)?
When done properly in VS Debug, should Service1's
proxy.ProcessForm(formMessage);
hit breakpoints in my Service2 code, or is there another way to hande Service2 debug (ala windows services for example)?
Service1 Web.Config
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpFormBinding" crossDomainScriptAccessEnabled="true"/>
</webHttpBinding>
<netMsmqBinding>
<binding name="MsmqFormMessageBindingClient" exactlyOnce="false" useActiveDirectory="false" >
<security mode="None">
<message clientCredentialType="None"/>
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
</security>
</binding>
</netMsmqBinding>
</bindings>
<client>
<endpoint
name="HttpServiceWebEndpoint"
address=""
binding="webHttpBinding"
bindingConfiguration="webHttpFormBinding"
contract="Service1.HttpService.IHttpServiceWeb" />
<endpoint name="MsmqFormMessageBindingClient"
address="net.msmq://127.0.0.1/private/Service2/Service2.svc"
binding="netMsmqBinding"
contract="MyInfrastructure.IService2" />
</client>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
<!--
<serviceAuthenticationManager />
-->
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
On Receipt of an HTTP Post Service1 executes the following:
StreamReader sr = new StreamReader(formData);
string str = sr.ReadToEnd();
var t = HttpUtility.ParseQueryString(str);
Hashtable nvc = new Hashtable();
foreach (string n in t)
{
nvc.Add(n, (string)t[n]);
}
WcfFormMessage formMessage = new WcfFormMessage(nvc);
////create the Service binding
NetMsmqBinding msmq = new NetMsmqBinding("MsmqFormMessageBindingClient");
msmq.Security.Mode = (NetMsmqSecurityMode) MsmqAuthenticationMode.None;
EndpointAddress address = new EndpointAddress("net.msmq://127.0.0.1/private/Service2/Service2.svc");
ChannelFactory<IService2> factory = new ChannelFactory<IFormService>(msmq,address);
IService2 proxy = factory.CreateChannel();
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
proxy.ProcessForm(formMessage);
//do any 'sent to queue logging/updates here
}
I am ready to bet that your problem is related to 127.0.0.1 in your config. Type the machine name in there, even if it is local.
One of my WCF Services has an operation contract taking a large sized file as a parameter. So, when the client tries to send this over, I got an exception and when I looked at the server trace this is what I saw:
MESSAGE: The maximum message size quota for incoming messages (65536)
has been exceeded. To increase the quota, use the
MaxReceivedMessageSize property on the appropriate binding element.
I was using the default simplified configuration for my WCF services, so added a new service definition as follows:
<system.serviceModel>
<services>
<service name="MyNamespace.MyService">
<endpoint address="MyService.svc"
binding="basicHttpBinding" bindingConfiguration="basicHttp"
contract="MyNamespace.IMyService" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="basicHttp" allowCookies="true"
maxReceivedMessageSize="10485760"
maxBufferSize="10485760"
maxBufferPoolSize="10485760">
<readerQuotas maxDepth="32"
maxArrayLength="10485760"
maxStringContentLength="10485760"/>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
...
</behaviors>
<protocolMapping>
...
</protocolMapping>
The way I consume my services is, I have a function returning a channel in my helper class, and I use that channel to call the operations:
public static T CreateChannel<T>() where T : IBaseService
{
System.ServiceModel.BasicHttpBinding binding= new System.ServiceModel.BasicHttpBinding();
binding.TransferMode = TransferMode.Streamed;
binding.Security = new BasicHttpSecurity() { Mode = BasicHttpSecurityMode.None };
binding.MaxReceivedMessageSize = 10485760;
binding.MaxBufferSize = 10485760;
System.ServiceModel.ChannelFactory<T> cf2 = new ChannelFactory<T>(binding,
new System.ServiceModel.EndpointAddress(MyEndpointAddress)); //I checked this part, the address is correct.
T Channel= cf2.CreateChannel();
return Channel;
}
and then,
var businessObject = WcfHelper.CreateChannel<IMyService>();
var operationResult = await businessObject.MyOperationAsync(...);
Even though, my other services are running correctly, the one I defined in the configuration explicitly returns an exception of "There was no endpoint listening..." I am developing on VS2012, using IISExpress. What may be the problem, any suggestions?
I think there is a mismatch for transfert mode. In client-side, you are are using streamed transfert whereas in server-side it is not in the config. In addition, you have specified 10MB, which is not so high.
Please visit this for more info on streaming.
Edit :
If you are hosting under IIS, please also check (default is 4Mb) :
<system.web>
<httpRuntime maxRequestLength="4096 " />
</system.web>
I have two WCF services hosted with a hosting provider. Both service to work fine. I can access them from my own computer or even from a website hosted with another provider. The weird part (at least, the part I don't understand) is; one cannot call the other.
Both services are located in a subfolder of the web root, at the same hierarchical level. Like wwwroot\serviceone and wwwroot\servicetwo. Both are marked as application folder in IIS en both have an almost similar web.config as shown below, only the names differ:
<configuration>
<system.web>
<compilation targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="servone">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<services>
<service name="MyService.ServiceOne" behaviorConfiguration="servone">
<endpoint address="" binding="basicHttpBinding" contract=" MyService.IServiceOne "/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Browsing to the .svc displays the well-known service page with the example code;
class Test
{
static void Main()
{
ServiceOne client = new ServiceOne ();
// Use the 'client' variable to call operations on the service.
// Always close the client.
client.Close();
}
}
The client has a method named HandleRequest(string str). So in my code (C#) there's a line like;
client.HandleRequest("blah");
The call doesn't raise an exception (I can tell because they are catched, handled and written to a database). It's like the message is sent but never returns.
When I run this service (who calls the other) locally and leave the second on the remote server, all works well.
Obvious it is hard to provide all the details from the hosting party. Unfortunate I don't have access to an IIS installation to simulate the environment either. So, I'm not expecting an in-depth technical solution based on the little information I can provide. But any comment about how this setup differs from all others might be helpful.
I really appreciate any effort, thanks.
Edit:
The call is made like this:
public bool Send(String str)
{
bool result = false;
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress ep = new EndpointAddress("http://www.mydomain.com/ServiceTwo.svc");
client = new ServiceTwoClient(b, ep);
//
try
{
result = client.HandleRequest(str);
client.Close();
return result;
}
catch (Exception x)
{
Add2DbLog(x.Message);
return false;
}
}
The domain alias you're using may not work locally on the server. Log in to that server, launch a web browser, and navigate to the service URL used in your code (http://www.mydomain.com/ServiceTwo.svc). Ensure that you don't get any error messages.
I came across a page on MSDN explaining transaction in WCF Services here. I tweaked the binding settings and used netTcpBinding. Here is the serviceModel section of my app.config file:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpBindingConfiguration1" transactionFlow="true">
<security mode="Message" />
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="OrderingService.OrderService">
<clear />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"
listenUriMode="Explicit">
</endpoint>
<endpoint address="net.tcp://localhost:8880/OrderingService"
binding="netTcpBinding" bindingConfiguration="netTcpBindingConfiguration1"
contract="OrderingService.IOrderService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8888/OrderingService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
I created a windows application as the client of the service. I used netstat command to see the TCP connections between the client and the service (hosted in a console application). I realized for each operation (which was a button click in my client app that places a new order by invoking the methods of the service's proxy class), a new connection is created and all previous connections still remain ESTABLISHED. Obviously, this is not an ideal condition. I wondered what I did wrong and what setting or configuration would work out this problem by reducing the number of connections to only one. By the way, the service class that implements the service interface has InstanceContextMode set to PerSession. Here are the contract interface and the service class:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IOrderService
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.NotAllowed)]
List<Customer> GetCustomers();
[OperationContract]
[TransactionFlow(TransactionFlowOption.NotAllowed)]
List<Product> GetProducts();
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string PlaceOrder(Order order);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string AdjustInventory(int productId, int quantity);
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
string AdjustBalance(int customerId, decimal amount);
}
[ServiceBehavior(TransactionIsolationLevel = IsolationLevel.Serializable,
TransactionTimeout = "00:00:20",
InstanceContextMode = InstanceContextMode.PerSession,
TransactionAutoCompleteOnSessionClose = true)]
public class OrderService : IOrderService
{...}
Here is the code the uses the proxy class in the client app:
using (TransactionScope scope = new TransactionScope())
{
try
{
proxy = new OrderServiceClient("NetTcpBinding_IOrderService");
result = proxy.PlaceOrder(order);
MessageBox.Show(result);
result = proxy.AdjustInventory(product.ProductId, quantity);
MessageBox.Show(result);
result = proxy.AdjustBalance(customer.CustomerId, product.Price * quantity);
MessageBox.Show(result);
proxy.Close();
scope.Complete();
}
catch (Exception exc)
{
MessageBox.Show("Error occurred: " + exc.Message);
}
}
With regards to the TCP connection remaining ESTABLISHED - are you calling .Close() on your instance of the client when you are finished with it?
If you want to use a single connection you should change the instance context mode to 'Single' and reuse the connection you establish in the client to process all your service calls. This suits an architecture where you want to maintain state within your service.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service : IService
{
}
I found this link very helpful when I was learning about context modes in WCF: CodeProject link
As you are currently using PerSession context mode you should be able to limit it to a single connection by adding a setting for maxConcurrentSessions in your behaviors section. You can do it like this:
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="False" />
<serviceThrottling maxConcurrentSessions="1" />
</behavior>
</serviceBehaviors>
This would only be a good idea if you have a single client.
I am trying to create a custom ASP.NET Forms Authentication Service using WCF. I am calling it via a test page that contains only a single line of JS (except for the ScriptManager scripts). The problem is that the server returns response code 500 and the response body is empty. My breakpoints in the service method and in the Application_Error in Global.asax are not being hit.
Sys.Services.AuthenticationService.login('user', 'pass', false, null, null, null, null, null);
I can see the request go to the server in the browser tools with the following request body:
{"userName":"user","password":"pass","createPersistentCookie":false}
Other things on the request side also seem fine.
Here is the configuration service:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="BtxAuthenticationEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
</serviceBehaviors>
</behaviors>
<services>
<service name="MyNamespace.BtxAuthenticationService">
<endpoint contract="MyNamespace.IBtxAuthenticationService" binding="webHttpBinding" behaviorConfiguration="BtxAuthenticationEndpointBehavior"/>
</service>
</services>
</system.serviceModel>
And the declaration of the interface:
[ServiceContract]
public interface IBtxAuthenticationService
{
[OperationContract]
[WebInvoke]
bool Login(string username, string password, bool createPersistentCookie);
[OperationContract]
[WebInvoke]
void Logout();
}
The implementation:
public class BtxAuthenticationService : IBtxAuthenticationService
{
public bool Login(string username, string password, bool createPersistentCookie)
{
... irrelevant because this is never hit
}
public void Logout()
{
}
}
Can someone tell me how to configure this or point me to a way to debug it. An article about implementing custom Forms Authentication with a WCF service will be welcome too. I've tried experimenting with various other settings including all the exception details settings I could find but could not make any progress (though I was able to make some regress and get different exceptions like missing endpoints and so on).
Thank you for your time.
Not sure if this helps. I have never written such service but your configuration creates WCF service wich is not ASP.NET AJAX ready and works with XML instead of JSON. Try to use this instead of webHttp behavior:
<endpointBehaviors>
<behavior name="BtxAuthenticationEndpointBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>