WCF: Intermittent connectivity issue with private MSMQ - WCF stops reading messages from queue
Issue:
WCF intermittently stops reading messages from the private local MSMQ, which will cause messages to get piled up.
It will be cleared as soon as we browse the service or send a GET request to metadata.
Reason:
Known issue with WCF and yet not fixed even in framework 4.6.2.
To repro:
Issue can be easily repro, if we recycle the app pool hosting WCF service on IIS (while it is happily reading the messages from the queue).
With this, the service host object associated with the old process id is cleared and even though IIS creates a new W3WP process, service host is not yet initialized. Because of this WCF wont pick any request from the queue, until we initialize the service host by either browsing the service or sending the GET request to the service WSDL/Metadata.
Steps:
1. Just enable the MSMQ feature on windows machine.
2. Add message to queue from client app
3. Host WCF service on IIS and it does read messages from queue, but unfortunately everything freeze once app pool gets recycle.
Client app code:
static void Main(string[] args)
{
try
{
for (int i = 0; i < 50; i++)
{
const string queueName = @".\private$\TestQueue1/Service1.svc";
MessageQueue msMq = null;
if (!MessageQueue.Exists(queueName))
msMq = MessageQueue.Create(queueName);
else
msMq = new MessageQueue(queueName);
//Person p = new Person(){FirstName = "ITFunda",LastName = ".Com"};
string msgText = String.Format("Sample Message Sent At {0}", DateTime.Now);
msMq.Send(msgText);
msMq.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
WCF Service configuration:
[ServiceContract]
public interface IService1
{
[OperationContract(IsOneWay = true, Action ="*")]
void ShowMessage(MsmqMessage<string> msg);
}
<service name="WcfService1.Service1" behaviorConfiguration="beh">
<endpoint address="msmq.formatname:DIRECT=OS:.\private$\TestQueue1/Service1.svc" binding="msmqIntegrationBinding"
bindingConfiguration="msmqInt" name="msmqInt" contract="WcfService1.IService1" />
<msmqIntegrationBinding>
<binding name="msmqInt" exactlyOnce="false">
<security mode="None"></security>
</binding>
</msmqIntegrationBinding>
Available workarounds:
- Disable the app pool default recycle time to 0. By default RegularTimeInterval(minutes) is set to 1740.
- This will avoid the un-necessary app pool recycle event and will avoid the BAD state where service host is closed and not activated.
- From IIS 7.5 onwards we can use the - App Initialization module (IIS Application Initialization)
<www.iis.net/downloads/microsoft/application-initialization>
https://www.iis.net/configreference/system.webserver/applicationinitialization
<www.iis.net/learn/get-started/whats-new-in-iis-8/iis-80-application-initialization>
Basically how this works is we set the appPool StartMode as AlwaysRunning and then specify the initialization page. I.E.:<system.webServer>
<applicationInitialization
doAppInitAfterRestart="true"
skipManagedModules="true"
remapManagedRequestsTo="filename.htm"/>
<add initializationPage="/default.aspx" hostName="myhost"/>
</applicationInitialization>
</system.webServer>
- Another easy way is to HOST WCF as Self host or via Windows Service and we get full control to initialize the Service Host object.
- Use the below reference to get the App.EXE created to auto trigger the GET request for respective machine, app pool and GET URL.
- The request will be triggered at interval of 1740 minutes. Thus we still get BEST of both worlds.
- In case we deal with Multiple app pool and do not want to set separate TASK for all of them, we can use below solution to use a Powershell script to auto trigger GET request for all the web sites hosted on IIS.
Sample and Module Location: https://1drv.ms/f/s!ArgnWb8iHXB6gpccIvMqLCWkdYgxdA
Hope this helps.
Thanks
Saurabh Somani