Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
356 views
in Technique[技术] by (71.8m points)

c# - Windows Service runs but stops processing after two days

I've seen a fair few posts regarding this issue but non that seem to fit my criteria.

Having said that, this is the first one that I've built that "needs" to perform a tremendous amount of logic. All works great for a couple days but after that logic stops working with no log in the event viewer nor firing of an email (exceptions).

I'm wondering if my logic is not correct... Looking for advice and pointers.

public partial class QuayService : ServiceBase
{
    private System.Timers.Timer m_mainTimer;
    private bool m_timerTaskSuccess;
    private Email _email;
    public QuayService()
    {
        InitializeComponent();
        _email = new Email();
    }

    protected override void OnStart(string[] args)
    {
        try
        {
            // Create and start a timer.
            m_mainTimer = new System.Timers.Timer();
            m_mainTimer.Interval = 5000;   // every 5 seconds
            m_mainTimer.Elapsed += m_mainTimer_Elapsed;
            m_mainTimer.AutoReset = false;  // makes it fire only once
            m_mainTimer.Start(); // Start

            m_timerTaskSuccess = false;

            _email.SendEmail("Quay", "(Shopify)Quay Service Started",
                "(Shopify)Quay Service Successfuly Started");

            EventLog.WriteEntry("(Shopify)Quay Service Started...");
        }
        catch (Exception ex)
        {
            // Log/Send Email
            _email.SendEmail("Quay", "Error starting (Shopify)Quay Service", ex.Message + " " + 
                ex.InnerException.Message);

            EventLog.WriteEntry("Error starting (Shopify)Quay service and timer..." + ex.Message + " " +
                ex.InnerException.Message);
        }
    }

    protected override void OnStop()
    {
        try
        {
            // Service stopped. Also stop the timer.
            m_mainTimer.Stop();
            m_mainTimer.Dispose();
            m_mainTimer = null;

            _email.SendEmail("Quay", "(Shopify)Quay Service stopped",
                "(Shopify)Quay Service Successfuly Stopped");

            EventLog.WriteEntry("(Shopify)Quay Service stopped...");
        }
        catch (Exception ex)
        {
            _email.SendEmail("Quay", "Error stopping (Shopify)Quay Service", ex.Message + " " +
                ex.InnerException.Message);

            // Log/Send Email
            EventLog.WriteEntry("Error stopping (Shopify)Quay timer and service..." + ex.Message + " " +
                ex.InnerException.Message);
        }
    }

    void m_mainTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        try
        {
            var orderUoW = new OrderUoW();
            orderUoW.Create();

            m_timerTaskSuccess = true;
        }
        catch (Exception ex)
        {
            //Error with timer elapsed
            m_timerTaskSuccess = false;

            _email.SendEmail("Quay", "Error creating (Shopify)Quay order(s)", ex.Message + " " +
                ex.InnerException.Message);

            EventLog.WriteEntry("Error creating (Shopify)Quay order(Time elapsed event)..." + ex.Message
                + " " + ex.InnerException.Message);
        }
        finally
        {
            if (m_timerTaskSuccess)
            {
                m_mainTimer.Start();
            }
        }
    }
}

To get to this point, I "googled" and "SO'ed" to find the best use of timer... I also have a na?ve test layer that reproduces what's in the service, I can run through that fine without any exceptions but this one is driving me nuts.

Any help truly appreciated!

//EDIT

A little explanation on:

var orderUoW = new OrderUoW(); 
orderUoW.Create();

OrderUoW is a class in my service that inherits from BatchOrder which is responsible for many actions including connecting to two databases and polling of the shopify API... OrderUow is purely to decouple from the business layer but giving access to "x" methods.

All works flawlessly for two days but just seems to halt. I'm not hitting request limits within shopify... so at the moment, I'm at a loss.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

After many moments of scratching my head and extreme annoyance with the MS Timer Bug.

Not only did it swallow exceptions, the timer did not raise the event! My service was hanging, which was particularly difficult because it also swallowed exceptions.

Now I did go down the route of half implementing with Threading.Timer

  1. Don't use System.Windows.Forms.Timer because it won't work (this only makes sense).

  2. Don't use System.Threading.Timer because it doesn't work, use System.Timers.Timer instead.

  3. Don't use System.Timers.Timer because it doesn't work, use System.Threading.Timer instead.

Related question.

I really could not be bothered with the issues that came with these steps, as my aim was to focus on the business logic. So I made the decision to use Quartz.Net.

It made my life much easier and seems to work perfectly! 45-60 minutes worth of work.

Basic Setup:

1, Nuget > Quartz

2, New Folder(s) > QuartzComponents > Jobs and Schedule

enter image description here

3, Added OrderJob.cs and OrderJobSchedule.cs in respective folders

OrderJob.cs logic:

 public sealed class OrderJob : IJob
 {
    public void Execute(IJobExecutionContext context)
    {
            var orderUoW = new OrderUoW();
            orderUoW.Create();
     }
  }

Notice that it creates and instance of my UoW?!? logic that it would hit on each pass.

OrderJobSchedule.cs logic:

   public sealed class OrderJobSchedule
    {
        public void Start()
        {
            IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
            scheduler.Start();

            IJobDetail job = JobBuilder.Create<OrderJob>().Build();

            ITrigger trigger = TriggerBuilder.Create()
                   .WithSimpleSchedule(a => a.WithIntervalInSeconds(15).RepeatForever())
                   .Build();

            scheduler.ScheduleJob(job, trigger);
        }

        public void Stop()
        {
            IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
            scheduler.Shutdown();
        }
    }

A lot of magic here, but to emphasis on:

JobBuilder.Create<OrderJob>().Build();

a.WithIntervalInSeconds(15).RepeatForever()

Now that's in place we need to add logic to the "guts" of the service:

public partial class QuayService : ServiceBase
    {
        OrderJobSchedule scheduler;
        public QuayService()
        {
            InitializeComponent();
        }
        protected override void OnStart(string[] args)
        {
            try
            {
                scheduler = new OrderJobSchedule();
                scheduler.Start();

                SendEmail("(Shopify)Quay Service Started",
                    "(Shopify)Quay Service Successfuly Started");
            }
            catch (Exception ex)
            {
                ProcessException(ex, "Error starting (Shopify)Quay Service");
                EventLog.WriteEntry("Error starting (Shopify)Quay service and timer..." + ex.Message);
            }
        }
        protected override void OnStop()
        {
            try
            {
                if (scheduler != null)
                {
                    scheduler.Stop();
                }

                SendEmail("(Shopify)Quay Service stopped",
                    "(Shopify)Quay Service Successfuly Stopped");
            }
            catch (Exception ex)
            {
                ProcessException(ex, "Error stopping (Shopify)Quay Service");
                EventLog.WriteEntry("Error stopping (Shopify)Quay timer and service..." + ex.Message);
            }
        }
        private void SendEmail(string subject, string body)
        {
            new Email().SendErrorEmail("Quay", subject, body);
        }
        private void ProcessException(Exception ex,
            string customMessage)
        {
            var innerException = "";
            if (ex.InnerException != null)
                innerException = (!string.IsNullOrWhiteSpace(ex.InnerException.Message)) ? ex.InnerException.Message : "";


            new Email().SendErrorEmail("Quay", customMessage, 
                ex.Message + " " + innerException);
        }
    } 

Very easy to set up and solved my horrendous experience with Timers.Timer Whilst I didn't fix the core issue, I came up with a solution and got a bug free working system in place.

Please note to future readers DO NOT USE System.Timers.Timer unless you are prepared to add a "hack'ish" fix.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...