The easy way to create tasks in Beego

This is actually a quite funny story - I'm working on my second iOS app and I decided to write its backend in Rails. I think I was midway through when I added the NewRelic gem to get a rough idea of what I should expect in order of resource usage once it's live. I fired a terminal and hit the user login endpoint with wrk (nothing fancy, the .lua file it's just a POST with username and user_id fields).

wrk -t12 -c400 -d30s -s users_login.lua http://localhost:3000/users/login

The bad part was when I checked NewRelic and the Rails app went over 500MB of RAM and I was sitting there thinking

Oh, man, this is not going to be good I still have to add Resque and resque-scheduler plus at least a worker, FFFFFUUUUU!!!"


Well, I spent the next 2 days rewriting my API in Go and I have "no regerts". Despite its shady syntax, Go is actually a really nice language and there are a lot of tools that make developers' lives easier. Even though all the Go community is pretty against full stack (or as they call them: "batteries included") frameworks suggesting you could do everything with the net/http package or using Gorilla Toolkit, I'm more of a framework guy.


I had to choose between Revel and Beego and I went for Beego because Revel doesn't actually have an ORM, which wasn't enough "batteries included" for me.

Let's Beego

Beego has many predefined modules which makes it pretty cool to work with. At this point Beego's main problem is its lacking documentation, if you ask me. So, without further ado, let's get straight to the Beego tasks (you should probably take a brief look through the task.go source code).


Start by creating a folder in your Beego project path and a new task file.

mkdir path/to/beego/project/tasks
touch path/to/beego/project/tasks/first_task.go

Now for the task file:

package tasks

import (
    "fmt"
    "time"

    "github.com/astaxie/beego/toolbox" // the toolbox package
    "MyApp/models" // you probably want to access your models in the task
                   // to keep the business logic at the right place
)

func init() {
    first_task := toolbox.NewTask("first_task", "0/30 * * * * *", func() error {
        // this task will run every 30 seconds
        campaigns, err := models.GetFinishedCampaigns()
        if err != nil {
            fmt.Println("Could not load finished campaigns with error:", err)
            return err
        }

        if len(campaigns) == 0 {
            fmt.Println("No campaigns finished yet. Exiting.")
            return nil
        }

        for _, campaign := range campaigns {
            result, err := models.SendCampaignNotification(campaign)
            if err != nil {
                fmt.Printf("\nCampaign %d could not be notified!\n", campaign.Id)
            }

            if result {
                fmt.Printf("\nCampaign %d was notified!\n", campaign.Id)
            }
        }

        fmt.Printf("\nNotification task ran at: %s\n", time.Now())
        return nil
    })

    toolbox.AddTask("first_task", first_task)
    toolbox.StartTask()
    defer toolbox.StopTask()
}

Now there's only one thing left to do - go to your main.go file and import your new package. It should look something like this:

package main

import (
    _ "MyApp/routers"

    "github.com/astaxie/beego"
    "github.com/astaxie/beego/orm"
    _ "github.com/go-sql-driver/mysql"

    _ "MyApp/tasks" // this is the task import
)

func main() {
    beego.Run()
}

That's it!

Now that you saw how easy it is to create a task in Beego just think about all that memory that you use for Resque (yeah, I know a queue system is not actually the same as a simple task, but this tiny task will run every 30 seconds at an incredible speed and low resource usage).

And that's how you Beego for fun and profit! :)

Written by Bogdan Constantinescu on
Tagged: go golang beego