6

It is possible to run a thread from the WebRole.cs OnStart() method in such a way that we are able to access it through aspx page to perform background work? I know that the correct way to do it would be to use a Worker Role but i wish to maintain the running costs as low as possible.

The idea would be to create a thread that would be always running and waiting for a job, for instance if i want to make a blocking operation like sending an email i would use the thread giving the SendEmail method, is it possible to do? If so, can you provide me some examples that could point me in the right direction?

David Makogon
  • 69,407
  • 21
  • 141
  • 189
ToinoBiclas
  • 262
  • 4
  • 13
  • How would that reduce running costs? How is what you want different from single instance single concurrency service? – paparazzo Jun 07 '12 at 13:10
  • 3
    @Blam - Running background tasks in a Web Role allows the combining of operations into a single set of VM instances. This is in contrast to placing background operations in a separate set of role instances. Combining into one role would save costs, as each role must have at least one instance running. For low-volume sites, this is a great cost-saving architecture. If there's a risk of background tasks starving the website, or there's a need to scale front-end and background tasks separately (or have differing VM size needs), it's worth considering the move to a separate role. – David Makogon Jun 07 '12 at 13:18
  • @DavidMakogon Thanks I did not know a Worker Role required a separate instance. And I did not try and answer the question. +1 I learned a lot from this question. – paparazzo Jun 07 '12 at 14:42

3 Answers3

10

I would suggets a solution that is different from Leon and David's solution:

  • David's solution is OK but is not resilient. What it the instance/process goes offline while processing the task?
  • Leon's solution mostly applies to scheduled jobs, but sending an email isn't always something that is scheduled (maybe you want to send an email when someone registers in your app).

An other option you should look at is using Windows Azure Storage Queues (they are very cheap) in this scenario:

  • Your web application: Sends messages to the queue (like 'send an email to someone@someone.com')
  • WebRole.cs: spawn a new thread when starting the instance and have it listen for messages from that queue. Whenever an message arrives, process it. If success, remove the message from the queue.

This solution has many advantages. The WebRole.cs runs in a different process than your web application, so there is no impact on the request threads. And besides that, if sending the mail fails for whatever reason, the message will stay in the queue and will be processed the next time. This will make sure you won't loose any tasks to execute if the application or the process crashes.

Here is an example to get you started. Note that you'll need to improve this code if you want it to be production ready (retry policy, exception handling, backoff polling, ...):

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.Threading.Tasks;

namespace MvcWebRole1
{
    public class WebRole : RoleEntryPoint
    {
        public override bool OnStart()
        {
            Task.Factory.StartNew(InitializeQueueListener);
            return base.OnStart();
        }

        private void InitializeQueueListener()
        {
            Microsoft.WindowsAzure.CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                configSetter(Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue(configName));
            });


            var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
            var queueStorage = storageAccount.CreateCloudQueueClient();
            var queue = queueStorage.GetQueueReference("myqueue");
            queue.CreateIfNotExist();

            while (true)
            {
                CloudQueueMessage msg = queue.GetMessage();
                if (msg != null)
                {
                    // DO SOMETHING HERE
                    queue.DeleteMessage(msg);
                }
                else
                {
                    System.Threading.Thread.Sleep(1000);
                }
            }
        }
    }
}
Sandrino Di Mattia
  • 24,739
  • 2
  • 60
  • 65
  • Seems like a better solution for the kind of work i'm willing to perform, anyway i was planning on having Queues for "talking" with an on-premises application so it will be not that expensive... Can you further detail how to "spawn a new thread when starting the instance and have it listen for messages from that queue"? **it would be great if you could give me an example or point me to a sample that explains the threading part involved in starting a listening thread in WebRole.cs**. Thanks – ToinoBiclas Jun 07 '12 at 13:49
  • Agreed - queues are very resilient. My intent was to point out the viability of creating threads (which is not always obvious). – David Makogon Jun 07 '12 at 13:50
  • @Sandrino Let me know if this is inline with your solution... create a thread in WebRole.OnStart() 'new Thread(new ThreadStart(ClassListeningToQueue.Method0)' then inside Method0 write an infinite loop or a loop with a condition that only changes through WebRole.OnStop() 'while (true) { try { CloudQueueMessage msg = queue.GetMessage(); if (msg != null) {' finnaly perform the task according to the values inside the msg for example i could have a field that indicates which method to call and the parameters as well. Is it safe to call other threads inside the main thread? – ToinoBiclas Jun 07 '12 at 14:37
  • This is safe indeed. I would not advise on starting other threads in the thread you just spawned, because you might cause it to empty the queue immediately and use too much resources on the current instance. – Sandrino Di Mattia Jun 07 '12 at 14:40
  • @Sandrino Forget my last post i added the comment after you edited the answer but the page was not refreshed so i was not able to see it LOL. I will set the answer as accepted, it was really helpfull. – ToinoBiclas Jun 07 '12 at 14:43
  • Because the WebRole.cs runs in a different process than your web app, it also does not have access to the web.config. How would I go about defining a connection string for this process? – Kyle Oct 24 '12 at 14:29
  • You need to create an extra file, WaIISHost.exe.config. See this blog post: http://blogs.msdn.com/b/windowsazure/archive/2010/12/02/new-full-iis-capabilities-differences-from-hosted-web-core.aspx – Sandrino Di Mattia Oct 24 '12 at 19:30
2

Absolutely you can create a thread (or lots of them). A Web Role is basically Windows 2008 Server. You don't need a separate Worker Role just to set up a background task. Of course, you can have a separate worker role, which would allow you to scale those instances independently of your Web role instances. This is where you'll need to balance performance/scaling with cost.

David Makogon
  • 69,407
  • 21
  • 141
  • 189
  • Can you give me some details on how to do this? How am i able to access the thread variable from one of the aspx web pages if it has been declared in the WebRole : RoleEntryPoint? – ToinoBiclas Jun 07 '12 at 13:35
  • 2
    If you need to interact with threads from your aspx pages, why not start the threads from global.asax? RoleEntryPoint will run in a separate AppDomain so you won't have interaction between app and thread. – David Makogon Jun 07 '12 at 13:49
  • Honestly never thought about it. I will try to do some testing with a Thread Pool declared in global.asax. – ToinoBiclas Jun 07 '12 at 14:09
1

I found this when I was looking for "azure scheduled tasks": http://www.ronaldwidha.net/2011/02/23/cron-job-on-azure-using-scheduled-task-on-a-web-role-to-replace-azure-worker-role-for-background-job/

Looks like exactly what you're looking for.

Leon Cullens
  • 12,276
  • 10
  • 51
  • 85
  • The example that you pointed out is for running scheduled jobs, much like crontabs in Linux. What i am willing to do its having a thread waiting to be signaled. – ToinoBiclas Jun 07 '12 at 13:33
  • It's just to point out that it's definately possible to do these kind of things. I guess you can come up with a solution with just a little creativity. – Leon Cullens Jun 07 '12 at 13:35