通过实例入门Golang
如果想学会一门新语言,不仅要多读文档,还要多看别人写的代码,更要强迫自己用新语言多写代码。我在学习 Golang 之前,读过好几本相关的书籍,不过总感觉没真正学会,于是我决定动手用 Golang 写一个能用的工具试试,因为 Golang 最大的优势就是 goroutine 和 channel,所以我觉得实现一个简版的 ab(Web 压力测试工具)应该是一个不错的选择,用 Golang 磕磕绊绊总算实现了预想的功能,能够计算 Requests per second 和 Time per request 的值,不过总感觉写出来的代码不够漂亮,于是我又找来 hey 的代码前后读了几遍,然后结合自己的理解临摹了一遍,感觉总算是入门了。
虽然 hey 的代码本身已经相当简洁,但是洋洋洒洒加起来也有五六百行代码,下面是我默写的版本,仅保留主体功能,总共就一两百行代码:
package main import ( "flag" "fmt" "log" "net/http" "os" "strings" "sync" "time" ) var usage = `Usage: %s [options] Options are: -n requests Number of requests to perform -c concurrency Number of multiple requests to make at a time -s timeout Seconds to max. wait for each response -m method Method name ` var ( requests int concurrency int timeout int method string url string ) func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, usage, os.Args[0]) } flag.IntVar(&requests, "n", 1000, "") flag.IntVar(&concurrency, "c", 100, "") flag.IntVar(&timeout, "s", 10, "") flag.StringVar(&method, "m", "GET", "") flag.Parse() if flag.NArg() != 1 { exit("Invalid url.") } method = strings.ToUpper(method) url = flag.Args()[0] if method != "GET" { exit("Invalid method.") } if requests < 1 || concurrency < 1 { exit("-n and -c cannot be smaller than 1.") } if requests < concurrency { exit("-n cannot be less than -c.") } w := Work{ Requests: requests, Concurrency: concurrency, Timeout: timeout, Method: method, Url: url, } w.Run() } func exit(msg string) { flag.Usage() fmt.Fprintln(os.Stderr, "\n[Error] "+msg) os.Exit(1) } type Work struct { Requests int Concurrency int Timeout int Method string Url string results chan *Result start time.Time end time.Time } type Result struct { Duration time.Duration } func (w *Work) Run() { w.results = make(chan *Result, w.Requests) w.start = time.Now() w.runWorkers() w.end = time.Now() w.print() } func (w *Work) runWorkers() { var wg sync.WaitGroup wg.Add(w.Concurrency) for i := 0; i < w.Concurrency; i++ { go func() { defer wg.Done() w.runWorker(w.Requests / w.Concurrency) }() } wg.Wait() close(w.results) } func (w *Work) runWorker(num int) { client := &http.Client{ Timeout: time.Duration(w.Timeout) * time.Second, } for i := 0; i < num; i++ { w.sendRequest(client) } } func (w *Work) sendRequest(client *http.Client) { req, err := http.NewRequest(w.Method, w.Url, nil) if err != nil { log.Fatal(err.Error()) } start := time.Now() client.Do(req) end := time.Now() w.results <- &Result{ Duration: end.Sub(start), } } func (w *Work) print() { sum := 0.0 num := float64(len(w.results)) for result := range w.results { sum += result.Duration.Seconds() } rps := int(num / w.end.Sub(w.start).Seconds()) tpr := sum / num * 1000 fmt.Printf("Requests per second:\t%d [#/sec]\n", rps) fmt.Printf("Time per request:\t%.3f [ms]\n", tpr) }
代码虽短,却涵盖了 Golang 常见的用法,如果你想学习 Golang,不妨亲自动手实现一下本例子,搞懂它基本就可以算是入门了。
文章来源:
Author:老王
link:https://huoding.com/2017/06/09/623
下一篇:如何快速判断配置文件的路径
上一篇:说说压力测试工具