The source address

Today’s article involves very little coding content, mainly introduces what needs to be done before a project goes online, beginners can read this article to have a bottom in mind, if necessary, each direction in this article is worth taking the time to in-depth study

Generate interface documentation

github.com/swaggo/swag

Gin Swagger gin Swagger using gin framework

You can refer to the annotation API for writing comments

The first thing is to install

go install github.com/swaggo/swag/cmd/swag@latest
Copy the code

Secondly, we can refer to the documentation to write comments

Take our login interface for example

You can define a special set of structs to represent parameters or you can just use any of the mature structs in your project

package controllers //c.JSON(http.StatusOK, &Response{ //Code: CodeSuccess, //Msg: CodeSuccess.Msg(), //Data: data, //}) type _ResponseLogin struct {Code int64 'json:" Code "' // Service status response Code Message string 'json:" Message "' // Prompt Message Data string 'json:"data"' // token} type _RequestLogin struct {// Username Username string 'json:" Username "' // Password string `json:"password"` }Copy the code
// LoginHandler User login interface // @router/API /v1/login [POST] // @summary login interface // @Accept Application /json // @produce Application /json // @param login body _RequestLogin true "// @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)) // The json format Error is not a validator Error and cannot be translated. So here to do type judgment is incremented, ok: = err. (the validator. ValidationErrors) if! ok { ResponseError(c, CodeInvalidParam) } else { ResponseErrorWithMsg(c, CodeInvalidParam, RemoveTopStruct (errs.translate (trans)))} return} // Business processing token, err := logic.login (p) if err! Zap.l ().error ("login failed", zap.string ("username", p.user name), zap.Error(err)) if errors.Is(err, mysql.WrongPassword) { ResponseError(c, CodeInvalidPassword) } else { ResponseError(c, CodeServerBusy) } return } ResponseSuccess(c, token) }Copy the code

Don’t forget to add some basic instructions to the main function

Once you’ve written it, you can use Swag init to generate the document

The default is generated in this path:

Then go to the file where we generated the route import first

gs "github.com/swaggo/gin-swagger" "github.com/swaggo/gin-swagger/swaggerFiles" _ "go_web_app/docs" // Don't forget to import the docs you generated in the previous stepCopy the code

Then add the corresponding route

R.git ("/swagger/*any", gs.WrapHandler(swaggerFiles.Handler))Copy the code

Then run and take a look:

It’s kind of convenient

It is also important to note that the initial of the struct used in Swagger must be uppercase, otherwise swag will not display the correct parameter

Finally, we can also modify the air configuration to enable it to automatically restart the Web service after the document modification, which is very convenient, but pay attention to the command change

Unit testing

Just create a new xxx_test.go file and run

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":" new ", "Community_id ":2} 'req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader([]byte(body))) r.ServeHTTP(w, req) assert.Equal(t, 200, W.code) // Determine if the response Contains an unlogged error message 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) }Copy the code

The only caveat here is that if you are running on the command line, it is best to run directly under a package, otherwise it will be troublesome because your test code will not reference code from other files.

Pressure test

Here are a few concepts we need to know about stress testing

Response time RT Throughput QPS TPS Number of concurrent connections

These 5 indicators we can search the corresponding meaning of baidu

Apache Bench

Apache out of the pressure measurement tool

wrk

Open source pressure tools, very easy to use, through Lua scripts to write complex scenarios

You can verify our native service

WRK – t8 – c100 – d30s – latency at http://127.0.0.1:8016/api/v1/postlist? pageSize=10

Current limiting

This is easy to understand for Uber. In fact, no matter how big your input is, my output is always the same. It is just like when I was a child getting soy sauce, I inserted a funnel into the mouth of the bottle and the master poured it into the funnel.

But the downside of this is that it might not be appropriate to have a sudden scene, like a 12 o ‘clock seconds kill? The traffic was so high that the funnel blocked most of the requests for a while, and by the time it was time to execute them, the kill was over.

Uber’s funnel limiting algorithm

Token bucket traffic limiting algorithm

This is easy to understand, you design a token bucket, say 1000 cards in it, each request comes in if there is a sign to go straight to the request, no sign to wait.

This is much better than the previous funnel model, not as rough as the funnel algorithm

Token bucket algorithm implementation

Use flow limiting middleware in gin framework

package middleware import ( "github.com/gin-gonic/gin" "github.com/juju/ratelimit" "net/http" "time" ) // RateLimitMiddleware(fillInterval) specifies the number of caps to be added every fillInterval seconds. Use time. Second or 2ns time.Duration, cap int64) func(c *gin.Context) { bucket := ratelimit.NewBucket(fillInterval, Cap) return func(c *gin.Context) {// if bucket.TakeAvailable(1) == 0 {c.tring (http.statusok, "rate limit") c.Abort() return } c.Next() } }Copy the code

Performance statistics

In fact, go language already has a very good performance statistics tool, here we use gin framework, for convenience directly under the gin performance statistics tool is almost the same with minor differences

"github.com/gin-contrib/pprof"
Copy the code

Then add a line of code where we registered the route:

Then start our Web service

Access the following address (the port number is your server)

http://localhost:8016/debug/pprof/

This is just telling you what analysis is provided

Then we can analyze it on the command line

Terminal input:

Go tool pprof http://127.0.0.1:8016/debug/pprof/profile

Then wait 30 seconds (you can try to access your server during this 30 seconds)

After 30s:

Top3 shows the functions that took the longest time during that time

Of course, you can also view it graphically

brew install graphviz

Then type web in the input field and you can see the SVG image directly

You can also use the Go Torch flame diagram.