I have a windows service that runs four timers for a monitoring application. The timer in question opens a web request, polls a rest web service, and saves the results in a database.
Please see the elapsed method below:
void iSMSPollTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
iSMSPollTimer.Stop();
try
{
Logger.Log("iSMSPollTimer elapsed - polling iSMS modem for new messages");
string url = "http://...:../recvmsg?user=" + iSMSUser + "&passwd=" + iSMSPassword;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream resStream = response.GetResponseStream();
XmlSerializer serializer = new XmlSerializer(typeof(Response));
using (XmlReader reader = XmlReader.Create(resStream))
{
Response responseXml = (Response)serializer.Deserialize(reader);
if (responseXml.MessageNotification != null)
{
foreach (var messageWrapper in responseXml.MessageNotification)
{
DataContext dc = new DataContext();
DateTime monitorTimestamp = DateTime.Now;
if (messageWrapper.Message.ToUpper().EndsWith("..."))
{
//Saved to DB
}
else if (messageWrapper.Message.ToUpper().EndsWith("..."))
{
//Saved to DB
}
dc.SubmitChanges();
}
}
else
{
Logger.Log("No messages waiting in the iSMS Modem");
}
}
Logger.Log("iSMSPollTimer processing completed");
}
catch (Exception ex)
{
Logger.Log(GetExceptionLogMessage("iSMSPollTimer_Elapsed", ex));
Logger.Debug(GetExceptionLogMessage("iSMSPollTimer_Elapsed", ex));
}
finally
{
iSMSPollTimer.Start();
}
}
When I look at the log messages, I do get "iSMSPollTimer processing completed" and randomly afterwards the timer does not restart.
Any thoughts?
I'm thinking there's a potential reentrancy problem here, but I can't put my finger on it exactly.
I would suggest that, rather than calling Timer.Stop and then Timer.Start, set the timer's AutoReset property to false when you create it. That will prevent any reentrancy problems because the timer is automatically stopped the first time the interval elapses.
Your handler code remains the same except that you remove the code that calls iSMSPollTimer.Stop.
I'm not saying that this will solve your problem for sure, but it will remove the lingering doubt about a reentrancy problem.
This is a pretty well known issue with using timers in .NET service. People will tell you to use a different type of timer (Threading vs System), but in the end they will also fail you. How long before they stop triggering? The shorter your interval, the faster it will fail. If you set it to 1 second, you'll see it happen every couple hours.
The only workaround that I found working for me, is not depending on timers altogether and use a while loop with a Sleep function inside.
Related
I created window service that use SSH.net to read file from sFTP server every 600 seconds
in OnStart I create timer
timer = new Timer(TimerCallback, null, 0, timeDelay);
and TimerCallBack is function that use SSH.net. In connection before read file I have code to connect to sFTP server like below
var FILE_READ_FLAG = false;
try
{
req = new SftpClient(ftpHost, ftpLogin, ftpLoginPassword);
req.Connect();
if (req.IsConnected)
{
directories = req.ListDirectory(ftpPath);
FILE_READ_FLAG = true;
}
}
catch (Exception e)
{
EventLog.WriteEntry(sSource,"ERROR : " + GetFullMessageFromException(e),
EventLogEntryType.Information, 234);
FILE_READ_FLAG = false;
}
if (FILE_READ_FLAG){
// read file and insert to database
}
the problem is when service run and exception occur the service can run again every 600 seconds but with the error
Session operation has timed out
at Renci.SshNet.Session.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
at Renci.SshNet.Session.WaitOnHandle(WaitHandle waitHandle)
at Renci.SshNet.Session.Connect()
at Renci.SshNet.BaseClient.Connect()
the service cannot run again and seem to stuck when I try to stop and restart service, Windows show alert message cannot stop in timely fashion and I have to kill service by cmd and start it manually
My question is why another exception can handle with try/catch block but with this error the service not run again with timer what I missing or using it wrong? thank you
You've created an object SftpClient. Looking at the code it does not get disposed properly and also you don't call disconnect. It's possible, if there is an error the system will hang as there is an open connection.
However you set the client as a variable (i.e req = new SftpClient(ftpHost, ftpLogin, ftpLoginPassword), so its possible the disconnect/ disposed is called elsewhere. Although I'd still recommend re-structuring the code so that you get the data from the SFTP Client and them immediately disconnect/ dispose. It's good practice to minimise the amount of time the connection is open.
Consider the following:
List<SftpFile> files = null;
using (SftpClient sftp = new SftpClient(host, username, password)) // using will dispose SftpClient
{
try
{
sftp.Connect();
files = sftp.ListDirectory(ftpPath).ToList();
sftp.Disconnect();
}
catch (Exception e)
{
EventLog.WriteEntry(sSource,"ERROR : " + GetFullMessageFromException(e),
EventLogEntryType.Information, 234);
}
}
if(files.any())
{
// Process the files.
}
I'm using SyslogNet.Client to syslog (UDP) messages to a server. I loop through a collection of errors from a database and send them. Only ~40% of messages arrive. I understand that with UDP, there is no guarantee of message arrival, but this is a very high percentage. However, if I call Thread.Sleep(1) between each iteration of the loop, 100% of the messages arrive. I'm having trouble understanding why this is happening.
Here's the loop:
private static void SyslogErrors(Dictionary<int, string> errorCol)
{
foreach (var error in errorCol)
{
SyslogMessage("AppName", error.Value);
Thread.Sleep(1);
}
}
And here's SyslogMessage:
private static void SyslogMessage(string appName, string message)
{
using (_syslogSender = new SyslogUdpSender(Server, Port))
{
var msg = new SyslogMessage(DateTime.Now,
Facility.SecurityOrAuthorizationMessages1,
Severity.Informational,
Environment.MachineName,
appName,
message);
_syslogSender.Send(msg, new SyslogRfc3164MessageSerializer());
}
}
First of all, why is this happening? Secondly, what's the "best practices" way to slow the loop down? Thread.Sleep(1) doesn't seem like a very clean solution.
Thanks!
It looks like the OS rejects some logs when spammed too much, and the sleep will most likely do just fine. But there are indeed some more reliable solutions. You can setup a Timer object and call your logger using the Elapsed event.
Something like:
//likely much longer than needed, I figure you dont need this to breeze anyway
aTimer = new System.Timers.Timer(50);
aTimer.Elapsed += timeToLog;
aTimer.Enabled = true;
Then you log in time:
private static void timeToLog(Object source, ElapsedEventArgs e)
{
var error = getNextError(); //Keep an iterator somewhere
SyslogMessage("AppName", error.Value);
}
I can add the code you'll need in order to create and manage the iterator (getNextError), although we're getting outside the scope of your question.
I am building an application that highly relies on the loading speed of a web page.
I am not getting any good results with HttpWebResponse on C#. I am getting better results with internet browsers like Chrome and IE
Here are the stats that i collected:
HttpWebResponse (C#) = 17 Seconds / 20 Requests
Javascript/iFrame on Chrome = 9 seconds / 20 requests
Javascript/iFrame on IE = 11 seconds / 20 requests
Question #1
Is there anything i can do, to optimize my code for better performance?
Question #2
I can click start button twice and open two connections, so that i can get on par with browser performance. This works great, however the website i send a request to has a limit. If i send a new request before the other one is completed, it blocks my connection for 10 minutes. Is there a way i can prevent this?
My Thread:
void DomainThreadNamecheapStart()
{
while (stop == false)
{
foreach (string FromDomainList in DomainList.Lines)
{
if (FromDomainList.Length > 1)
{
// I removed my api parameters from the string
string namecheapapi = "https://api.namecheap.com/foo" + FromDomainList + "bar";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(namecheapapi);
request.Proxy = null;
request.ServicePoint.Expect100Continue = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
status.Text = FromDomainList + "\n" + sr.ReadToEnd();
sr.Close();
}
}
}
}
My Button:
private void button2_Click(object sender, EventArgs e)
{
stop = false;
Thread DomainThread = new Thread(new ThreadStart(DomainThreadNamecheapStart));
DomainThread.Start();
}
My Old Question:
How do I increase the performance of HttpWebResponse?
You're creating a thread every time the button is pressed. Creating a thread is expensive and takes time by itself. Try using a thread from an existing thread pool (try QueueUserWorkItem) and see if that helps.
How can I get this error from with in the DownloadStringCompleted Event? Doesn't that mean, it's finished? Is there another event I can fire this from?
I get this error extremely rarely, but once in a while it will happen on my WP7 phone. I have a web client that I fire over and over, and I fire it again from the completed event. Is this happening because there is still some stale connection open? Is there a way to prevent this 100%?
I have checked to see if there is a chance for the thread to walk over itself, but it is only fired from within the completed event.
How can I be sure, when the complete event is fired, the client is no longer isBusy? One suggestion was to add a while with a thread sleep while the client is busy.
Some pseudo code.
var client = new WebClient("URL 1");
client.CompletedEvent += CompletedEvent;
client.downloadasync();
void CompletedEvent(){
Dosomestuff;
client.downloadasync(); //This is where we break.
}
The WebClient only supports a single operations, it cannot download multiple files. You haven't shown your code, but my guess is that you are somehow firing a new request before the old is completed. My bet is that WebClient.IsBusy is true when you attempt to perform another fetch.
See the following thread:
wb.DownloadFileAsync throw "WebClient does not support concurrent I/O operations." exception
The only answer is to create a new webclient within the scope of the Completed Event. You can't set it to new since webclient is readonly. Creating a new client is the only solution. This allows the old client to complete in the background. This does have slight memory implications since you are creating a new instance instead of reusing an old. But the garbage collector should keep it clean if your scope is setup right.
Instead of using WebClient use HttpClient to do parallel HTTP calls. Below code shows how to download files.
HttpClient httpClient = new HttpClient();
var documentList=_documentManager.GetAllDocuments();
documentList.AsParallel().ForAll(doc =>
{
var responseResult= httpClient.GetAsync(doc.FileURLPath);
using (var memStream = responseResult.Result.Content.ReadAsStreamAsync().Result)
{
using (var fileStream =File.Create($"{filePath}\\{doc.FileName}"))
{
memStream.CopyTo(fileStream);
}
}
});
The solution, I found is to use multiple WebClient objects, so to modify your pseudocode example; try
var client = new WebClient("URL 1");
client.CompletedEvent += CompletedEvent;
client.downloadasync();
void CompletedEvent(){
Dosomestuff;
var client2 = new WebClient();
client2.downloadasync();
}
Create a new Web Client for each new request. Don't reuse an existing Web Client instance.
This allows the first request to complete before starting the new one. This is a standard way of creating new requests.
private async Void SyncParcelStatus(List<string> Urls)
{
try
{
foreach (var URL in WebhookUrls)
{
Task.Factory.StartNew(() => AsyncDownLoad(URL));
}
}
catch (Exception ex)
{
//log Exception
}
}
private async void AsyncDownLoad(string URL)
{
using (WebClient myWebClient = new WebClient())
{
try
{
Uri StringToUri = new Uri(URL);
myWebClient.DownloadStringAsync(StringToUri);
}
catch (Exception ex)
{
//log Exception
}
}
}
I have a lot of trouble with the internet connectivity in the program I am working on and it all seems to spawn from some issue with the proxy settings. Most of the issues at this point are fixed, but the issue I am having now is that my method of testing the proxy settings makes some users wait for long periods of time.
Here is what I do:
System.Net.WebClient webClnt = new System.Net.WebClient();
webClnt.Proxy = proxy;
webClnt.Credentials = proxy.Credentials;
byte[] tempBytes;
try
{
tempBytes = webClnt.DownloadData(url.Address);
}
catch
{
//Invalid proxy settings
//Code to handle the exception goes here
}
This is the only way that I've found to test if the proxy settings are correct. I tried making a web service call to our web service, but no proxy settings are needed when making the call. It will work even if I have bogus proxy settings. The above method, though, has no timeout member that I can set that I can find and I use the DownloadData as opposed to the DownloadDataAsync because I need to wait til the method is done so that I can know if the settings are correct before continuing on in the program.
Any suggestions on a better method or a work around for this method is appreciated.
Mike
EDIT: I tried something else, but no luck. I used the DownloadDataAsync method to download the data in a separate thread which raises the DownloadDataCompleted event of the WebClient when finished. While I wait for the event to get called I have a loop: while(DateTime.Now < downloadStart.AddMinutes(timeout) && !TestIsDone) {} The DownloadDataCompleted event sets the TestIsDone member to true when the event is called. The problem here is if the proxy settings are bad the Event never gets called, no exception is thrown, and the program waits for the entire timeout period before continuing. Here is the code for this approach:
public static bool TestProxy(System.Net.WebProxy proxy)
{
ProxySettingsTestDone = false; //public static var
string address = //url to some arbitrary data on our server
System.Net.WebClient webClnt = new System.Net.WebClient();
webClnt.Proxy = proxy;
webClnt.Credentials = proxy.Credentials;
try
{
webClnt.DownloadDataCompleted += new System.Net.DownloadDataCompletedEventHandler(DownloadDataCallback);
webClnt.DownloadDataAsync(new Uri(address));
//Timeout period
DateTime dnldStartTime = DateTime.Now;
while (DateTime.Now < dnldStartTime.AddMinutes(1.0) && !ProxySettingsTestDone)
{ }
if (!ProxySettingsTestDone) //Exceded timeout
{
throw new System.Net.WebException("Invalid Proxy Settings");
}
}
catch (System.Net.WebException e)
{
if (e.Status == System.Net.WebExceptionStatus.ProxyNameResolutionFailure)
{
//Proxy failed, server may or may not be there
Util.ConnectivityErrorMsg = e.Message;
return false;
}
else if (e.Status == System.Net.WebExceptionStatus.ProtocolError)
{
//File not found, server is down, but proxy settings succeded
ServerUp = false;
Util.ConnectivityErrorMsg = e.Message;
return true;
}
return false;
}
Util.ConnectivityErrorMsg = "";
return true;
}
private static void DownloadDataCallback(object sender, System.Net.DownloadDataCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
ProxySettingsTestDone = true;
else
throw new System.Net.WebException("Invalid Proxy Settings");
}
Sorry about the long post. I wanted to update this question with the information that I found after testing this new approach.
Thanks,
Mike
You can run the proxycheck in a seperate thread. And consider the check to be failed if the thread takes too long.
Or you could use WebRequest, it allows you set a timeout:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://url");
request.Proxy = proxy;
request.Timeout = 2000;
If the request has not finished within the given timeout a WebException with the Status property set to WebExceptionStatus.Timeout will be thrown.
Every method mentioned here are valid. But the most important one is to test the Proxy connection using the same Windows user account for the process that you want to test. Many proxies has specific privileges for each Windows user.
A recent post Testing IP:Port proxies explains proxy check using a simple Python script. The script checks for availability and correctness of proxy servers.