Go语言 基于gin框架从0开始构建一个bbs server(七)- 接口文档,限流,单元测试,压测
今天这篇文章涉及到的编码内容很少,主要是介绍一个项目上线之前 需要做哪些工作,新手可以看看这篇文章以后心里有个底,有需要的话 这文章里面每一个方向 都值得花时间 深入搞一下
生成接口文档
使用gin框架的 swagger gin swagger
具体写注释 可以参照 注释api
首先要 安装
go install github.com/swaggo/swag/cmd/swag@latest
其次 我们就可以参照文档 来写注释了
就以我们的登录接口为例
可以定义一组专门的struct 来代表参数 当然你也可以直接用项目里的那些成熟的struct 都可以
package controllers
//c.JSON(http.StatusOK, &Response{
//Code: CodeSuccess,
//Msg: CodeSuccess.Msg(),
//Data: data,
//})
type _ResponseLogin struct {
Code int64 `json:"code"` // 业务状态响应码
Message string `json:"message"` //提示信息
Data string `json:"data"` // token
}
type _RequestLogin struct {
// 用户名
Username string `json:"username"`
//密码
Password string `json:"password"`
}
// LoginHandler 用户登录接口
// @Router /api/v1/login [post]
// @Summary 登录接口
// @Accept application/json
// @Produce application/json
// @Param login body _RequestLogin true "需要上传的json"
// @Success 200 {object} _ResponseLogin
func LoginHandler(c *gin.Context) {
p := new(models.ParamLogin)
if err := c.ShouldBindJSON(p); err != nil {
zap.L().Error("LoginHandler with invalid param", zap.Error(err))
// 因为有的错误 比如json格式不对的错误 是不属于validator错误的 自然无法翻译,所以这里要做类型判断
errs, ok := err.(validator.ValidationErrors)
if !ok {
ResponseError(c, CodeInvalidParam)
} else {
ResponseErrorWithMsg(c, CodeInvalidParam, removeTopStruct(errs.Translate(trans)))
}
return
}
// 业务处理
token, err := logic.Login(p)
if err != nil {
// 可以在日志中 看出 到底是哪些用户不存在
zap.L().Error("login failed", zap.String("username", p.UserName), zap.Error(err))
if errors.Is(err, mysql.WrongPassword) {
ResponseError(c, CodeInvalidPassword)
} else {
ResponseError(c, CodeServerBusy)
}
return
}
ResponseSuccess(c, token)
}
在main函数 这里 不要忘记加一些基本的说明
写好了以后 就可以用swag init 来生成文档了
默认是生成在这个路径下:
然后去我们生成路由的文件哪里 先import
gs "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
_ "go_web_app/docs" // 千万不要忘了导入把你上一步生成的docs
然后添加一下对应的路由
//swagger 接口文档
// http://localhost:你的端口号/swagger/index.html 可以看到接口文档
r.GET("/swagger/*any", gs.WrapHandler(swaggerFiles.Handler))
然后跑起来看一下:
还是挺方便的
另外一定要注意对于swagger里面使用的 struct来说 首字母 一定要大写,否则 swag展示不出来正确的参数
最后就是我们还可以修改一下air的配置 让他支持 文档修改以后 自动重启web服务,很方便,要注意的是命令的变化
单元测试
其实就是新建一个 xxx_test.go 的文件 然后直接跑就行了
package controllers
import (
"bytes"
"encoding/json"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
func TestCreatePostHandler(t *testing.T) {
gin.SetMode(gin.TestMode)
url := "/api/v1/post"
r := gin.Default()
r.POST(url, CreatePostHandler)
w := httptest.NewRecorder()
body := `{
"title":"新增呢2",
"content":"vivo比华1231为好多了",
"community_id":2
}`
req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader([]byte(body)))
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
// 判断响应中 是不是包含了 未登录的 错误信息
assert.Contains(t, w.Body.String(), "未登录")
// 也可以继续用下面的方法来判定
res := new(Response)
err := json.Unmarshal(w.Body.Bytes(), res)
if err != nil {
t.Fatalf("json unmarshal failed: %v", err)
}
assert.Equal(t, res.Code, CodeNoLogin)
}
这里唯一要注意的是,如果你是命令行跑,最好是直接跑某个包下面的,否则会很麻烦,因为你的test代码里面 引用不到其他文件里的代码。
压力测试
压力测试 我们需要 知道下面几个概念
响应时间RT 吞吐量 qps tps 并发连接数
这5个指标 大家可以自行百度查询对应的含义
Apache Bench
apache 出的 压测工具
wrk
开源的压测工具,很好用,通过lua脚本来编写复杂的场景
可以验证下我们的本机服务
wrk -t8 -c100 -d30s --latency http://127.0.0.1:8016/api/v1/postlist?pageSize=10
限流
uber的这个很好理解,其实就是 不管你输入有多大,但是我输出始终就是那么多,很像以前小时候打酱油的时候,瓶子口那里插个漏斗,然后师傅往漏斗里面灌就行了。
但是这个有一个缺点就是 如果突发性的场景 可能不太适合,比如12点来个秒杀活动? 访问量大增,结果这个时候漏斗把大部分请求 都暂时挡在外面了,等过会轮到执行的时候 秒杀都结束了。
令牌桶限流算法
这个很好理解,就是你一开始设计一个令牌桶,比如里面做1000个牌,每个请求来的时候 如果有牌子 就直接去处理请求,没有牌子 就等一下。
这种就比之前的漏斗模型 要好很多,不会像漏斗算法那样 那么粗暴
在gin框架中 使用限流中间件
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/juju/ratelimit"
"net/http"
"time"
)
// RateLimitMiddleware 每fillInterval 秒 自动添加 cap 个数的令牌 注意参数 要用Time.Second 否则就是2ns了
func RateLimitMiddleware(fillInterval time.Duration, cap int64) func(c *gin.Context) {
bucket := ratelimit.NewBucket(fillInterval, cap)
return func(c *gin.Context) {
// 如果取不到令牌 就直接返回吧
if bucket.TakeAvailable(1) == 0 {
c.String(http.StatusOK, "rate limit")
c.Abort()
return
}
c.Next()
}
}
性能统计
其实go语言已经自带了很不错的性能统计工具了,这里我们因为使用gin框架,为了方便 就直接用gin下的性能统计工具 大同小异 都是一样的
"github.com/gin-contrib/pprof"
然后在我们注册路由的地方 加上一行代码就可以了:
然后开启我们的web服务
访问 如下地址 即可(端口号就是你的server)
http://localhost:8016/debug/pprof/
这个就是告诉你 提供了哪些分析
然后我们就可以在命令行中 分析了
终端输入:
go tool pprof http://127.0.0.1:8016/debug/pprof/profile
然后等待30s(你可以在这30s内访问你的server 试试看)
30s结束以后:
top3 就可以查看 这段时间内 耗时最长的函数
当然你也可以用图形化的方式 来查看
brew install graphviz
然后 再在输入框内 输入web 就可以直接看到svg图片了很直观
也可以利用 go torch 火焰图来看。
转载自:https://juejin.cn/post/7038188270806630436