Build Tiny Load Tool Using Go and Concurrency Model
One of the best thing in go is concurrency and channels and it's really useful while building the tools or application which need high concurrency. I have recently build the load application tool using golang which need to make multiple requests to server for certain period of time.
What is concurrency:
Ability to run multiple tasks concurrently or simultaneously.
May be above statement vague for some of the people but consider this scenario:
There are 10 process which need to write in a file simultaneously and system has one CPU. Now cpu can only serve one process at one time. So at one time there could be only one process which would be writing in file and other process will wait but wait would be very short(e.g. nanoseconds) and cpu will serve other process very quickly which seem like it's parallel but underneath cpu serving all the process in sequence for very short span of time(e.g. nanoseconds). you can check this digram to understand this:
How Concurrency work on Golang:
You just say : go foo() => which called goroutine
Which basically call method and create goroutine. goroutine is light weight thread, which take very less stack memory(2k) and cpu and destroy automatically as soon as they done.
Here is sample example to build goroutines:
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
runtime.GOMAXPROCS(1)
fmt.Println("Starting it => ")
var swg sync.WaitGroup
swg.Add(1)
go func() {
defer swg.Done()
for count := 0; count 1; count++ {
for char := 'a'; char 'a'+26; char++ {
fmt.Printf("%c", char)
}
}
}()
fmt.Println("\nWaiting to finish")
swg.Wait()
}
Let me explain, what's happening here:
runtime.GoMAXPROCS(1) : This is golang official site say about runtime:
The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes the limit.
go func() { }() : This is go anonymous function but here it's serving as goroutine. When controller come to this point it generate goroutine(which is basically a method) to do the job.
sync.WaitGroup : it waits for goroutine to complete
provides methods as : , and . identify how many goroutines need to be waited. When a goroutine exits, it must call . The goroutine blocks on , Once the becomes, the will return, and goroutine can continue to run.
swg.Add(1) : you are adding one goroutine in the group and telling main function to wait for this goroutine to complete.
defer swg.Done() : defer is golang keyword. it allow the statement to be executed at the end of method and Done(method) which signals that this goroutine is done.
swg.Wait() : Wait in main function until swg.Done hasn't been called.
Let's build one simple load tool using goroutine:
Let's modify our above code and build a load testing tool which will make the requests to server continuously. It doesn't need much change, we just need to include some of the new libs like flag(for command line options) and log(for logging). Other than that almost everything is really simple and same as above.
Run this code as : go run main.go -rps 10 -req "http://google.com" -req_type GET
package main
import (
"flag"
"log"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
rps := flag.Int("rps", 10, "request per second
or throughput")
req := flag.String("req", "http://google.com",
"server need to hit")
reqType := flag.String("req_type", "GET",
"Request type to make")
flag.Parse()
log.Printf("Throughput => %d\n", *rps)
log.Printf("Requests => %s\n", *req)
log.Printf("Type of Request => %s\n", *reqType)
wg.Add(*rps) //dynamically adding goroutine
var i = 0
for i *rps { // Loop to generate goroutines
go func() {
for {
time.Sleep(time.Second)
log.Printf("RPS : %d\n", *rps)
}
}()
i++
}
wg.Wait()
}
I guess, most of the things would be clear as everything is same as our first example.
Hope this will give some light to understand goroutine in golang. Please provide your comments and feedback below.