写点什么

Golang fasthttp 实践

用户头像
FunTester
关注
发布于: 刚刚

原计划学完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.Requestfunc 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.Requestfunc 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.Requestfunc 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 errorfunc 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 errorfunc 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.Clientfunc 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.ArgsUtilimport com.mocofun.moco.MocoServerimport 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   TestGet2021/10/18 18:56:49 get请求--- PASS: TestGet (0.01s)=== RUN   TestPostJson2021/10/18 18:56:49 post请求json表单--- PASS: TestPostJson (0.00s)=== RUN   TestPostForm2021/10/18 18:56:49 post请求form表单--- PASS: TestPostForm (0.00s)=== RUN   TestGetNor2021/10/18 18:56:49 get请求--- PASS: TestGetNor (0.00s)=== RUN   TestPostJsonNor2021/10/18 18:56:49 post请求json表单--- PASS: TestPostJsonNor (0.00s)=== RUN   TestPostFormNor2021/10/18 18:56:49 post请求form表单--- PASS: TestPostFormNor (0.00s)=== RUN   TestStageJSON
复制代码

Have Fun ~ Tester !

发布于: 刚刚阅读数: 2
用户头像

FunTester

关注

公众号:FunTester,650+原创,欢迎关注 2020.10.20 加入

Have Fun,Tester! 公众号FunTester,坚持原创文章的测试人。 FunTester测试框架作者,DCS_FunTester分布式性能测试框架作者。

评论

发布
暂无评论
Golang fasthttp实践