什么是 Fasthttp
fasthttp 是 Go 语言生态中性能极致的 HTTP 服务器 / 客户端库,专为高并发、低延迟场景设计(如网关、微服务、压测工具、高 QPS 接口服务)。
- 官方定位:比 Go 标准库 net/http 快 10 倍以上,内存分配极少
- 核心优势:零 / 低内存分配、连接复用、对象池化、原生支持 HTTP/1.1 全特性
- 适用场景:高并发 API 服务、反向代理、网关、压测客户端
- 不适用场景:需要 HTTP/2、HTTP/3、WebSocket 完整扩展(fasthttp 对 WebSocket 支持有限)
Fasthttp 核心原理(为什么这么快?)
fasthttp 的高性能完全围绕减少内存分配、避免 GC、复用资源设计,核心原理如下:
- sync.Pool 对象池化(核心)
- 标准库 net/http 每次请求都会新建 Request/Response 对象,GC 压力极大
- fasthttp 用 sync.Pool 缓存所有高频对象(Request、Response、Ctx、Buffer),请求结束后重置对象并放回池内复用,几乎零内存分配
- 减少系统调用与拷贝
- 原生使用零拷贝技术处理请求体 / 响应体
- 避免 \[\]byte 和 string 频繁转换(fasthttp 大量使用 \[\]byte 处理数据,比 string 更快)
- 连接复用与长连接
- 默认强制开启 HTTP/1.1 长连接,减少 TCP 握手 / 挥手开销
- 优化了 TCP 读写缓冲区,高并发下连接数大幅降低
- 精简的请求上下文
- 用 *fasthttp.RequestCtx 替代标准库的 http.ResponseWriter + *http.Request
- 所有方法直接操作字节,无反射、无多余封装,单请求处理耗时极低
- 高性能路由
- 基于前缀树实现路由匹配,比正则路由快一个数量级
- 支持静态路由、参数路由、通配符路由
环境安装
go
go get github.com/valyala/fasthttp
Fasthttp 完整 API 分类 + 实战示例
按照服务器、客户端、请求 / 响应操作、路由、工具函数五大类,覆盖几乎所有核心 API,每个 API 都配可直接运行的示例
第一部分:FastHTTP 服务器核心 API
ListenAndServe(addr string, handler RequestHandler) error
作用:启动 HTTP 服务器(最基础 API),监听指定地址
go
package main
import (
"github.com/valyala/fasthttp"
)
// 处理器函数:所有请求都会进入这里
func requestHandler(ctx *fasthttp.RequestCtx) {
// 响应文本
ctx.WriteString("Hello Fasthttp!")
}
func main() {
// 启动服务器:监听 8080,使用自定义处理器
err := fasthttp.ListenAndServe(":8080", requestHandler)
if err != nil {
panic(err)
}
}
ListenAndServeTLS(addr, certFile, keyFile string, handler RequestHandler) error
作用:启动 HTTPS 服务器
go
func main() {
// 传入证书和密钥路径
err := fasthttp.ListenAndServeTLS(":443", "server.crt", "server.key", requestHandler)
if err != nil {
panic(err)
}
}
Server 结构体(自定义服务器配置)
作用:精细化配置服务器(超时、最大连接数、工作池)
go
func main() {
server := &fasthttp.Server{
Handler: requestHandler, // 处理器
ReadTimeout: 5 * time.Second, // 读取超时
WriteTimeout: 5 * time.Second, // 写入超时
MaxConns: 100000, // 最大连接数
IdleTimeout: 10 * time.Second, // 长连接空闲超时
}
// 启动服务器
err := server.ListenAndServe(":8080")
if err != nil {
panic(err)
}
}
Serve(l net.Listener, handler RequestHandler) error
作用:使用自定义 net.Listener 启动服务器
go
func main() {
ln, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
// 基于自定义 listener 启动
err = fasthttp.Serve(ln, requestHandler)
if err != nil {
panic(err)
}
}
第二部分:RequestCtx 核心 API(请求处理必备)
RequestCtx 是 fasthttp 最核心的对象,封装了请求 + 响应 + 上下文。
获取请求方法、路径、URI
go
func handler(ctx *fasthttp.RequestCtx) {
// 1. 获取请求方法 GET/POST/PUT...
method := ctx.Method() // []byte
println("方法:", string(method))
// 2. 获取请求路径
path := ctx.Path()
println("路径:", string(path))
// 3. 获取完整请求URI
uri := ctx.URI()
println("完整URI:", string(uri.FullURI()))
}
响应数据(Write/WriteString/SetBody)
go
func handler(ctx *fasthttp.RequestCtx) {
// 写入字节
ctx.Write([]byte("字节响应"))
// 写入字符串(最常用)
ctx.WriteString("字符串响应")
// 直接设置响应体
ctx.SetBody([]byte("直接设置body"))
}
设置响应状态码
go
func handler(ctx *fasthttp.RequestCtx) {
// 200 成功
ctx.SetStatusCode(fasthttp.StatusOK)
// 404 未找到
// ctx.SetStatusCode(fasthttp.StatusNotFound)
// 500 服务端错误
// ctx.SetStatusCode(fasthttp.StatusInternalServerError)
}
请求 / 响应头操作
go
func handler(ctx *fasthttp.RequestCtx) {
// ✅ 获取请求头
ua := ctx.Request.Header.Peek("User-Agent")
println("UA:", string(ua))
// ✅ 设置响应头
ctx.Response.Header.Set("Server", "Fasthttp-Server")
ctx.Response.Header.Add("X-Custom", "test")
// ✅ 设置 Content-Type
ctx.SetContentType("application/json; charset=utf-8")
}
获取 GET/POST 参数
go
func handler(ctx *fasthttp.RequestCtx) {
// GET 参数:?name=张三
name := ctx.QueryArgs().Peek("name")
// POST 表单参数
age := ctx.PostArgs().Peek("age")
// 获取参数(自动兼容 GET/POST)
id := ctx.FormValue("id")
println(string(name), string(age), string(id))
}
获取请求体
go
func handler(ctx *fasthttp.RequestCtx) {
// 获取原始请求体
body := ctx.PostBody()
println("请求体:", string(body))
}
重定向
go
func handler(ctx *fasthttp.RequestCtx) {
// 302 临时重定向
ctx.Redirect("https://www.baidu.com", fasthttp.StatusFound)
// 301 永久重定向
// ctx.Redirect("/new", fasthttp.StatusMovedPermanently)
}
错误响应
go
func handler(ctx *fasthttp.RequestCtx) {
// 快速返回错误
ctx.Error("参数错误", fasthttp.StatusBadRequest)
}
获取客户端 IP
go
func handler(ctx *fasthttp.RequestCtx) {
ip := ctx.RemoteIP()
println("客户端IP:", ip.String())
}
第三部分:路由 API(高性能路由)
fasthttp 内置高性能路由,支持静态路由、参数路由、通配符。
基础路由注册
go
func main() {
router := func(ctx *fasthttp.RequestCtx) {
// 路径匹配
switch string(ctx.Path()) {
case "/":
ctx.WriteString("首页")
case "/user":
ctx.WriteString("用户页")
default:
ctx.Error("404 未找到", fasthttp.StatusNotFound)
}
}
fasthttp.ListenAndServe(":8080", router)
}
官方推荐:router.Router 高级路由
需要安装路由扩展:
go
go get github.com/fasthttp/router
go
package main
import (
"github.com/fasthttp/router"
"github.com/valyala/fasthttp"
)
func main() {
r := router.New()
// 静态路由
r.GET("/", homeHandler)
// 参数路由 /user/123
r.GET("/user/{id}", userHandler)
// 通配符路由
r.GET("/files/*filepath", filesHandler)
// 启动服务器
fasthttp.ListenAndServe(":8080", r.Handler)
}
func homeHandler(ctx *fasthttp.RequestCtx) {
ctx.WriteString("首页")
}
func userHandler(ctx *fasthttp.RequestCtx) {
// 获取路径参数
id := ctx.UserValue("id").(string)
ctx.WriteString("用户ID:" + id)
}
func filesHandler(ctx *fasthttp.RequestCtx) {
fp := ctx.UserValue("filepath").(string)
ctx.WriteString("文件路径:" + fp)
}
第四部分:Fasthttp 客户端 API
fasthttp 客户端同样比标准库 http.Client 快 5-10 倍。
快速 GET 请求
go
func main() {
// 快速GET:返回状态码、响应体、错误
status, body, err := fasthttp.Get(nil, "https://www.baidu.com")
if err != nil {
panic(err)
}
println("状态码:", status)
println("响应体:", string(body))
}
快速 POST 请求
go
func main() {
// POST 表单数据
args := fasthttp.AcquireArgs()
defer fasthttp.ReleaseArgs(args)
args.Set("username", "test")
args.Set("password", "123456")
status, body, err := fasthttp.Post(nil, "http://127.0.0.1:8080/login", args)
if err != nil {
panic(err)
}
println(status, string(body))
}
自定义客户端(精细化配置)
go
func main() {
// 创建客户端
client := &fasthttp.Client{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
}
// 构造请求
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req)
req.SetRequestURI("http://127.0.0.1:8080")
req.Header.SetMethod("GET")
// 构造响应
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)
// 发送请求
err := client.Do(req, resp)
if err != nil {
panic(err)
}
println("响应:", string(resp.Body()))
}
池化请求 / 响应对象
go
// 必须使用 Acquire/Release 避免内存泄漏
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req) // 用完放回池
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)
第五部分:工具类 API
URI 解析
go
func handler(ctx *fasthttp.RequestCtx) {
u := ctx.URI()
println("协议:", string(u.Scheme()))
println("主机:", string(u.Host()))
println("路径:", string(u.Path()))
println("查询参数:", string(u.QueryString()))
}
字节 / 字符串工具
go
// 安全转换 []byte <-> string(零拷贝)
b := []byte("test")
s := fasthttp.StringToByte("test")
b2s := fasthttp.ByteToString(b)
状态码常量
fasthttp 内置所有 HTTP 状态码:
go
fasthttp.StatusOK // 200
fasthttp.StatusNotFound // 404
fasthttp.StatusBadRequest // 400
fasthttp.StatusInternalServerError // 500
Gzip 压缩
go
rawHandler := func(ctx *fasthttp.RequestCtx) {
ctx.WriteString("long text content...")
}
// 自动压缩响应
compressHandler := fasthttp.CompressHandler(rawHandler)
fasthttp.ListenAndServe(":8080", compressHandler)
静态文件服务
go
// 静态文件目录 ./static,访问 /static/*
staticHandler := fasthttp.FSHandler("./static", 0)
fasthttp.ListenAndServe(":8080", staticHandler)
表单提交 POST
go
args := fasthttp.Args{}
args.Set("username", "admin")
args.Set("password", "123456")
req.SetBody(args.QueryString())
req.Header.SetContentType("application/x-www-form-urlencoded")
PipelineClient 批量并发请求(高吞吐)适合批量调用第三方接口
go
pipeline := fasthttp.PipelineClient{
Client: client,
}
// 批量发送多个req
var reqs []*fasthttp.Request
// ...填充reqs
resps, err := pipeline.Do(reqs...)
Args 参数工具(URL 参数、表单)
go
var args fasthttp.Args
args.Set("id", "100")
args.Set("name", "zhangsan")
// 拼接成 query string
qs := args.QueryString() // id=100&name=zhangsan
URI 工具
go
u := fasthttp.AcquireURI()
u.SetScheme("https")
u.SetHost("baidu.com")
u.SetPath("/search")
u.QueryArgs().Set("wd", "go")
fmt.Println(string(u.FullURI())) // https://baidu.com/search?wd=go
fasthttp.ReleaseURI(u)
完整 API 清单(速查)
- 服务器
ListenAndServe
ListenAndServeTLS
Serve
ServeTLS - Server 结构体
RequestCtx
Method(), Path(), URI()
Write(), WriteString(), SetBody()
SetStatusCode(), SetContentType()
Request.Header, Response.Header
QueryArgs(), PostArgs(), FormValue()
PostBody(), RemoteIP()
Redirect(), Error()
UserValue()(路由参数) - 客户端
Get(), Post(), Put(), Delete()
Client{}
AcquireRequest(), ReleaseRequest()
AcquireResponse(), ReleaseResponse()
AcquireArgs(), ReleaseArgs() - 路由
router.New()
GET/POST/PUT/DELETE 注册
{param} 路径参数
- 通配符
- 工具
URI 解析
字节 / 字符串零拷贝转换
HTTP 状态码常量
总结
- fasthttp 核心优势:对象池化、零内存分配、低 GC、高并发,性能远超标准库 net/http
- 核心原理:sync.Pool 复用对象 + 零拷贝 + 长连接 + 精简上下文
- 使用规范:必须用 Acquire/Release 管理请求 / 响应对象,避免内存泄漏
- 适用场景:高并发 API、网关、代理、压测;不适合 HTTP/2、WebSocket 重度使用
在实际项目中使用 fasthttp
项目目录
fast-api/
├── cmd
│ └── server
│ └── main.go
├── config
│ ├── app.yaml
│ └── config.go
├── internal
│ ├── handler
│ │ ├── demo_handler.go
│ │ └── user_handler.go
│ ├── middleware
│ │ ├── auth.go
│ │ ├── cors.go
│ │ ├── logger.go
│ │ └── recovery.go
│ ├── repo
│ │ └── user_repo.go
│ └── service
│ ├── demo_service.go
│ └── user_service.go
├── pkg
│ ├── httpclient
│ │ └── client.go
│ ├── resp
│ │ └── response.go
│ └── validator
│ └── validate.go
├── router
│ ├── group.go
│ └── router.go
├── go.mod
└── go.sum
go.mod
go
module fast-api
go 1.21
require (
github.com/fasthttp/router v1.4.0
github.com/go-playground/validator/v10 v10.15.0
github.com/spf13/viper v1.18.2
github.com/valyala/fasthttp v1.51.0
)
config/app.yaml
go
app:
name: fast-api
port: 8080
read_timeout: 3000
write_timeout: 3000
idle_timeout: 10000
max_conns: 100000
shutdown_timeout: 10
auth_token: "admin123456"
config/config.go
go
package config
import (
"github.com/spf13/viper"
"log"
"time"
)
var Conf AppConfig
type AppConfig struct {
App struct {
Name string
Port string
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxConns int
}
ShutdownTimeout time.Duration
AuthToken string
}
func Init() {
viper.SetConfigFile("config/app.yaml")
if err := viper.ReadInConfig(); err != nil {
log.Fatalf("read config err: %v", err)
}
Conf.App.Name = viper.GetString("app.name")
Conf.App.Port = viper.GetString("app.port")
Conf.App.ReadTimeout = viper.GetDuration("app.read_timeout") * time.Millisecond
Conf.App.WriteTimeout = viper.GetDuration("app.write_timeout") * time.Millisecond
Conf.App.IdleTimeout = viper.GetDuration("app.idle_timeout") * time.Millisecond
Conf.App.MaxConns = viper.GetInt("app.max_conns")
Conf.ShutdownTimeout = viper.GetDuration("shutdown_timeout") * time.Second
Conf.AuthToken = viper.GetString("auth_token")
}
pkg/resp/response.go
go
package resp
import (
"encoding/json"
"github.com/valyala/fasthttp"
)
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data,omitempty"`
TraceID string `json:"traceId"`
}
const (
CodeSuccess = 0
CodeFail = 400
CodeAuth = 401
CodeServer = 500
)
func JSON(ctx *fasthttp.RequestCtx, code int, msg string, data any) {
ctx.SetContentType("application/json; charset=utf-8")
traceID := ctx.UserValue("traceId").(string)
_ = json.NewEncoder(ctx).Encode(Response{
Code: code,
Msg: msg,
Data: data,
TraceID: traceID,
})
}
func Success(ctx *fasthttp.RequestCtx, data any) {
JSON(ctx, CodeSuccess, "success", data)
}
func Fail(ctx *fasthttp.RequestCtx, msg string) {
JSON(ctx, CodeFail, msg, nil)
}
func AuthFail(ctx *fasthttp.RequestCtx) {
JSON(ctx, CodeAuth, "token invalid", nil)
}
func ServerErr(ctx *fasthttp.RequestCtx) {
JSON(ctx, CodeServer, "server internal error", nil)
}
pkg/validator/validate.go
go
package validator
import (
"github.com/go-playground/validator/v10"
)
var validate *validator.Validate
func Init() {
validate = validator.New()
}
func Struct(obj any) error {
return validate.Struct(obj)
}
pkg/httpclient/client.go
go
package httpclient
import (
"encoding/json"
"github.com/valyala/fasthttp"
"log"
)
var client = &fasthttp.Client{
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
}
func Get(url string) ([]byte, error) {
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req)
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)
req.SetRequestURI(url)
req.Header.SetMethod(fasthttp.MethodGet)
if err := client.Do(req, resp); err != nil {
return nil, err
}
return resp.Body(), nil
}
func PostJSON(url string, body any) ([]byte, error) {
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req)
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)
req.SetRequestURI(url)
req.Header.SetMethod(fasthttp.MethodPost)
req.Header.SetContentType("application/json")
_ = json.NewEncoder(req.BodyWriter()).Encode(body)
if err := client.Do(req, resp); err != nil {
log.Printf("http post err: %v", err)
return nil, err
}
return resp.Body(), nil
}
internal/middleware/cors.go
go
package middleware
import "github.com/valyala/fasthttp"
func Cors(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
ctx.Response.Header.Set("Access-Control-Allow-Origin", "*")
ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
ctx.Response.Header.Set("Access-Control-Allow-Headers", "Content-Type,Authorization")
if string(ctx.Method()) == fasthttp.MethodOptions {
ctx.SetStatusCode(fasthttp.StatusOK)
return
}
next(ctx)
}
}
internal/middleware/recovery.go
go
package middleware
import (
"fast-api/pkg/resp"
"github.com/valyala/fasthttp"
"log"
)
func Recovery(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
defer func() {
if err := recover(); err != nil {
log.Printf("panic recovered: %v", err)
resp.ServerErr(ctx)
}
}()
next(ctx)
}
}
internal/middleware/logger.go
go
package middleware
import (
"fast-api/pkg/resp"
"github.com/valyala/fasthttp"
"log"
"math/rand"
"time"
)
func Logger(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
start := time.Now()
// 生成traceId
traceID := randString(16)
ctx.SetUserValue("traceId", traceID)
next(ctx)
cost := time.Since(start)
method := string(ctx.Method())
path := string(ctx.Path())
status := ctx.Response.StatusCode()
ip := ctx.RemoteIP().String()
log.Printf("[%s] %s %s status=%d ip=%s cost=%s", traceID, method, path, status, ip, cost)
}
}
func randString(n int) string {
letters := []rune("0123456789abcdefghijklmnopqrstuvwxyz")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
internal/middleware/auth.go
go
package middleware
import (
"fast-api/config"
"fast-api/pkg/resp"
"github.com/valyala/fasthttp"
)
func Auth(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
token := string(ctx.Request.Header.Peek("Authorization"))
if token != config.Conf.AuthToken {
resp.AuthFail(ctx)
return
}
next(ctx)
}
}
internal/repo/user_repo.go
go
package repo
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
func GetUserByID(id int) *User {
// 模拟数据库查询
return &User{
ID: id,
Name: "test_user",
Age: 22,
}
}
internal/service/user_service.go
go
package service
import (
"fast-api/internal/repo"
)
func GetUserInfo(uid int) (*repo.User, error) {
user := repo.GetUserByID(uid)
return user, nil
}
internal/service/demo_service.go
go
package service
func Hello(name string) string {
return "hello " + name
}
internal/handler/demo_handler.go
go
package handler
import (
"fast-api/internal/service"
"fast-api/pkg/resp"
"github.com/valyala/fasthttp"
)
func Hello(ctx *fasthttp.RequestCtx) {
// FormValue 兼容GET/表单参数
name := string(ctx.FormValue("name"))
msg := service.Hello(name)
resp.Success(ctx, map[string]any{"msg": msg})
}
type ReqDemo struct {
Text string `json:"text" validate:"required,min=2"`
}
func PostDemo(ctx *fasthttp.RequestCtx) {
var req ReqDemo
body := ctx.PostBody()
if err := json.Unmarshal(body, &req); err != nil {
resp.Fail(ctx, "json parse error")
return
}
if err := validator.Struct(&req); err != nil {
resp.Fail(ctx, err.Error())
return
}
resp.Success(ctx, req)
}
internal/handler/user_handler.go
go
package handler
import (
"fast-api/internal/service"
"fast-api/pkg/resp"
"github.com/valyala/fasthttp"
"strconv"
)
func GetUser(ctx *fasthttp.RequestCtx) {
// 路径参数
idStr := ctx.UserValue("id").(string)
uid, err := strconv.Atoi(idStr)
if err != nil {
resp.Fail(ctx, "invalid user id")
return
}
user, err := service.GetUserInfo(uid)
if err != nil {
resp.Fail(ctx, "user not found")
return
}
resp.Success(ctx, user)
}
router/group.go
go
package router
import (
"fast-api/internal/handler"
"fast-api/internal/middleware"
"github.com/fasthttp/router"
)
func registerPublic(r *router.Router) {
// 全局中间件:日志、跨域、panic恢复
r.GET("/", middleware.Cors(middleware.Logger(middleware.Recovery(handler.Hello))))
}
func registerAPI(r *router.Router) {
api := r.Group("/api/v1")
// 分组增加鉴权中间件
api.GET("/user/{id}", middleware.Auth(middleware.Cors(middleware.Logger(middleware.Recovery(handler.GetUser)))))
api.POST("/demo", middleware.Auth(middleware.Cors(middleware.Logger(middleware.Recovery(handler.PostDemo)))))
}
router/router.go
go
package router
import "github.com/fasthttp/router"
func InitRouter() *router.Router {
r := router.New()
registerPublic(r)
registerAPI(r)
return r
}
cmd/server/main.go
go
package main
import (
"context"
"fast-api/config"
"fast-api/pkg/validator"
"fast-api/router"
"github.com/valyala/fasthttp"
"log"
"os"
"os/signal"
"syscall"
)
func main() {
// 初始化配置
config.Init()
// 参数校验器初始化
validator.Init()
// 初始化路由
r := router.InitRouter()
// 构建fasthttp服务
srv := &fasthttp.Server{
Handler: r.Handler,
ReadTimeout: config.Conf.App.ReadTimeout,
WriteTimeout: config.Conf.App.WriteTimeout,
IdleTimeout: config.Conf.App.IdleTimeout,
MaxConns: config.Conf.App.MaxConns,
TCPKeepalive: true,
}
// 信号监听
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 异步启动服务
go func() {
log.Printf("server listen on :%s", config.Conf.App.Port)
if err := srv.ListenAndServe(":" + config.Conf.App.Port); err != nil && err != fasthttp.ErrServerClosed {
log.Fatalf("listen err: %v", err)
}
}()
// 阻塞等待关闭信号
sig := <-sigChan
log.Printf("receive signal: %s, start graceful shutdown", sig.String())
signal.Stop(sigChan)
// 优雅关闭
ctx, cancel := context.WithTimeout(context.Background(), config.Conf.ShutdownTimeout)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("shutdown err: %v", err)
}
log.Println("server exit success")
}
其他
针对json.NewEncoder(ctx).Encode()
fasthttp 场景下不推荐这么写
- 问题 1:不会自动设置 Content-Type
json.Encoder 只写内容,不会给响应头加 Content-Type: application/json,浏览器 / 客户端会识别成普通文本。
需要手动补充:
运行
go
ctx.SetContentType("application/json; charset=utf-8")
- 问题 2:性能差,违背 fasthttp 设计初衷
fasthttp 主打对象池、减少内存分配、低 GC:json.NewEncoder(ctx) 每次请求都会新建一个 Encoder 对象,堆内存分配,增加 GC 压力;
标准 Encoder 内部缓冲区没有复用,高 QPS 下开销明显; - 问题 3:自带多余换行符
.Encode() 输出末尾强制加 \n,部分前端解析无影响,但接口返回格式不纯净。
fasthttp 标准高性能写法(推荐替换上面代码)
- 方案 1:预序列化 + Write(性能最优)
go
import "encoding/json"
// 1. 先序列化
resp := Response{Code: code, Msg: msg, Data: data, TraceID: traceID}
buf, err := json.Marshal(resp)
if err != nil {
ctx.Error("json marshal fail", fasthttp.StatusInternalServerError)
return
}
// 2. 设置json头
ctx.SetContentType("application/json; charset=utf-8")
// 3. 写入响应
ctx.Write(buf)
- 复用 bytebuffer 极致优化(高并发网关)
借助 fasthttp 自带缓冲池减少分配:
go
buf := fasthttp.AcquireByteBuffer()
defer fasthttp.ReleaseByteBuffer(buf)
resp := Response{Code: code, Msg: msg, Data: data, TraceID: traceID}
err := json.NewEncoder(buf).Encode(&resp)
if err != nil {
ctx.Error("marshal err", 500)
return
}
ctx.SetContentType("application/json")
ctx.Write(buf.B)
