fasthttp 据说是目前golang性能最好的http库,相对于自带的net/http,性能说是有10倍的提升,具体介绍可以看看官方介绍: valyala/fasthttp
正好最近需要用到,但是发现网上的资料也不是很多,特别是关于client模块的就更少了,只有一些翻译成中文的文档,于是乎就把关于client的代码研究了一下,总结了一些比较简单的使用方法,测试对比net/http是有一定程度的提升,如果需要用到http client似乎fasthttp也是一个不错的选择,当然fasthttp也可以用来做http服务的,不过着并不在此次研究范围内。
顺便也提下他的不足之处吧: 一个是他目前还没有支持http2, 一个是不支持WebSocket,但是WebSocket貌似已经有第三方库的支持了。
fasthttp截至目前为止的todo list:
api文档:https://godoc.org/github.com/valyala/fasthttp#RequestCtx
照例先来一个简单例子:
import (
"fmt"
"github.com/valyala/fasthttp"
)
//相应请求的函数 RequestCtx 传递数据
func testhandle(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "hello world")
}
func main() {
if err := fasthttp.ListenAndServe(":8001", testhandle); err != nil {
fmt.Println("start fasthttp fail", err.Error())
}
}
浏览器访问 http://127.0.0.1:8001/ 就可以看到hello world
net/http 提供 http.ServeMux 实现路由服务,但是匹配规则简陋,功能很简单,基本不会使用。fasthttp 吸取教训,默认没有提供路由支持。因此使用第三方的 fasthttp 的路由库 fasthttprouter 来辅助路由实现:
import (
"fmt"
"github.com/buaazp/fasthttprouter"
"github.com/valyala/fasthttp"
)
//路由函数没有变,可以通过UserValue方法获取路由路径
func testhandle(ctx *fasthttp.RequestCtx) {
fmt.Fprintf(ctx, "hello world--> %s!\n", ctx.UserValue("name"))
}
func main() {
router := fasthttprouter.New()
//“/:name”为url中的匹配字符名称,与UserValue函数内的参数对应
router.GET("/:name", testhandle)
if err := fasthttp.ListenAndServe(":8001", router.Handler); err != nil {
fmt.Println("start fasthttp fail", err.Error())
}
}
*RequestCtx 综合 http.Request 和 http.ResponseWriter 的操作,可以更方便的读取和返回数据。
将相应函数改为:
func texthandle(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("text/html") // 记得添加 Content-Type:text/html,否则都当纯文本返回
fmt.Fprintf(ctx, "Method:%s
", ctx.Method())
fmt.Fprintf(ctx, "URI:%s
", ctx.URI())
fmt.Fprintf(ctx, "RemoteAddr:%s
", ctx.RemoteAddr())
fmt.Fprintf(ctx, "UserAgent:%s
", ctx.UserAgent())
fmt.Fprintf(ctx, "Header.Accept:%s
", ctx.Request.Header.Peek("Accept"))
}
fmt.Fprintf(ctx, "IP:%s
", ctx.RemoteIP())
fmt.Fprintf(ctx, "Host:%s
", ctx.Host())
fmt.Fprintf(ctx, "ConnectTime:%s
", ctx.ConnTime()) // 连接收到处理的时间
fmt.Fprintf(ctx, "IsGET:%v
", ctx.IsGet()) // 类似有 IsPOST, IsPUT 等
我忽略了很多错误处理,在实际使用中,千万千万要处理掉错误。
fmt.Println(string(ctx.PostBody())) //打印请求体
//ctx.Request.BodyGunzip() 获取Gzip个数数据
getpost := ctx.QueryArgs()
// 127.0.0.1:8001/text?aaa=123&aaa=haha
fmt.Fprintf(ctx, "post aaa first = %s
", getpost.Peek("aaa")) //只获取第一个值
fmt.Fprintf(ctx, "post aaa all = %s
", bytes.Join(getpost.PeekMulti("aaa"), []byte(","))) //获取全部
getform := ctx.FormValue("aaa")
fmt.Fprintf(ctx, "form aaa = %s", getform)
getform, _ := ctx.FormFile("aaa")
file, _ := getform.Open()
defer file.Close()
ctx.SetContentType("image/jpeg")
filel, _ := os.OpenFile("./haha.jpg", os.O_CREATE|os.O_WRONLY, 0644)
defer filel.Close()
io.Copy(filel, file)
错误标志,字符串 推送
ctx.WriteString("hello,fasthttp")
// 因为实现不同,fasthttp 的返回内容不是即刻返回的
// 不同于标准库,添加返回内容后设置状态码,也是有效的
ctx.SetStatusCode(404)
// 返回的内容也是可以获取的,不需要标准库的用法,需要自己扩展 http.ResponseWriter
fmt.Println(string(ctx.Response.Body()))
文件推送
func httpHandle(ctx *fasthttp.RequestCtx) {
ctx.SendFile("abc.txt")
}
不推荐RequestCtx 复用
Rate Limiter 限流器
限流器模块提供了到限流器代码包的端点适配器。限流器对服务端和客户端同等生效,使用限流器可以强制进、出请求量在阈值以下。使用限流器能限制访问后端的流量,起到一个保护作用,被限制的流量,可以根据具体的业务逻辑去处理,直接返回错误或者返回默认值。
go-kit的限流器模块是封装了golang自带的golang.org/x/time/rate包来实现的,提供了两种限流:
// NewDelayingLimiter是go-kit实现的ratelimiter,我们可以直接使用。
func NewDelayingLimiter(limit ratelimit.Waiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if err := limit.Wait(ctx); err != nil {
return nil, err
}
return next(ctx, request)
}
}
}
使用也很简单,下面看代码
// 如何使用此中间件
import (
"golang.org/x/time/rate"
"github.com/go-kit/kit/ratelimit"
)
limiter := rate.NewLimiter(rate.Every(time.Second * 1), 1)
GetSpecEndpoint = ratelimit.NewDelayingLimiter(limiter)(GetSpecEndpoint)
getSpecHandler := httptransport.NewServer(
GetSpecEndpoint,
decodes.GetSpecDecode,
encodes.GetSpecEncode,
)
未完待续。。。