FunTester Golang fasthttp 实践

FunTester · 2021年10月20日 · 848 次阅读

原计划学完Golang 语言 HTTP 客户端实践之后,就可以继续了,没想到才疏学浅,在搜资料的时候发现除了 Golang SDK 自带的net/http,还有一个更牛的 HttpClient 实现github.com/valyala/fasthttp,据说性能是net/http的 10 倍,我想可能是有点夸张了,后期我会进行测试,以正视听。

github.com/valyala/fasthttp用到了对象池,为了在高性能测试中减少内存的使用,fasthttp 使用了两个对象池(我只看了这俩):requestPool sync.PoolresponsePool sync.Pool,当然 fasthttp 也提供了正常的对象创建 API,后面我在案例中也会写到。

基础 API 演示

首先分享一下基础的用法封装:

PS:这个属于练习版本,所以没写多少注释。

package ft

import (
    "encoding/json"
    "fmt"
    "funtester/task"
    "github.com/valyala/fasthttp"
)


func FastGet(url string, args map[string]interface{}) ([]byte, error) {
    uri := url + "?" + task.ToValues(args)
    _, resp, err := fasthttp.Get(nil, uri)
    if err != nil {
        fmt.Println("请求失败:", err.Error())
        return nil, err
    }
    return resp, err
}

func FastPostForm(url string, args map[string]interface{}) ([]byte, error) {

    // 填充表单,类似于net/url
    params := &fasthttp.Args{}
    for s, i2 := range args {
        sprintf := fmt.Sprintf("%v", i2)
        params.Add(s, sprintf)
    }
    _, resp, err := fasthttp.Post(nil, url, params)
    if err != nil {
        fmt.Println("请求失败:", err.Error())
        return nil, err
    }
    return resp, nil
}

func FastPostJson(url string, args map[string]interface{}) ([]byte, error) {

    req := &fasthttp.Request{}
    req.SetRequestURI(url)

    marshal, _ := json.Marshal(args)
    req.SetBody(marshal)

    // 默认是application/x-www-form-urlencoded,其实无所谓
    req.Header.SetContentType("application/json")
    req.Header.SetMethod("POST")

    resp := &fasthttp.Response{}
    if err := fasthttp.Do(req, resp); err != nil {
        fmt.Println("请求失败:", err.Error())
        return nil, err
    }
    return resp.Body(), nil
}

其中两点主要注意:

  • FastGet、FastPostForm 使用的 fasthttp 提供的默认获取请求的方式,FastPostJson 使用了自定义请求和获取响应的方式
  • 关于请求头中的 req.Header.SetContentType 方法,其实无所谓,服务端都可以解析

高性能 API 演示

下面分享使用更高的性能(基于对象池)的 API 创建请求和获取响应的方式:

package task

import (
    "crypto/tls"
    "encoding/json"
    "fmt"
    "github.com/valyala/fasthttp"
    "log"
    "time"
)

var FastClient fasthttp.Client = fastClient()

// FastGet 获取GET请求对象,没有进行资源回收
// @Description:
// @param url
// @param args
// @return *fasthttp.Request
func FastGet(url string, args map[string]interface{}) *fasthttp.Request {
    req := fasthttp.AcquireRequest()
    req.Header.SetMethod("GET")
    values := ToValues(args)
    req.SetRequestURI(url + "?" + values)
    return req
}

// FastPostJson POST请求JSON参数,没有进行资源回收
// @Description:
// @param url
// @param args
// @return *fasthttp.Request
func FastPostJson(url string, args map[string]interface{}) *fasthttp.Request {
    req := fasthttp.AcquireRequest()
    // 默认是application/x-www-form-urlencoded
    req.Header.SetContentType("application/json")
    req.Header.SetMethod("POST")
    req.SetRequestURI(url)
    marshal, _ := json.Marshal(args)
    req.SetBody(marshal)
    return req
}

// FastPostForm POST请求表单传参,没有进行资源回收
// @Description:
// @param url
// @param args
// @return *fasthttp.Request
func FastPostForm(url string, args map[string]interface{}) *fasthttp.Request {
    req := fasthttp.AcquireRequest()
    // 默认是application/x-www-form-urlencoded
    //req.Header.SetContentType("application/json")
    req.Header.SetMethod("POST")
    req.SetRequestURI(url)
    marshal, _ := json.Marshal(args)
    req.BodyWriter().Write([]byte(ToValues(args)))
    req.BodyWriter().Write(marshal)
    return req
}

// FastResponse 获取响应,保证资源回收
// @Description:
// @param request
// @return []byte
// @return error
func FastResponse(request *fasthttp.Request) ([]byte, error) {
    response := fasthttp.AcquireResponse()
    defer fasthttp.ReleaseResponse(response)
    defer fasthttp.ReleaseRequest(request)
    if err := FastClient.Do(request, response); err != nil {
        log.Println("响应出错了")
        return nil, err
    }
    return response.Body(), nil
}

// DoGet 发送GET请求,获取响应
// @Description:
// @param url
// @param args
// @return []byte
// @return error
func DoGet(url string, args map[string]interface{}) ([]byte, error) {
    req := fasthttp.AcquireRequest()
    defer fasthttp.ReleaseRequest(req) // 用完需要释放资源
    req.Header.SetMethod("GET")
    values := ToValues(args)
    req.SetRequestURI(url + "?" + values)
    resp := fasthttp.AcquireResponse()
    defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源
    if err := FastClient.Do(req, resp); err != nil {
        fmt.Println("请求失败:", err.Error())
        return nil, err
    }
    return resp.Body(), nil
}

// fastClient 获取fast客户端
// @Description:
// @return fasthttp.Client
func fastClient() fasthttp.Client {
    return fasthttp.Client{
        Name:                     "FunTester",
        NoDefaultUserAgentHeader: true,
        TLSConfig:                &tls.Config{InsecureSkipVerify: true},
        MaxConnsPerHost:          2000,
        MaxIdleConnDuration:      5 * time.Second,
        MaxConnDuration:          5 * time.Second,
        ReadTimeout:              5 * time.Second,
        WriteTimeout:             5 * time.Second,
        MaxConnWaitTimeout:       5 * time.Second,
    }
}

测试服务

用的还是moco_FunTester测试框架,脚本如下:

package com.mocofun.moco.main

import com.funtester.utils.ArgsUtil
import com.mocofun.moco.MocoServer
import org.apache.tools.ant.taskdefs.condition.And

class Share extends MocoServer {

    static void main(String[] args) {
        def util = new ArgsUtil(args)
        //                def server = getServerNoLog(util.getIntOrdefault(0,12345))
        def server = getServer(util.getIntOrdefault(0, 12345))
        server.get(both(urlStartsWith("/test"),existArgs("code"))).response("get请求")
        server.post(both(urlStartsWith("/test"), existForm("fun"))).response("post请求form表单")
        server.post(both(urlStartsWith("/test"), existParams("fun"))).response("post请求json表单")
        server.get(urlStartsWith("/qps")).response(qps(textRes("恭喜到达QPS!"), 1))
//        server.response(delay(jsonRes(getJson("Have=Fun ~ Tester !")), 1000))
        server.response("Have Fun ~ Tester !")
        def run = run(server)
        waitForKey("fan")
        run.stop()
    }
}

Golang 单元测试

第一次写 Golang 单测,有点不适应,搞了很久才通。

package test

import (
    "funtester/ft"
    "funtester/task"
    "log"
    "testing"
)

const url = "http://localhost:12345/test"

func args() map[string]interface{} {
    return map[string]interface{}{
        "code": 32,
        "fun":  32,
        "msg":  "324",
    }
}

func TestGet(t *testing.T) {
    get := task.FastGet(url, args())
    res, err := task.FastResponse(get)
    if err != nil {
        t.Fail()
    }
    v := string(res)
    log.Println(v)
    if v != "get请求" {
        t.Fail()
    }
}

func TestPostJson(t *testing.T) {
    post := task.FastPostJson(url, args())
    res, err := task.FastResponse(post)
    if err != nil {
        t.Fail()
    }
    v := string(res)
    log.Println(v)
    if v != "post请求json表单" {
        t.Fail()
    }
}

func TestPostForm(t *testing.T) {
    post := task.FastPostForm(url, args())
    res, err := task.FastResponse(post)
    if err != nil {
        t.Fail()
    }
    v := string(res)
    log.Println(v)
    if v != "post请求form表单" {
        t.Fail()
    }
}

func TestGetNor(t *testing.T) {
    res, err := ft.FastGet(url, args())
    if err != nil {
        t.Fail()
    }
    v := string(res)
    log.Println(v)
    if v != "get请求" {
        t.Fail()
    }
}

func TestPostJsonNor(t *testing.T) {
    res, err := ft.FastPostJson(url, args())
    if err != nil {
        t.Fail()
    }
    v := string(res)
    log.Println(v)
    if v != "post请求json表单" {
        t.Fail()
    }
}

func TestPostFormNor(t *testing.T) {
    res, err := ft.FastPostForm(url, args())
    if err != nil {
        t.Fail()
    }
    v := string(res)
    log.Println(v)
    if v != "post请求form表单" {
        t.Fail()
    }
}

测试报告

用的自带的控制台输出内容:

=== RUN   TestGet
2021/10/18 18:56:49 get请求
--- PASS: TestGet (0.01s)
=== RUN   TestPostJson
2021/10/18 18:56:49 post请求json表单
--- PASS: TestPostJson (0.00s)
=== RUN   TestPostForm
2021/10/18 18:56:49 post请求form表单
--- PASS: TestPostForm (0.00s)
=== RUN   TestGetNor
2021/10/18 18:56:49 get请求
--- PASS: TestGetNor (0.00s)
=== RUN   TestPostJsonNor
2021/10/18 18:56:49 post请求json表单
--- PASS: TestPostJsonNor (0.00s)
=== RUN   TestPostFormNor
2021/10/18 18:56:49 post请求form表单
--- PASS: TestPostFormNor (0.00s)
=== RUN   TestStageJSON

Have Fun ~ Tester !

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册