I have an application (.NET 4.0) that loads administrative data on startup. I have to make 25 concurrent async WCF calls, some of them are quick (40ms), some others take longer to execute, up to 882 ms. I plan to store the data locally, but for the very first application startup, it needs to be done quickly as possible.
It should be noted that the proxies are in a library targetting .NET 3.5 and are internally using BeginXxx and EndXxx methods pattern wrapped into an async method whith the help of Jeffrey Richter's Async Enumerator.
The WCF channel factories for each client proxy that will be used are opened before launching the calls.
At this time, I'm using Task.Factory.StartNew with an action that launch each async call. The experience is the following :
All BeginXXX calls are all sent.
The program seems to work outside of my code for at least 10 seconds.
Finally all the EndXXX calls are sent to retreive the results.
I'm wondering why such a delay occurs. My computer has 4 core, If the number of concurrent calls are limited to 4, there is no delay at all, as soon as I add another call, a delay is experienced.
Any help appreciated.
EDIT 1 : The binding used is netTcpBinding.
Server configuration is following :
<netTcpBinding>
<binding transactionFlow="true" listenBacklog="500" maxReceivedMessageSize="400000"
portSharingEnabled="false">
<readerQuotas maxDepth="200" />
<reliableSession enabled="false" />
<security mode="None">
<transport clientCredentialType="None" protectionLevel="None" />
<message clientCredentialType="None" />
</security>
</binding>
</netTcpBinding>
<service name="AdminService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration=""
contract="IAdmin">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
</service>
EDIT 2: Following are the default throttling values set by the WCF 4.5 runtime on a 4 Core machine.
ListenBacklog is [500]
MaxConnections is [48]
MaxConcurrentCalls is [64]
MaxConcurrentInstances is [2147483647]
MaxConcurrentSessions is [400]
EDIT 3 : Here is the code using J.Richter's AsyncEnumerator :
private IEnumerator<int> DoWorkGetXXXX(AsyncEnumerator<MyResult> ae)
{
ae.ThrowOnMissingDiscardGroup(true);
IClientChannel proxy = (IClientChannel)CreateChannel(_bindingName);
bool success = false;
try
{
proxy.Open();
// The call to BeginXXX goes here
((IAcaccount)proxy).BeginGetXXX(..., ae.EndVoid(0, DiscardGetXXX), proxy);
//
yield return 1;
if (ae.IsCanceled())
{
goto Complete;
}
// Iterator was not canceled, process should continue.
// The call to EndXXX goes here
IAsyncResult ar = ae.DequeueAsyncResult();
try
{
ae.Result = ((IAcaccount)ar.AsyncState).EndGetXXX(ar);
proxy.Close();
success = true;
}
// In the mean time, we catch and rethrow :)
// If this exception occurs, we should retry a call to the service
catch (FaultException<AppFabricCachingException> retry)
{
}
// fatal Exception in data service, administrator action required...
catch (FaultException<EFExecutionException> fatal)
{
}
catch (FaultException<EFUpdateException> fatal)
{
}
catch (FaultException<EFNoRowException> nr)
{
}
catch (FaultException fe)
{
}
catch (ServiceActivationException sae)
{
}
catch (CommunicationException ce)
{
}
//
}
finally
{
// If an error occurred, abort the proxy.
if (!success)
{
proxy.Abort();
}
}
// End of operations.
Complete:
proxy = null;
}
You could try to play with the value of serviceThrottling, as well as maxItemsInObjectGraph, maxBufferSize, MaxBufferPoolSize (i have them set to int.MaxValue).
Related
I am having really peculiar issue with certs not working after a while for WCF Client App that connect to SOAP 1.1 SAP service. What boggles me is the steps I have to take to make the certs work again. After I installed the certs on couple of load balanced servers, seems like everything works fine. But then after a few days, one of the app/servers throws this error.
The request was aborted: Could not create SSL/TLS secure channel.
When I log on to the server using MMC and open the cert (I do not even have to reinstall it, just open is good enough), the web app works. I am at my wits end as to why this might be happening. Any help will be appreciated.
Below are the architecture/some code samples of how the apps/web services are set up
[MVC WebApp]--[ Load Balancer ]-->(Server 1, Server 2)--> SAP SOAP 1.0 Web Service
Some configuration and code samples..
<system.serviceModel>
<client>
<endpoint address="https://somesapwebservice:8104/XISOAPAdapter/MessageServlet?senderParty=&senderService=BC_PORTAL&receiverParty=&receiverService=&interface=SI_AppFormData_Out_Sync&interfaceNamespace=urn%3Acominc.com%3AOTC%3AI1053%3AppForm" behaviorConfiguration="secureCert" binding="wsHttpBinding" contract="somecontract_Out_Sync" name="HTTPS_Port" />
</client>
<bindings>
<wsHttpBinding>
<binding>
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="secureCert">
<clientCredentials>
<clientCertificate storeName="My" storeLocation="CurrentUser" x509FindType="FindBySubjectDistinguishedName" findValue="CN=CNN, OU=Windows, OU=SAP, OU=Service Accounts, OU=Admin, OU=CORP, DC=myinc, DC=ds" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
..
<appSettings>
<add key="ProtocolExceptionMessage" value="The content type text/xml; charset=utf-8 of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8)" />
C# Code
public ActionResult FormSubmit(SubmitViewModel model)
try
{
this.SubmitToSAPService(model);
return this.RedirectToAction("Index", "Complete");
}
catch (ProtocolException pe)
{
// Current SAP only support SOAP 1.1 and WCF with .NET 4.6 runs on SOAP 1.2 - Catching the known exception
// Creating custom WCF binding to handle this is another possibility but that config could get convoluted
var messageSnippet = ConfigurationManager.AppSettings["ProtocolExceptionMessage"];
if (pe.Message.Contains(messageSnippet))
{
return this.RedirectToAction("Index", "Complete");
}
throw pe;
}
One thing I am doing little off here is that I was told is that SAP is currently running SAOP 1.1 and .NET we are running is SOAP 1.2. So I was always getting Protocol Exception. To get around that I check for the text and if the exception message matches exactly as expected, I bypass it.
public string SubmitToSAPService(SubmitViewModel model)
{
var dtFormDataRecords = new DT_FormData();
dtFormDataRecords.Records = new DT_FormDataRecords();
dtFormDataRecords.Records.Name = model.name
....
var client = new SI_AppFormData_Out_SyncClient();
try
{
client.SI_AppFormData_Out_Sync(dtFormDataRecords);
}
finally
{
client.Close();
}
...
After trying to run intellitrace on the app, that let to clue me in the app-pool settings Load User Profile was set to False. That had cause the issue I was getting. After I set the it to True, my issue was resolved.
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>
After reading more about it and trying to implement wshttpbinding, it just won't happen. No matter what I try, I keep getting the below error message (with security mode commented out). I understand why because of the different SOAP versions between bindings.
"(415) Cannot process the message because the content type 'application/soap+xml; charset=utf-8' was not the expected type 'text/xml; charset=utf-8'"
I read more about the TransportWithMessageCredentials at the following link:
http://msdn.microsoft.com/en-us/library/ms789011.aspx
but still could not get it to work.
I can use basicHttpBinding just fine for internal apps and works great (if I don't include any transactions), but my application in the WCF layer still needs to support transactions (see below), from which I understand that basicHttpBinding doesn't support, because it doesn't contain the transactionflow attribute.
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required))
When I try and run the below with the security mode included, the svc config editor doesn't even start up and throws the following error: "System.InvalidOperationException: Could not find a base address that matches scheme https for the endpoint with binding WSHttpBinding. Registered base address schemes are [http]."
I know it's expecting some kind of SSL/https security, but my website (as you can see below is http) . That would be fine for the public facing websites, but for internal sites, for now, all I want to do is have support for transactions.
Here is my server side setup for wsHttpBinding:
<bindings>
<wsHttpBinding>
<binding name="WsHttpBinding_IYeagerTechWcfService" closeTimeout="00:02:00" openTimeout="00:02:00" receiveTimeout="24.20:31:23.6470000" sendTimeout="00:10:00" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" transactionFlow="true">
<security mode="Transport" >
<transport clientCredentialType = "Windows" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<?xml version="1.0"?>
<services>
<clear />
<service name="YeagerTechWcfService.YeagerTechWcfService">
<endpoint address="" binding="wsHttpBinding" contract="YeagerTechWcfService.IYeagerTechWcfService" >
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://abc.com/yeagerte/YeagerTechWcfService.YeagerTechWcfService.svc" />
</baseAddresses>
</host>
</service>
</services>
Here is my client side setup:
<?xml version="1.0"?>
<client>
<endpoint address="http://abc.com/yeagerte/YeagerTechWcfService.YeagerTechWcfService.svc"
binding="wsHttpBinding" contract="YeagerTechWcfService.IYeagerTechWcfService"
name="WsHttpBinding_IYeagerTechWcfService" />
</client>
Could somebody please provide the following:
Is there another way to support transactions in WCF for basicHttpBinding or any other way for that matter?
If so, how do I implement it?
If not, what are my options?
For the above question, I may have figured out an answer but want to run it by somebody more experienced in this matter.
Instead of having the WCF layer handle the transactions (like mentioned above), I propose I use basicHttpBinding and the following code in my Controller when it passes the data to the WCF layer:
// method here
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required))
{
db.EditCategory(cat);
// the above code would execute the EditCategory method below in the WCF layer and keep the transaction alive ts.Complete();
ts.Dispose();
}
return Json(new GridModel(db.GetCategories()));
// end method
WCF layer:
public void EditCategory(Category cat)
{
try
{
using (YeagerTechEntities DbContext = new YeagerTechEntities())
{
Category category = new Category();
category.CategoryID = cat.CategoryID;
category.Description = cat.Description;
// do another db update here or in another method...
DbContext.Entry(category).State = EntityState.Modified;
DbContext.SaveChanges();
}
}
catch (Exception ex)
{
throw ex;
}
}
For public facing websites using SSL, how do I properly implement wsHttpBinding?
I encountered the same problem when using WCF Transactions
I used Message security with Windows authentication and did not have to setup any certificates.
<wsHttpBinding>
<binding name="WSHttpBinding_Transactional"
transactionFlow="true">
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" />
</security>
</binding>
</wsHttpBinding>
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
I wrote some code for a WCF P2P chat program.
<services>
<service name="PeerChat.Form1">
<host>
<baseAddresses>
<add baseAddress="net.p2p://PeerChat/" />
</baseAddresses>
</host>
<endpoint name="PeerChatEndPoint" address="" binding="netPeerTcpBinding" bindingConfiguration="BindingUnsecure"
contract="PeerChat.IChatService" />
</service>
</services>
<bindings>
<netPeerTcpBinding>
<binding name="BindingUnsecure">
<resolver mode="Pnrp" />
<security mode="None" />
</binding>
</netPeerTcpBinding>
</bindings>
<client>
<endpoint
name="PeerChatClientEndPoint"
address="net.p2p://PeerChat/"
binding="netPeerTcpBinding"
bindingConfiguration="BindingUnsecure"
contract="PeerChat.IChatService"
/>
</client>
I then host the service as follows:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public partial class Form1 : Form, IChatService
{
IChatService channel;
ServiceHost host = null;
ChannelFactory<IChatService> channelFactory = null;
private void StartService()
{
//Instantiate new ServiceHost
host = new ServiceHost(this);
//Open ServiceHost
host.Open();
//Create a ChannelFactory and load the configuration setting
channelFactory = new ChannelFactory<IChatService>("PeerChatClientEndPoint");
channel = channelFactory.CreateChannel();
//Lets others know that someone new has joined
channel.SendMessage("Hello."+ Environment.NewLine);
foreach (var cloud in Cloud.GetAvailableClouds())
{
textBox2.Text += cloud.Name + Environment.NewLine;
}
}
private void StopService()
{
if (host != null)
{
channel.SendMessage("Bye." + Environment.NewLine);
if (host.State != CommunicationState.Closed)
{
channelFactory.Close();
host.Close();
}
}
}
The problem is I can send a message to the same instance of the program but not to another instance. Ie an instance only receives its own messages not messages from other instances. Not sure if it is a matter of configuring PNRP correctly? I tested on Windows 7.
You wouldn't happen to have both instances of the program listening to the same end point would you? I am not certain, but I suspect what may be happening is that your client application is registering itself on the endpoint first, then intercepting all the messages that come to that endpoint before the second one can get them. What I'd suggest trying to do is configuring the second instance to start up on an endpoint with a diferent Uri. So say one connects on net.p2p://PeerChatA/ and the other net.p2p://PeerChatB/ .