Search Wiki:

Note: F# templates are now included with the Windows Azure Tools for VS2010

F# for Windows Azure

This download includes project templates for building Windows Azure worker roles using F#. This allows you to develop F# applications which run in the cloud. Two samples give examples of how to use F# as a background worker process for a standard Web application, and how to use F# with Windows Azure for distrubted computation and storage.

Getting Started


Windows Azure runs applications in partial trust, as part of sandboxing their execution. However, the F# core libraries are currently installed into the GAC, but do not have the AllowPartialTrustedCallers attribute. So when building F# applications to be run in Azure, the F# libraries must be statically linked using --standalone. The provided templates take care of this, but you'll notice the following side-affects:
  • Longer than usual compile times
  • Large set of references
  • A dummy reference to "RDManaged.dll"



A version of the Thumbnails sample that is part of the Windows Azure SDK samples. Combines a C# Web role and an F# Worker role. The Worker role accepts queued requests to create thumbnails for image files, and places the results into the blob store.

open Microsoft.ServiceHosting.ServiceRuntime
open Microsoft.ServiceHosting.ServiceRuntime;
open Microsoft.Samples.ServiceHosting.StorageClient;
open System
open System.Drawing
open System.Drawing.Drawing2D
open System.IO
open System.Threading
open System.Net
type WorkerRole() =
    inherit RoleEntryPoint() 
    let log kind message = RoleManager.WriteToLog(kind,message)
    /// Create a 128x128 thumbnail based on input binary image data
    let CreateThumbnail (input: Stream) = 
        let orig = new Bitmap(input)
        let width, height = 
            if (orig.Width > orig.Height)
            then 128, 128 * orig.Height / orig.Width
            else 128 * orig.Width / orig.Height, 128
        let thumb = new Bitmap(width, height)
        use graphic = Graphics.FromImage(thumb, 
                                         InterpolationMode = InterpolationMode.HighQualityBicubic,
                                         SmoothingMode = SmoothingMode.AntiAlias,
                                         PixelOffsetMode = PixelOffsetMode.HighQuality)
        do graphic.DrawImage(orig, 0, 0, width, height)
        let ms = new MemoryStream();
        do thumb.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
        let _ = ms.Seek(0L, SeekOrigin.Begin);
    /// Main loop of worker process
    let rec Loop (queue : MessageQueue, container: BlobContainer) = 
        let msg = queue.GetMessage();
        if msg = null
            let path = msg.ContentAsString();
            do RoleManager.WriteToLog("Information", String.Format("Dequeued '{0}'", path));
            let content = new BlobContents(new MemoryStream());
            let props = container.GetBlob(path, content, false);
            let blobProps = new BlobProperties(Path.Combine("thumbnails", props.Name))
            do blobProps.ContentType <-  "image/jpeg"
            let thumb = CreateThumbnail(content.AsStream)
            let _ = container.CreateBlob(blobProps, new BlobContents(thumb), true);
            do RoleManager.WriteToLog("Information", String.Format("Done with '{0}'", path));
            let _ = queue.DeleteMessage(msg)
    /// Setup the blob and queue storage we'll use and enter the main loop
    override wp.Start() =
        let blobStorage = BlobStorage.Create(StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration());
        let container = blobStorage.GetBlobContainer("photogallery");
        let _ = container.CreateContainer(null, ContainerAccessControl.Public);
        let queueStorage = QueueStorage.Create(StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());
        let queue = queueStorage.GetQueue("thumbnailmaker");
        let _ = queue.CreateQueue();
    override wp.GetHealthStatus() = RoleStatus.Healthy


A web crawler that follows links in web pages downloading the pages as it goes into a blob storage. New urls to process are pulled from a queue, the page for the url is downloaded and saved to the blob store, and the links from the page are extracted and placed back into the queue. This causes the queue to continue to grow as new pages are visited. The project is configured to run 4 worker processses, providing simple and implicit parallelism.

namespace SearchEngine_WorkerRole
open System
open System.Threading
open Microsoft.ServiceHosting.ServiceRuntime
open System.Net
open System.IO
open System.Text.RegularExpressions
open Microsoft.Samples.ServiceHosting.StorageClient;
open System.Web
open System.Runtime.Serialization.Formatters.Binary
type WorkerRole() =
    inherit RoleEntryPoint()
    // The page to start crawling from
    let startpage = @""
    // The filter to apply to links while crawling
    let pageFilter = fun (url:string) -> url.StartsWith("")
    /// Get the contents of a given url
    let http(url: string) = 
        let req    = WebRequest.Create(url) 
        use resp   = req.GetResponse()
        use stream = resp.GetResponseStream() 
        use reader = new StreamReader(stream) 
        let html   = reader.ReadToEnd()
    /// Get the links from a page of HTML
    let linkPat = "href=\s*\"[^\"h]*(http://[^&\"]*)\""
    let getLinks text =  [ for m in Regex.Matches(text,linkPat)  -> m.Groups.Item(1).Value ]
    /// Handle the message msg using the given queue and blob container
    let HandleMessage (msg : Message) (queue : MessageQueue, container: BlobContainer) =
        // There was a new item, get the contents
        let url = msg.ContentAsString();
        let urlBlobName = HttpUtility.UrlEncode(url)
        // Don't get the page if we've already seen it
        if not(container.DoesBlobExist(urlBlobName)) 
            do RoleManager.WriteToLog("Information", String.Format("Handling new url: '{0}'", url));
                // Get the contents of the page
                let content = http url
                // Store the page into the blob store
                let props = new BlobProperties(urlBlobName)
                let _ = container.CreateBlob(props, new BlobContents(System.Text.UTF8Encoding.Default.GetBytes(content)), true);
                // Get the links from the page
                let links = getLinks content
                // Filter down the links and then create a new work item for each
                |> Seq.filter pageFilter
                |> Seq.distinct
                |> Seq.filter (fun link -> not(container.DoesBlobExist(HttpUtility.UrlEncode(link))))
                |> Seq.iter (fun link -> queue.PutMessage(new Message(link)) |> ignore)
                queue.DeleteMessage(msg) |> ignore
            | _ ->()
    /// Main loop of worker process
    let rec Loop (queue : MessageQueue, container: BlobContainer) = 
        // Get the next page to crawl from the queue
        let msg = queue.GetMessage(240);
        if msg = null
        then Thread.Sleep(1000)
        else HandleMessage msg (queue, container)
    override wp.Start() =
        // Initialize the Blob storage
        let blobStorage = BlobStorage.Create(StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration());
        let container = blobStorage.GetBlobContainer("searchengine");
        let a = container.CreateContainer(null, ContainerAccessControl.Public);
        // Initialize the Queue storage
        let queueStorage = QueueStorage.Create(StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());
        let queue = queueStorage.GetQueue("searchworker");
        let b = queue.CreateQueue()
        // Put an initial message in the queue, using the start page
        let c = queue.PutMessage(new Message(startpage));
        // Begin the main loop, processing messages in the queue
        Loop(queue, container)
    override wp.GetHealthStatus() = RoleStatus.Healthy

Last edited Mar 31 2010 at 6:03 PM  by LukeH, version 13
Page view tracker