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! :)