Go小课:第一次Say Hello
一、Say Hello请求
1、环境配置
- 安装Go的包依赖管理命令行工具
govendor
go get -u github.com/kardianos/govendor 复制代码
- 创建项目文件夹
mkdir -p $GOPATH/src/github.com/yourusername/project && cd "$_" 复制代码
- govendor初始化
govendor init 复制代码
- 获取go的web框架gin
govendor fetch github.com/gin-gonic/gin 复制代码
- 在项目根路径下,新建文件main.go文件
2、Get请求和Post请求代码
//main.go package main import ( "github.com/gin-gonic/gin" "net/http" "strconv" ) func main() { // gin的Default方法创建一个路由handler。然后通过HTTP方法绑定路由规则和路由函数,gin把request和response都封装到gin.Context的上下文环境。 // 最后启动路由的Run方法监听端口。 router := gin.Default() // get请求 router.GET("/user/welcome", func(c *gin.Context) { //获取url中的参数信息 namestr := c.DefaultQuery("name", "Guest") agestr := c.DefaultQuery("age", "18") //各种处理逻辑 ageValue, err := strconv.Atoi(agestr) role := "小伙子" if err != nil { role = "可疑的人" } else { if ageValue > 28 { role = "大哥" } } //返回 c.String(http.StatusOK, "%s %s,你好呀", role, namestr) }) //curl http://127.0.0.1:8000/user/welcome\?name\=jack\&age\=18 router.POST("/user/postdata", func(c *gin.Context) { message := c.PostForm("message") namestr := c.DefaultPostForm("name", "anonymous") agestr := c.DefaultPostForm("age", "18") //各种处理逻辑 ageValue, err := strconv.Atoi(agestr) role := "小伙子" if err != nil { role = "可疑的人" } else { if ageValue > 28 { role = "大哥" } } message = role + namestr + " 你好呀" c.JSON(http.StatusOK, gin.H{ "status": gin.H{ "code": http.StatusOK, "status": "ok", }, "data": gin.H{ "message": message, }, }) }) //curl -X POST http://127.0.0.1:8000/user/postdata -H "Content-Type:application/x-www-form-urlencoded" -d "name=jack&age=28" | python -m json.tool router.Run(":8000") } 复制代码
说明:启动服务后,可以用curl命令工具来发请求,也可以用Postman来发请求,借此来测试接口是否可以访问。
二、接口联调
1、GET请求联调
-
接口名:/user/welcome
-
端口:80000
curl http://127.0.0.1:8000/user/welcome\?name\=jack\&age\=38 大哥 jack,你好呀**%** 复制代码
curl http://127.0.0.1:8000/user/welcome\?name\=jack\&age\=18 小伙子 jack,你好呀**%** 复制代码
curl http://127.0.0.1:8000/user/welcome\?name\=jack\&age\=hhah 可疑的人 jack,你好呀**%** 复制代码
说明:可以使用本机的ip地址。
2、POST请求联调
-
接口名:/user/postdata
-
端口:80000
curl -X POST http://127.0.0.1:8000/user/postdata -H "Content-Type:application/x-www-form-urlencoded" -d "name=jack&age=28" | python -m json.tool % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 123 100 107 100 16 104k 16000 --:--:-- --:--:-- --:--:-- 120k { "data": { "message": "54GP5b+O57Su54Cb5oGTYWNrIOa1o+eKsuOCvemNm+KCrA==" }, "status": { "code": 200, "status": "ok" } } 复制代码
三、接口性能测试
1、接口性能指标
衡量接口性能可以从下面几个指标来看:
-
QPS(TPS):每秒钟 Request/事务 数量,在互联网领域,指 每秒响应请求数 (指http请求)
-
事务: 用户某一步或几步操作的集合,我们要保证它有一个完整意义。比如用户对某一个页面的一次请求,用户对某系统的一次登录,淘宝用户对商品的一次确认支付过程。这些我们都可以看作一个事务
-
响应时间:系统对一个请求做出响应的平均时间。例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间(我认为这里应该仅包含处理时间,网络传输时间忽略)
-
并发数:系统同时处理的request / 事务数
-
吞吐量:单位时间内处理的请求数量(通常由QPS与并发数决定);
2、性能测试工具安装
我们将利用wrk工具简单测试下接口的QPS和响应时间,wrk是轻量级的 HTTP 性能测试工具,在这里我们使用wrk简单测试下我们刚写接口的QPS和响应时间
#安装 brew install wrk 复制代码
3、接口性能测试
#以get请求为例 wrk -t4 -c1000 -d30s -T30s --latency http://127.0.0.1:8000/user/welcome\?name\=jack\&age\=38 Running 30s test @ http://127.0.0.1:8000/user/welcome?name=jack&age=38 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 2.48ms 1.15ms 36.87ms 80.11% Req/Sec 24.44k 2.48k 33.65k 72.67% Latency Distribution 50% 2.33ms 75% 2.81ms 90% 3.71ms 99% 6.53ms 2919566 requests in 30.03s, 389.80MB read Socket errors: connect 751, read 89, write 0, timeout 0 Requests/sec: 97228.78 Transfer/sec: 12.98MB 复制代码
解释1:用4个线程来模拟1000个并发连接,整个测试持续30秒,连接超时30秒,打印出请求的延迟统计信息。 wrk 使用异步非阻塞的 io, 并不是用线程去模拟并发连接,因此不需要设置很多的线程 ,一般根据 CPU 的核心数量设置即可。(网络通信不会阻塞线程执行,用很少的线程模拟大量网路连接)
解释2:
- Socket errors socket 错误的数量 - Requests/sec 每秒请求数量,也就是并发能力 - Latency 响应时间 Avg:平均、Max:最大、Stdev:标准差、+/- Stdev: 正负一个标准差占比 复制代码
4、性能说明
-
使用Go写一个简单接口,在我们的开发机器上的QPS大约是9w7左右,哈哈,我们也算写了一个”高性能,低延迟”接口,当然这归功于优秀的go语言的并发处理能力和web框架gin
-
实际的后台接口的延迟不可能这么快,它涉及到业务处理,服务间调用,复杂的网络环境,真实的QPS远远没有这么夸张。
#测试百度首页的QPS和Latency wrk -t4 -c1000 -d30s -T30s https://www.baidu.com Running 30s test @ https://www.baidu.com 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 196.47ms 44.94ms 1.18s 88.71% Req/Sec 310.42 59.95 494.00 69.06% 37095 requests in 30.09s, 548.20MB read Socket errors: connect 754, read 111, write 0, timeout 0 Requests/sec: 1232.74 Transfer/sec: 18.22MB 复制代码
四、小课堂
1、govendor
-
基于
vendor
机制实现的 Go 包依赖管理命令行工具。与原生 vendor 无侵入性融合,也支持从其他依赖管理工具迁移,可以很方便的实现同一个包在不同项目中不同版本、以及无相互侵入的开发和管理。 -
最开始的时候,Go 并没有提供较为妥当的包管理工具。从 1.5 版本开始提供了 vendor 特性,但需要手动设置环境变量
GO15VENDOREXPERIMENT=1
。 -
在执行
go build
或go run
命令时,会按照以下顺序去查找包:- 当前包下的 vendor 目录
- 向上级目录查找,直到找到 src 下的 vendor 目录
- 在 GOROOT 目录下查找
- 在 GOPATH 下面查找依赖包
-
在发布 1.6 版本时,该环境变量的值已经默认设置为 1 了,该值可以使用
go env
命令查看;在发布 1.7 版本时,已去掉该环境变量,默认开启vendor
特性。 -
govendor一些常用命令如下:
//初始化 govendor init //将已被引用且在 $GOPATH 下的所有包复制到 vendor 目录 govendor add +external //仅从 $GOPATH 中复制指定包 govendor add gopkg.in/yaml.v2 //列出代码中所有被引用到的包及其状态 govendor list //列出一个包被哪些包引用 govendor list -v fmt //从远程仓库添加或更新某个包(不会在 $GOPATH 也存一份) govendor fetch golang.org/x/net/context //安装指定版本的包,eg: govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55 govendor fetch golang.org/x/net/context@v1 # Get latest v1.*.* tag or branch. govendor fetch golang.org/x/net/context@=v1 # Get the tag or branch named "v1". 复制代码