跳到主要内容
版本: v2.x

超时

Fiber 中存在两种不同的超时中间件实现。

New

用超时封装一个 fiber.Handler。如果处理程序返回的时间超过给定持续时间,则设置超时错误并将其转发到集中式 ErrorHandler

注意

由于存在竞态条件,此功能已被弃用。

NewWithContext

作为一个 fiber.Handler 封装器,它使用 context.WithTimeout 创建一个上下文并将其传递到 UserContext 中。

如果传递的上下文执行(例如 数据库操作、Http 调用)返回的时间超过给定持续时间,则设置超时错误并将其转发到集中式 ErrorHandler

它不会取消长时间运行的执行。底层执行必须通过使用 context.Context 参数来处理超时。

签名

func New(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler
func NewWithContext(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler

示例

导入 Fiber Web 框架的中间件包

import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/timeout"
)

初始化 Fiber 应用后,可以使用以下方式

func main() {
app := fiber.New()

h := func(c *fiber.Ctx) error {
sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms")
if err := sleepWithContext(c.UserContext(), sleepTime); err != nil {
return fmt.Errorf("%w: execution error", err)
}
return nil
}

app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second))
log.Fatal(app.Listen(":3000"))
}

func sleepWithContext(ctx context.Context, d time.Duration) error {
timer := time.NewTimer(d)

select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
return context.DeadlineExceeded
case <-timer.C:
}
return nil
}

使用 curl 测试 http 200

curl --location -I --request GET 'http://localhost:3000/foo/1000' 

使用 curl 测试 http 408

curl --location -I --request GET 'http://localhost:3000/foo/3000' 

与自定义错误一起使用

var ErrFooTimeOut = errors.New("foo context canceled")

func main() {
app := fiber.New()
h := func(c *fiber.Ctx) error {
sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms")
if err := sleepWithContextWithCustomError(c.UserContext(), sleepTime); err != nil {
return fmt.Errorf("%w: execution error", err)
}
return nil
}

app.Get("/foo/:sleepTime", timeout.NewWithContext(h, 2*time.Second, ErrFooTimeOut))
log.Fatal(app.Listen(":3000"))
}

func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error {
timer := time.NewTimer(d)
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
return ErrFooTimeOut
case <-timer.C:
}
return nil
}

数据库调用的示例用法

func main() {
app := fiber.New()
db, _ := gorm.Open(postgres.Open("postgres://localhost/foodb"), &gorm.Config{})

handler := func(ctx *fiber.Ctx) error {
tran := db.WithContext(ctx.UserContext()).Begin()

if tran = tran.Exec("SELECT pg_sleep(50)"); tran.Error != nil {
return tran.Error
}

if tran = tran.Commit(); tran.Error != nil {
return tran.Error
}

return nil
}

app.Get("/foo", timeout.NewWithContext(handler, 10*time.Second))
log.Fatal(app.Listen(":3000"))
}