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 !

  • 相关阅读:
    leetcode 347. Top K Frequent Elements
    581. Shortest Unsorted Continuous Subarray
    leetcode 3. Longest Substring Without Repeating Characters
    leetcode 217. Contains Duplicate、219. Contains Duplicate II、220. Contains Duplicate、287. Find the Duplicate Number 、442. Find All Duplicates in an Array 、448. Find All Numbers Disappeared in an Array
    leetcode 461. Hamming Distance
    leetcode 19. Remove Nth Node From End of List
    leetcode 100. Same Tree、101. Symmetric Tree
    leetcode 171. Excel Sheet Column Number
    leetcode 242. Valid Anagram
    leetcode 326. Power of Three
  • 原文地址:https://www.cnblogs.com/FunTester/p/15428959.html
Copyright © 2011-2022 走看看