一、 基本介绍
当使用go进行http或者https请求时,我们立刻可以想到的就是官方提供的net/http包。而valyala公司编写推出了一种新的http包——fasthttp,并且其宣称比net/http要快十倍。正如其名,快是valyala公司非常看重的点,经过几次的更新迭代,该包也越来越多地被使用。
二、 相互对比
net/http:
说明:在net/http包中,如果服务器收到一个请求,就会accept一个conn,然后创建一个协程(worker)来处理该请求,期间worker会一直保持,请求处理完后worker会被删除,即连接关闭,请求完成。
fasthttp:
说明:在fasthttp包中,如果一个服务器监听到一个请求,那么Workerpool会从ready状态的channel中取出一个。被取出之后,这个请求conn将会被accept到该channel中。worker goroutine检测到channel有conn之后,会接收并处理该请求。请求处理完成之后,worker goroutine并不会退出,而是将该channel重新放回到Workerpool中,等待下一次的复用。
实际上,fasthttp的实现使用到了池的概念,就像线程池、连接池一样,只不过其是协程池的实现。当有请求来到时,会从池子中取出空闲的协程来处理,处理完成后不会立刻退出协程,而是交还给pool来管理,这样就实现了复用协程,这也是速度比net/http快的一个重要原因。
三、 安装
go get -u github.com/valyala/fasthttp
四、 使用例子
简单的get请求
url := "https://www.ctyun.cn/developer"
code, body, err := fasthttp.Get(nil, url)
if err != nil {
fmt.Println("请求异常: ", err.Error())
return err
}
if code != fasthttp.StatusOK {
fmt.Println("请求错误: ", code)
return err
}
fmt.Println(body)
简单的post请求
url := "https://www.ctyun.cn/developer"
//定义请求参数
args := fasthttp.Args{}
args.Set("name", "zhans")
args.Set("age", "25")
args.Set("type", "code")
code, body, err := fasthttp.Post(nil, url, &args)
if err != nil {
fmt.Println("请求异常: ", err.Error())
return err
}
if code != fasthttp.StatusOK {
fmt.Println("请求错误: ", code)
return err
}
fmt.Println(body)
设置请求头/请求体
url := "https://www.ctyun.cn/developer"
//定义请求
req := fasthttp.Request{}
//设置请求url
req.SetRequestURI(url)
//设置请求体
body := []byte(`{"name": "ctyun"}`)
req.SetBody(body)
//设置请求头
req.Header.SetMethod("POST")
//默认是application/x-www-form-urlencoded
req.Header.SetContentType("application/json")
resp := fasthttp.Response{}
client := fasthttp.Client{}
if err := client.Do(&req, &resp); err != nil {
fmt.Println("请求异常: ", err.Error())
return err
}
fmt.Println(resp.Body())
从协程池获取request和response
设置client对象
func getHttpClient() *fasthttp.Client {
return &fasthttp.Client{
// 读超时时间,不设置read超时,可能会造成连接复用失效
ReadTimeout: time.Second * 5,
// 写超时时间
WriteTimeout: time.Second * 5,
// 5秒后,关闭空闲的活动连接
MaxIdleConnDuration: time.Second * 5,
// 当true时,从请求中去掉User-Agent标头
NoDefaultUserAgentHeader: true,
// 当true时,header中的key按照原样传输,默认会根据标准化转化
DisableHeaderNamesNormalizing: true,
//当true时,路径按原样传输,默认会根据标准化转化
DisablePathNormalizing: true,
Dial: (&fasthttp.TCPDialer{
// 最大并发数,0表示无限制
Concurrency: 4096,
// 将 DNS 缓存时间从默认分钟增加到一小时
DNSCacheDuration: time.Hour,
}).Dial,
}
}
get请求
func HttpGet(uri string, arg *fasthttp.Args) (string, error) {
url := "https://www.ctyun.cn/developer"
req, resp := fasthttp.AcquireRequest(), fasthttp.AcquireResponse()
// 最后需要归还req、resp到池中
defer func() {
fasthttp.ReleaseRequest(req)
fasthttp.ReleaseResponse(resp)
}()
req.Header.SetMethod(fasthttp.MethodGet)
req.SetRequestURI(url)
// 设置请求参数
if arg != nil {
req.URI().SetQueryString(arg.String())
}
if err := getHttpClient().Do(req, resp); err != nil {
return "", err
}
return string(resp.Body()), nil
}
post请求(application/x-www-form-urlencoded)
func HttpPostData(uri string, args *fasthttp.Args) (string, error) {
url := "https://www.ctyun.cn/developer"
req, resp := fasthttp.AcquireRequest(), fasthttp.AcquireResponse()
// 最后需要归还req、resp到池中
defer func() {
fasthttp.ReleaseRequest(req)
fasthttp.ReleaseResponse(resp)
}()
req.Header.SetMethod(fasthttp.MethodPost)
req.SetRequestURI(url)
req.Header.SetContentType("application/x-www-form-urlencoded")
// 获取请求体输出流,并将请求参数写入
if _, err := args.WriteTo(req.BodyWriter()); err != nil {
return "", err
}
// 发起请求
if err := getHttpClient().Do(req, resp); err != nil {
return "", err
}
return string(resp.Body()), nil
}
post请求(application/json)
func HttpPostJson(uri string, data []byte) (string, error) {
url := "https://www.ctyun.cn/developer"
req, resp := fasthttp.AcquireRequest(), fasthttp.AcquireResponse()
// 最后需要归还req、resp到池中
defer func() {
fasthttp.ReleaseRequest(req)
fasthttp.ReleaseResponse(resp)
}()
req.Header.SetMethod(fasthttp.MethodPost)
req.SetRequestURI(url)
req.Header.SetContentType("application/json")
req.SetBody(data)
// 发起请求
if err := getHttpClient().Do(req, resp); err != nil {
return "", err
}
return string(resp.Body()), nil
}
五、 总结
fasthttp通过复用来提高了处理的性能和效率,尤其是对服务协程以及内存对象的复用,节省了大量的资源分配成本,同时也大量使用了sync.Pool,资源得到了合理的运用。所以,如果您在使用net/http包的过程中出现了性能瓶颈突破不了的话,不妨考虑一下fasthttp。