zoukankan      html  css  js  c++  java
  • Golang fasthttp实践

    原计划学完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 !

  • 相关阅读:
    字符串与数组的相互转换
    临时笔记-react实战
    临时笔记-react-router
    vuejs上传图片| table的data更新了,但插槽的数据不能及时更新
    Intellij IDEA软件使用教程
    Git软件使用教程
    阿里程序员常用的 15 款开发工具
    Office后缀含义
    Project软件使用教程
    PowerDesigner软件使用教程
  • 原文地址:https://www.cnblogs.com/FunTester/p/15428959.html
Copyright © 2011-2022 走看看