前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过实例入门Golang

通过实例入门Golang

作者头像
LA0WAN9
发布2021-12-14 08:40:38
2220
发布2021-12-14 08:40:38
举报
文章被收录于专栏:火丁笔记

如果想学会一门新语言,不仅要多读文档,还要多看别人写的代码,更要强迫自己用新语言多写代码。我在学习 Golang 之前,读过好几本相关的书籍,不过总感觉没真正学会,于是我决定动手用 Golang 写一个能用的工具试试,因为 Golang 最大的优势就是 goroutine 和 channel,所以我觉得实现一个简版的 ab(Web 压力测试工具)应该是一个不错的选择,用 Golang 磕磕绊绊总算实现了预想的功能,能够计算 Requests per second 和 Time per request 的值,不过总感觉写出来的代码不够漂亮,于是我又找来 hey 的代码前后读了几遍,然后结合自己的理解临摹了一遍,感觉总算是入门了。

虽然 hey 的代码本身已经相当简洁,但是洋洋洒洒加起来也有五六百行代码,下面是我默写的版本,仅保留主体功能,总共就一两百行代码:

代码语言:javascript
复制
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"
)

var usage = `Usage: %s [options]
Options are:
    -n number     Number of requests to perform
    -c concurrency  Number of multiple requests to make at a time
    -t timeout      Seconds to max. wait for each response
    -m method       Method name
`

var (
	number      int
	concurrency int
	timeout     int
	method      string
	url         string
)

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usage, os.Args[0])
	}

	flag.IntVar(&number, "n", 1000, "")
	flag.IntVar(&concurrency, "c", 10, "")
	flag.IntVar(&timeout, "t", 1, "")
	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 number < 1 || concurrency < 1 {
		exit("-n and -c cannot be smaller than 1.")
	}

	if number < concurrency {
		exit("-n cannot be less than -c.")
	}
}

func main() {
	b := benchmark{
		number:      number,
		concurrency: concurrency,
		timeout:     timeout,
		method:      method,
		url:         url,
	}

	b.run()
}

func exit(msg string) {
	flag.Usage()
	fmt.Fprintln(os.Stderr, "\n[Error] "+msg)
	os.Exit(1)
}

type benchmark struct {
	number      int
	concurrency int
	timeout     int
	method      string
	url         string
	duration    chan time.Duration
	start       time.Time
	end         time.Time
}

func (b *benchmark) run() {
	b.duration = make(chan time.Duration, b.number)
	b.start = time.Now()
	b.runWorkers()
	b.end = time.Now()

	b.report()
}

func (b *benchmark) runWorkers() {
	var wg sync.WaitGroup

	wg.Add(b.concurrency)

	for i := 0; i < b.concurrency; i++ {
		go func() {
			defer wg.Done()
			b.runWorker(b.number / b.concurrency)
		}()
	}

	wg.Wait()
	close(b.duration)
}

func (b *benchmark) runWorker(num int) {
	client := &http.Client{
		Timeout: time.Duration(b.timeout) * time.Second,
	}

	for i := 0; i < num; i++ {
		b.request(client)
	}
}

func (b *benchmark) request(client *http.Client) {
	req, err := http.NewRequest(b.method, b.url, nil)

	if err != nil {
		log.Fatal(err.Error())
	}

	start := time.Now()
	client.Do(req)
	end := time.Now()

	b.duration <- end.Sub(start)
}

func (b *benchmark) report() {
	sum := 0.0
	num := float64(len(b.duration))

	for duration := range b.duration {
		sum += duration.Seconds()
	}

	rps := int(num / b.end.Sub(b.start).Seconds())
	tpr := sum / num * 1000

	fmt.Printf("rps: %d [#/sec]\n", rps)
	fmt.Printf("tpr: %.3f [ms]\n", tpr)
}

代码虽短,却涵盖了 Golang 常见的用法,如果你想学习 Golang,不妨亲自动手实现一下本例子,搞懂它基本就可以算是入门了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档